前言
作爲工作了近四年時間的Android開發,搞通這個Demo用了一個工作日,覺得自己動手能力有些差,所以記錄下。這裏我們不介紹JNI的基礎理論知識。只講Demo流程。
Demo
第一步
創建Demo工程,從SDK Manager中下載NDK相關組件,同時將Support Repository也下載下來
第二步
創建Java文件,如下:
package com.example.jnidemo;
public class JNITest {
public native String getStrFromJNI();
}
第三步
使用javah命令生成JNITest的頭文件。我就是這步卡主的,在使用javah命令過程中遇到了很多問題。例如 javah不是命令、無法識別的文件名、找不到類文件。這裏我說下我這裏如何跑通的。注意以下幾點。
-
1、在Android Studio的終端中使用Javah如果報不是內部或外部命令,也不是可運行的程序。則在cmd命令行中執行,只要環境變量配置無誤,一定會識別javah命令
-
2、在生成的classes文件夾目錄下使用命令,在源文件目錄下執行會報找不到xxx的類文件
-
3、之前class文件目錄是在build/intermediates/classes/debug下。我一度懷疑沒有生成class文件。後來發現目錄是:build/intermediates/javac/debug/complieDebugJavaWithJavac/classes下。那麼我們就在如下目錄下執行如下命令:
javah -jni com.example.jnidemo.JNITest
如下:
生成的.h文件就在同目錄下,如下:
第四步
在src/main/下建立jni文件下,jni文件夾與java文件是平行的。建立方法如下:
第五步
將剛剛生成的.h文件複製到jni目錄下,並編寫其實現類,文件名自取,我們這裏就叫demo.c。如下:
//
// Created by xxx on 2019/6/17.
//
#include<jni.h>
//導入頭文件
#include "com_example_jnidemo_JNITest.h"
//實現.h文件中定義的方法
jstring Java_com_example_jnidemo_JNITest_getStrFromJNI(JNIEnv *env,jobject thiz){
return (*env)->NewStringUTF(env,"I am Str from jni libs!");
}
第六步
在jni文件夾下建立Android.mk文件用於編譯,如下:
#my-dir:返回當前 Android.mk 所在的目錄的路徑
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# 定義生成so文件的名字
LOCAL_MODULE := hello-jni
# 需要編譯的文件,由於在一個文件夾下,不用寫路徑
LOCAL_SRC_FILES := demo.c
include $(BUILD_SHARED_LIBRARY)
# 指定生成全部的ABI的so文件
APP_ABI := all
第七步
在app.gradle中android{}內添加ndk相關配置,如下:
externalNativeBuild {
ndkBuild {
path 'src/main/jni/Android.mk'
}
}
第八步
make project即可看到生成的so文件,如下:
可以看到每個ABI均生成了so文件,但是so文件的名字多了個前綴lib,這是因爲Android強制要求所有的so文件必須是lib前綴,我們可以看到例如高德地圖、融雲聊天等第三方的so庫也是如此
第九步
加載so文件並使用,如下:
package com.example.jnidemo;
public class JNITest {
static {
System.loadLibrary("jni-demo");//與Android.mk文件中設置的一致,不需要手動添加前綴lib
}
public native String getStrFromJNI();
}
package com.example.jnidemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new JNITest();
((TextView)findViewById(R.id.tv_info)).setText(new JNITest().getStrFromJNI());
}
}
文件目錄
寫在後邊
這裏只介紹了最基礎最簡單的jni使用方法,還有如CMake等方式沒有調研。後邊有時間會陸續更新。如照着這個Demo跑不通可留言共同探討。網上還有另一種方式,不寫Android.mk文件,而是在app.gradle的defaultConfig{}下添加ndk設置,如下:
ndk {
moduleName "NdkJniDemo" //生成的so名字
abiFilters "armeabi-v7a", "x86","arm64-v8a","x86_64" //輸出指定三種abi體系結構下的so庫,目前可有可無。
}
我並沒有跑通。沒有繼續糾結。