Android JNI開發-Linux C程式移植
最近在公司做一個專案,想把之前Linux小組做了三四年的一個專案移植到Android平臺上。如果讓Android從頭開始做,估計又是三四年時間去對接不同平臺,而且還需要兩個組同時維護,這樣太不划算了。所以就打算把Linux上的程式移植到Android上,Android只需要提供一些特有的介面給C就好了,以後只維護一套程式碼,簡單方便。
經過一天的嘗試,最終成功完成了一個滿足需求的Demo,整體結構如下:

整體來看也比較簡單,Java調JNI,JNI啟動Linux程式,Linux程式執行中需要操作介面時再用JNI的反射方法呼叫到Java層修改介面。
接下來看下具體實現:
程式碼結構

LinuxHandler.java 定義native方法和等待反射呼叫方法 Androidhandle.cLinux程式碼呼叫方法反射Java層 demo.cLinux小組同事提供的測試程式碼,裡面有他們簡化的業務邏輯 linuxhandle.cJNI方法
LinuxHandler.java
Java層啟動
public class LinuxHandle { public native void startLinux(); public void speak(String msg) { Log.e("TEST", "speak C-> " + msg); } public void showMsg(String msg) { Log.e("TEST", "showMsg C-> " + msg); } }
定義了三個方法,native方法startLinux()從Java層啟動Linux程式,另外兩個是模擬等待反射呼叫的操作介面方法。
linuxhandle.c
真正去啟動Linux程式
#include <jni.h> #include "linuxhandle.h" #include "demo.h" //Linux C 標頭檔案,呼叫main()方法 JavaVM *local_jvm = NULL; jobject local_object = NULL; /* * Class:com_***_linuxdemo_LinuxHandle * Method:startLinux * Signature: ()V */ JNIEXPORT void JNICALL Java_com_***_linuxdemo_LinuxHandle_startLinux (JNIEnv *env, jobject object) { (*env)->GetJavaVM(env, &local_jvm); //儲存到全域性變數JVM中 local_object = (*env)->NewGlobalRef(env, object); /* 建立物件的本地變數 */ main(); //啟動Linux C程式 }
Androidhandle.c
等待Linux程式呼叫
#include <jni.h> #include "linuxhandle.h" void showMessage_c(char *message) { extern JavaVM *local_jvm; //獲取全域性JVM,由它獲取JNIEnv JNIEnv *env = NULL; extern jobject local_object; (*local_jvm)->AttachCurrentThread(local_jvm, &env, NULL); jclass jclass1 = (*env)->FindClass(env, "com/***/linuxdemo/LinuxHandle"); jmethodID methodID = (*env)->GetMethodID(env, jclass1, "showMsg", "(Ljava/lang/String;)V"); (*env)->CallVoidMethod(env, local_object, methodID, (*env)->NewStringUTF(env, message)); } void speak_c(char *text) { LOGE("speak at C"); //同樣反射到Java層 }
Demo.c
#include "Androidhandle.h" int main(void) { speak_c("1234"); ......//公司業務邏輯程式碼 showMessage_c("adc"); }
我自己做完後也做了多次驗證,並且匯入第三方SO庫測試也沒有問題,但因為還只是個驗證可行性的Demo,後面就要開始正式做移植的專案了,如果遇到了什麼坑我會盡快補上。