Android JNI开发-实际项目开发总结(回调函数)

这篇文章想要介绍一下我以前作的一个真实项目,感受比较有意思,记录一下。java

项目介绍

这个项目比较有特色的是项目的主要业务逻辑由C语言开发同事完成,在他的SO库中去作初始化、网络操做、数据库存储、硬件设备操做等等的逻辑,我这边Android主要就是调用他的接口结合界面完成业务。git

可是因为咱们在第三方硬件设备操做,而第三方只提供了Android的SDK,在C层面没法调用,因此硬件操做的接口也须要由我进行封装,提供操做的接口(回调函数,不了解的请看这里)给C开发同事调用。这样就造成了下面的结构:github

我在APP初始化的时候,就将全部有关界面操做和硬件操做的函数以参数的形式传给SO。SO库中实际业务中,若是须要修改界面,好比要显示黑名单下载进度,就调用传入downloadBlackListCallBack函数,这个函数中会以JNI反射的方式调用到Java层代码,再由Java层代码实现进度的更新。硬件操做接口一样的道理。数据库

代码实现

SO库函数定义

在一步中除了正常的参数传入,重要的就是传入回调函数了。markdown

和同事作好协商,以显示进度为例,在SO库对外提供的函数中有这样一个函数,用来设置进度回调。网络

void APi_SetProgressCallback(Processfun Callback);
复制代码

在他的头文件中Processfun的定义以下,要求咱们传入两个int类型的值,第一个参数为已下载数量,第二个参数为总数量。jvm

typedef void (*Processfun)(int,int);
复制代码

这个是SO库中要求的函数形式,接下来咱们就要写本身的实现函数函数

Java层实现

咱们首先实现一个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层实现

而后要从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语言回调函数的方式,将上层的操做封装成接口,交给底层逻辑本身控制,这样比较灵活,双方的代码量也减小,就是须要在前期双方梳理好须要的接口,完工!

相关文章
相关标签/搜索