Android Studio 2.2 之 NDK開發(fā)

前言

期待了幾個(gè)月的Android Studio 2.2 版本穩(wěn)定版昨天終于發(fā)布荞膘,迫不及待的更新嘗試缩焦。這次更新內(nèi)容頗多晰甚。我主要關(guān)注NDK開發(fā)卿啡,所以期待這一版本帶來的c/c++支持的增強(qiáng)昔瞧。本文介紹一下這兩天折騰新版AS開發(fā)和調(diào)試NDK的一些經(jīng)驗(yàn)

Paste_Image.png

一指蚁、NDK的支持

Android Studio 2.2 的NDK開發(fā)支持 Cmake和ndk-build兩種方式。相比與以前的gradle去配置ndk編譯目錄什么的簡直是方便多了自晰。對于老的通過Android.mk文件編譯的NDK項(xiàng)目凝化,直接一條配置整個(gè)項(xiàng)目就可以被AS支持了。

1.Cmake方式使用AS開發(fā)調(diào)試NDK

新版的Android Studio支持使用Cmake構(gòu)建c/c++工程酬荞。相比與上一版要通過gradle來配置c/c++工程簡單了多搓劫,而且也便于老項(xiàng)目的遷移了,下面先來一個(gè)簡單的例子

1).新建一個(gè)空的工程

默認(rèn)建一個(gè)空的工程袜蚕,只包含一個(gè)MainActivity

2).創(chuàng)建jni目錄和cpp文件

在左側(cè)Project欄選擇app糟把,右鍵-->New-->Folder-->JNI Folder ,app目錄下會(huì)多出一個(gè)cpp(貌似上一版還是jni目錄)的目錄牲剃。

Paste_Image.png

在cpp目錄右鍵New-->C/C++ Source File 遣疯,新建兩個(gè)文件jni_lib.cpp/h

Paste_Image.png

3).配置jni工程

進(jìn)入文件管理器,在jni目錄下創(chuàng)建CmakeLists.txt文件

# Sets the minimum version of CMake required to build the native
# library. You should either keep the default value or only pass a
# value of 3.4.0 or lower.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds it for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
             native-lib
             # 這個(gè)是jni編譯生產(chǎn)的so庫的名字
             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             # Associated headers in the same location as their source
             # file are automatically included.
             # 要編譯的c/c++文件列表 文件路徑想對于cmake文件路徑
             ./jni_lib.cpp )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because system libraries are included in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
              log-lib
              # 依賴的系統(tǒng)so庫
              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in the
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

Project欄app右鍵凿傅,Link C++ Project With Gradle 選擇CMakeLists.txt文件缠犀。或者也可以直接在gradle中加入如下配置即可

externalNativeBuild{    
      cmake{
        path file("src/main/jni/CMakeLists.txt") 
   }
}

選擇Gralde同步之后就完成了jni工程的創(chuàng)建

4).寫第一個(gè)jni函數(shù)

我們可以完成我們的第一個(gè)jni函數(shù)
jni_lib.h

#ifndef DEMOCMAKE_JNI_LIB_H
#define DEMOCMAKE_JNI_LIB_H
#include "jni.h"
jstring GetStrFromJNI(JNIEnv* env,jobject callObj);
#endif //DEMOCMAKE_JNI_LIB_H

jni_lib.cpp

#include "jni_lib.h"
jstring GetStrFromJNI(JNIEnv *env, jobject callObj)
 {    
      return env->NewStringUTF("String From Jni With c++");
}

光有這些是不夠的聪舒,java虛擬機(jī)是無法直接找到GetStrFromJNI這個(gè)函數(shù)的辨液,需要通過調(diào)用JNI_OnLoad函數(shù),實(shí)現(xiàn)JNI函數(shù)和java native聲明的對接箱残。關(guān)于jni的知識可以多Google一下學(xué)習(xí)滔迈。

so庫的加載和native函數(shù)的聲明
MainActivity.java

public class MainActivity extends AppCompatActivity {

    static {
        /*
        加載動(dòng)態(tài)庫止吁,動(dòng)態(tài)庫加載的時(shí)候 JNI_OnLoad函數(shù)會(huì)被調(diào)用
        
        在JNI——OnLoad函數(shù)中,Java虛擬機(jī)通過函數(shù)表的形式將JNI函數(shù)和java類中native函數(shù)對應(yīng)起來
         */
        System.loadLibrary("native-lib");
    }
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView= (TextView) findViewById(R.id.text);
        textView.setText(GetStrFromJNI());
    }
    
    /*
    Jni 函數(shù)的聲明
    當(dāng)調(diào)用到此函數(shù)時(shí)燎悍,java虛擬機(jī)會(huì)通過JNI_OnLoad里注冊的函數(shù)表找到對應(yīng)的函數(shù)去執(zhí)行
     */
    private native String GetStrFromJNI();
}

JNI_OnLoad的實(shí)現(xiàn)
jni_lib.cpp

//
// Created by kang on 9/23/16.
//

#include "jni_lib.h"

#define JNI_AN_MainActivity     "com/kang/demondk/MainActivity"

#define METHOD_NUM 1
JNINativeMethod g_nativeMethod[METHOD_NUM]={
        {"GetStrFromJNI","()Ljava/lang/String;",(void*)GetStrFromJNI}
};

/*
 * 被虛擬機(jī)自動(dòng)調(diào)用
 */
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv *env;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK)
        return JNI_ERR;

    jclass jClass = env->FindClass(JNI_AN_MainActivity);
    env->RegisterNatives(jClass,g_nativeMethod,METHOD_NUM);
    env->DeleteLocalRef(jClass);
    return JNI_VERSION_1_6;
}

void JNI_OnUnload(JavaVM* vm, void* reserved) {
    JNIEnv *env;
    int nJNIVersionOK = vm->GetEnv((void **)&env, JNI_VERSION_1_6) ;
    jclass jClass = env->FindClass(JNI_AN_MainActivity);
    env->UnregisterNatives(jClass);
    env->DeleteLocalRef(jClass);
}



jstring GetStrFromJNI(JNIEnv *env, jobject callObj) {
    return env->NewStringUTF("String From Jni With c++");
}


2.ndk-build方式使用AS開發(fā)調(diào)試NDK

ndk-build方式與cmake方式類似敬惦,只需要將cmake文件改寫為Android.mk和Appliction.mk文件。在CMakeLists.txt加載的位置將CMakeLists.txt替換為Android.mk即可

Android.mk


LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
APP_ABI := all

LOCAL_MODULE    := native-lib
LOCAL_CPPFLAGS  := -O0 -D_UNICODE -DUNICODE -DUSE_DUMP -Wno-error=format-security
LOCAL_CPP_EXTENSION := .cpp
LOCAL_LDLIBS    := -lm -llog -lz
LOCAL_SHORT_COMMANDS := true
INC_DIRS = -I$(LOCAL_PATH)/jni
LOCAL_CPPFLAGS += $(INC_DIRS)

LOCAL_SRC_FILES := \
    jni_lib.cpp \   \


LOCAL_SHARED_LIBRARIES += libandroid_runtime
    

include $(BUILD_SHARED_LIBRARY)

Appliction.mk

APP_ABI := all
NDK_TOOLCHAIN_VERSION := clang
APP_SHORT_COMMANDS      := true
APP_STL := stlport_static
APP_CPPFLAGS := -std=gnu++11 -D__STDC_LIMIT_MACROS

在gradle的配置中cmake的配置替換為

//    externalNativeBuild{
//        cmake{
//            path file("src/main/jni/CMakeLists.txt")
//        }
//    }
    externalNativeBuild{
        ndkBuild{
            path file("src/main/jni/Android.mk")
        }
    }

4.一些簡單的配置

產(chǎn)見

二谈山、體驗(yàn)改進(jìn)和存在的問題

1.編譯

配置好CmakeLists.txt或者Android.mk之后俄删,編譯的時(shí)候,Android Stuido 會(huì)自動(dòng)進(jìn)行NDK的編譯奏路。我測試了mac和ubuntu都沒有問題畴椰,在windows下似乎有些問題。發(fā)現(xiàn)鸽粉,當(dāng)Android.mk項(xiàng)目中有依賴的靜態(tài)庫的時(shí)候斜脂,編譯處理有些問題,無法通過編譯触机。windows黨自行測試秽褒。

2.調(diào)試

我升級新版Androd Studio的直接原因就是,終于可以方便的調(diào)試底層代碼了威兜。但是經(jīng)過兩天的使用還是發(fā)現(xiàn)有些問題的。

  • 調(diào)試帶有NDK項(xiàng)目的工程的時(shí)候庐椒,Android Studio會(huì)同時(shí)啟動(dòng)兩個(gè)調(diào)試器椒舵,一個(gè)針對NDK的lldb調(diào)試器,和Java調(diào)試器约谈。
  • 默認(rèn)NDK的調(diào)試是開的笔宿,因?yàn)榇蜷_NDK調(diào)試,啟動(dòng)調(diào)試的時(shí)候還是挺慢的棱诱,不需要的時(shí)候泼橘,可以將工程設(shè)置里的Debuger從Auto 改為Java
  • 另為需要注意的問題是,如果NDK代碼為了項(xiàng)目的Model中迈勋,必須在如下位置Symbol Directories炬灭,增加NDK所在項(xiàng)目的根路徑,否則LLDB調(diào)試器會(huì)報(bào)找不到符號文件錯(cuò)誤靡菇,是無法進(jìn)行調(diào)試的
Paste_Image.png
  • 在Ubuntu測試的時(shí)候還發(fā)現(xiàn)一個(gè)見鬼的問題重归,有時(shí)候配置好一個(gè)項(xiàng)目,程序死活不會(huì)停在c/c++的斷點(diǎn)出厦凤。剛開始感覺很莫名其妙鼻吮,配置兩個(gè)一模一樣的工程,一個(gè)可以調(diào)試NDK较鼓,一個(gè)不可以椎木。最后經(jīng)過反復(fù)測試违柏,才發(fā)現(xiàn)一個(gè)問題:當(dāng)NDK代碼位于Model中的時(shí)候,這個(gè)項(xiàng)目的APP的目錄深度一定不能大于NDK所在Model的路徑深度香椎!

后話

Android Studio 2.2的發(fā)布漱竖,盡管還有許多問題,在這兩天的使用中還是發(fā)現(xiàn)有很多不穩(wěn)定的地方士鸥。在編輯和調(diào)試大的項(xiàng)目時(shí)還是經(jīng)常容易出現(xiàn)異常闲孤。但是畢竟對于Androd Studio 進(jìn)行NDK開發(fā)來說還是很大的進(jìn)步。本人才疏學(xué)淺烤礁,近一年一直學(xué)習(xí)和使用NDK讼积,發(fā)現(xiàn)網(wǎng)上資料甚少,寫次博文全當(dāng)交流學(xué)習(xí)脚仔。歡迎有共同愛好和需求的朋友交流討論勤众,共同學(xué)習(xí)

本文Sample代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鲤脏,隨后出現(xiàn)的幾起案子们颜,更是在濱河造成了極大的恐慌,老刑警劉巖猎醇,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窥突,死亡現(xiàn)場離奇詭異,居然都是意外死亡硫嘶,警方通過查閱死者的電腦和手機(jī)阻问,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沦疾,“玉大人称近,你說我怎么就攤上這事∠” “怎么了刨秆?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長忆畅。 經(jīng)常有香客問我衡未,道長,這世上最難降的妖魔是什么家凯? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任眠屎,我火速辦了婚禮,結(jié)果婚禮上肆饶,老公的妹妹穿的比我還像新娘改衩。我一直安慰自己,他們只是感情好驯镊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布葫督。 她就那樣靜靜地躺著竭鞍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪橄镜。 梳的紋絲不亂的頭發(fā)上偎快,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機(jī)與錄音洽胶,去河邊找鬼晒夹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛姊氓,可吹牛的內(nèi)容都是我干的丐怯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼翔横,長吁一口氣:“原來是場噩夢啊……” “哼读跷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起禾唁,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤效览,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后荡短,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丐枉,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年掘托,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了矛洞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡烫映,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出噩峦,到底是詐尸還是另有隱情锭沟,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布识补,位于F島的核電站族淮,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏凭涂。R本人自食惡果不足惜祝辣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望切油。 院中可真熱鬧蝙斜,春花似錦、人聲如沸澎胡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至稚伍,卻和暖如春弯予,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背个曙。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工锈嫩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人垦搬。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓呼寸,卻偏偏與公主長得像,于是被迫代替她去往敵國和親悼沿。 傳聞我的和親對象是個(gè)殘疾皇子等舔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容