這篇文章想要介紹一下我以前作的一個真實項目,感受比較有意思,記錄一下。java
這個項目比較有特色的是項目的主要業務邏輯由C語言開發同事完成,在他的SO庫中去作初始化、網絡操做、數據庫存儲、硬件設備操做等等的邏輯,我這邊Android主要就是調用他的接口結合界面完成業務。git
可是因爲咱們在第三方硬件設備操做,而第三方只提供了Android的SDK,在C層面沒法調用,因此硬件操做的接口也須要由我進行封裝,提供操做的接口(回調函數,不瞭解的請看這裏)給C開發同事調用。這樣就造成了下面的結構:github
我在APP初始化的時候,就將全部有關界面操做和硬件操做的函數以參數的形式傳給SO。SO庫中實際業務中,若是須要修改界面,好比要顯示黑名單下載進度,就調用傳入downloadBlackListCallBack函數,這個函數中會以JNI反射的方式調用到Java層代碼,再由Java層代碼實現進度的更新。硬件操做接口一樣的道理。數據庫
在一步中除了正常的參數傳入,重要的就是傳入回調函數了。markdown
和同事作好協商,以顯示進度爲例,在SO庫對外提供的函數中有這樣一個函數,用來設置進度回調。網絡
void APi_SetProgressCallback(Processfun Callback);
複製代碼
在他的頭文件中Processfun的定義以下,要求咱們傳入兩個int類型的值,第一個參數爲已下載數量,第二個參數爲總數量。jvm
typedef void (*Processfun)(int,int);
複製代碼
這個是SO庫中要求的函數形式,接下來咱們就要寫本身的實現函數函數
咱們首先實現一個Java層真正去控制進度顯示的函數。oop
在JNIController類中實現函數:spa
/** * 黑名單下載進度展現 * * @param downloaded 已下載黑名單條數 * @param all 總黑名單條數 */
private void downloadBlackListCallBack(int downloaded, int all) {
Intent intent = new Intent();
intent.setAction(DOWNLOAD_BLACKLIST_ACTION);
intent.putExtra(BLACKLIST_DOWNLOADED, downloaded);
intent.putExtra(BLACKLIST_ALL, all);
Application.getInstance().sendBroadcast(intent);
}
複製代碼
入參和上面定義的同樣,已下載數量和總數量,當函數調用後經過廣播的方式將傳入的數量發送出去,廣播接收器去修改界面。
而後要從JNI層以反射的方式調用Java層的downloadBlackListCallBack函數。
void downloadBlackListCallBack(int downloaded, int all);
void downloadBlackListCallBack(int downloaded, int all) {
JNIEnv *env = NULL;
int status = (*local_jvm)->AttachCurrentThread(local_jvm, &env, NULL);
if (status < 0) {
LOGD("env is null");
return;
}
jclass jclass1 = (*env)->FindClass(env, "com/.../controller/JNIController");
if (jclass1 == 0) {
LOGD("jclass1 = 0");
return;
}
jmethodID downloadProcess = (*env)->GetMethodID(env, jclass1, "downloadBlackListCallBack",
"(II)V");
if (downloadProcess == 0) {
LOGD("methodID == 0");
return;
}
//調用Java層,downloaded, all參數傳入
(*env)->CallVoidMethod(env, local_object, downloadProcess, downloaded, all);
(*env)->DeleteLocalRef(env, jclass1);
}
複製代碼
在JNI中入參也是一致的形式,再經過反射的方式調用Java層代碼,將downloaded, all參數傳給Java函數。
JNI層的downloadBlackListCallBack函數已經寫好了,最後就在初始化的函數中,調用SO庫提供的APi_SetProgressCallback(Processfun Callback)
將函數以參數的形式傳入
APi_SetProgressCallback(downloadBlackListCallBack);
複製代碼
Processfun processFun = null;
void APi_SetProgressCallback(Processfun Callback){
if(Callback!=NULL){
processFun = Callback;
}
}
//業務代碼中
processFun(10,100);
複製代碼
這樣就是一個完整的流程了。
設備的硬件操做接口也是一樣的邏輯完成。
總體而言,主要仍是用了C語言回調函數的方式,將上層的操做封裝成接口,交給底層邏輯本身控制,這樣比較靈活,雙方的代碼量也減小,就是須要在前期雙方梳理好須要的接口,完工!