Android Studio的JNI开发快餐教程
从eclipse换到Android Studio之后,原来的NDK集成已经不能用了。同时,AndroidStudio也是在快速迭代进步中,不仅支持内置的ndk支持插件,还有支持外部编译系统的plugin. 而且这还是在实验中的结果,在不久的将来,还可能有更新的变化。不过,万变不离其宗,我们打好基础,形式上的东西跟着Android Studio变就好。
旧式的NDK支持
首先说明,既然是Android Studio认为它已经过时了,所以我们需要在gradle.properties中声明一下我们还坚持用这个属性:
android.useDeprecatedNdk=true
下面我们看下,在build.gradle中如何使用这种老式的办法:
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "24.0.0"
defaultConfig {
applicationId "com.yunos.xulun.testcppjni"
minSdkVersion 21
targetSdkVersion 24
versionCode 1
versionName "1.0"
ndk{
moduleName "testcppjni"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
这其中使用的plugin,是"com.android.application".
我们现在可以开始写一个类测试一下:
package com.yunos.xulun.testcppjni;
public class TestCppJni {
static{
System.loadLibrary("testcppjni");
}
public static native int callCpp();
}
编译之后,使用javah工具生成头文件com_yunos_xulun_testcppjni_TestCppJni.h:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_yunos_xulun_testcppjni_TestCppJni */
#ifndef _Included_com_yunos_xulun_testcppjni_TestCppJni
#define _Included_com_yunos_xulun_testcppjni_TestCppJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_yunos_xulun_testcppjni_TestCppJni
* Method: callCpp
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_yunos_xulun_testcppjni_TestCppJni_callCpp
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
然后我们写一个C++函数去实现它:
#include "com_yunos_xulun_testcppjni_TestCppJni.h"
JNIEXPORT jint JNICALL Java_com_yunos_xulun_testcppjni_TestCppJni_callCpp
(JNIEnv *env, jclass obj){
return (jint)0;
}
将头文件和cpp文件放到app/src/main/jni下面。
在Android工程中写个类去引用这个本地方法,就不多说了。编译运行,就生成了libtestcppjni.so。
还可以额外多设几个属性,比如编译选项,比如生成什么架构的库,引入什么库等等:
android {
compileSdkVersion 24
buildToolsVersion "24.0.0"
defaultConfig {
applicationId "com.yunos.xulun.testcppjni"
minSdkVersion 21
targetSdkVersion 24
versionCode 1
versionName "1.0"
ndk{
moduleName "testcppjni"
cFlags "-std=c++11"
abiFilters "arm64-v8a","armeabi", "armeabi-v7a", "x86"
ldLibs "log"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
新的实验性的编译系统的NDK支持
其实,新的实验性编译系统,对于java的编译也是变了不少。
下面,我们follow步骤去升级到新的编译系统:
升级gradle的版本
首先,要确保gradle的版本在2.10以上,我们通过修改gradle/wrapper下的gradle-wrapper.properties中的distributionUrl来实现这一点。
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
升级plug-in
我们修改build.gradle,从正式版改成实验版,在我写这篇文件时,最新版本是0.8.0-alpah5。我们保守点,选个不带alpha的0.7.2:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle-experimental:0.7.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
修改代码
经过上一步的修改,系统已经提示,无法找到我们之前用的"com.android.application"的包名了,所以我们要针对新的'com.android.model.applicaiton’包进行修改:
apply plugin: 'com.android.model.application'
model {
android {
compileSdkVersion 24
buildToolsVersion "24.0.0"
defaultConfig {
applicationId "com.yunos.xulun.testcppjni"
minSdkVersion.apiLevel 21
targetSdkVersion.apiLevel 24
versionCode 1
versionName "1.0"
buildConfigFields{
create() {
type "int"
name "VALUE"
value "1"
}
}
ndk {
moduleName "testcppjni"
cppFlags.add("-std=c++11")
ldLibs.add("log")
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles.add(file('proguard-rules.pro'))
}
}
productFlavors{
create("Flavor"){
applicationId "com.yunos"
}
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.0.0'
}
针对ndk部分,moduleName部分不用变。但是像cppFlags和ldLibs现在要通过add方法来进行添加了。
我们还可以加上调试和stl的支持:
apply plugin: 'com.android.model.application'
model {
android {
compileSdkVersion 24
buildToolsVersion "24.0.0"
defaultConfig {
applicationId "com.yunos.xulun.testcppjni"
minSdkVersion.apiLevel 21
targetSdkVersion.apiLevel 24
versionCode 1
versionName "1.0"
buildConfigFields{
create() {
type "int"
name "VALUE"
value "1"
}
}
ndk {
moduleName "testcppjni"
cppFlags.add("-std=c++11")
ldLibs.add("log")
stl "stlport_shared"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles.add(file('proguard-rules.pro'))
ndk{
debuggable true
}
}
}
productFlavors{
create("Flavor"){
applicationId "com.yunos"
}
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.0.0'
}
直接调用NDK或者CMake
上面这两种方式还不算完,生命不息,折腾不止,让我们一起跟随Android Studio继续折腾。
从2.2版本开始,Android Studio开始在64位OS上支持
这次我们直接调用NDK去build。
之前我们一直没有写Android.mk,现在要用NDK了,就写一个吧:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := testcppjni
LOCAL_SRC_FILES := test.cpp
LOCAL_CFLAGS += -std=c++11
LOCAL_ARM_MODE := thumb
include $(BUILD_SHARED_LIBRARY)
Application.mk也写个吧:
APP_ABI := armeabi armeabi-v7a arm64-v8a
然后改build.gradle:
android {
compileSdkVersion 20
buildToolsVersion "24.0.0"
externalNativeBuild{
ndkBuild{
path "Android.mk"
}
}
defaultConfig {
applicationId "com.yunos.xulun.testcppjni"
minSdkVersion 19
targetSdkVersion 19
externalNativeBuild {
ndkBuild {
targets "testcppjni"
arguments "NDK_APPLICATION_MK:=Application.mk"
cppFlags "-std=c++11"
abiFilters "armeabi-v7a", "armeabi","arm64-v8a"
}
}
}
综述
从目前的情况看,可以继续用过时的老方法,因为后两种都还没有正式发布。
有兴趣的可以跟进新的编译系统,升级到2.2之后,也可以考虑使用externalNativeBuild。