前言
期待了幾個(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)
一指蚁、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目錄)的目錄牲剃。
在cpp目錄右鍵New-->C/C++ Source File 遣疯,新建兩個(gè)文件jni_lib.cpp/h
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.一些簡單的配置
二谈山、體驗(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)試的
- 在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代碼