JNI入门课程-第七章:JNI线程通知安卓刷新

 序章:

1.本篇是JNI入门教程的第六篇,完整教程连接如下:

 JNI入门课程-序章https://blog.csdn.net/rzleilei/article/details/122084609

2.本篇主要考察的是JNI层线程的创建,以及各种类型的传递。

实现需求内容如下:

java层传入两个参数,字符串和activity对象,在JNI中创建线程,间隔5ms多次通知安卓进行字符串的刷新操作。

一。流程步骤

1.实现java中的声明方法和被调用方法

2.实现JNI中的代码

3.编写测试代码进行验证

二。在java中声明引用


1.这个和上一篇一样,只是增加了一个实现native方法。

public class DynamicRegister {

    static {
        System.loadLibrary("DynamicRegister");
    }

    /**
     * 拼接字符串str1和str2
     * @param str1
     * @param str2
     * @return
     */
    public native String spliceString(String str1,String str2);

    /**
     * 读取指定路径的文件内容
     * @param path
     * @return
     */
    public native String readStrByPath(String path);

       
    /**
     * JNI线程通知安卓刷新
     * @param path
     * @param activity
     */
    public native void refresh(String path, Activity activity);
}

2.在JNIActivity声明一个方法进行界面刷新,供JNI层线程调用。

这里并没有进行线程,也就是说UI刷新方法是在线程中执行的,为什么不会报错?这个可以参看我的另一篇文章,这里就不过多讲解了。

安卓子线程就真的不能更新UI吗?_分享+记录-CSDN博客

  /**
     * 供native方法调用通知安卓刷新
     * 这里是子线程刷新UI哦,并且可以正常运行不会报错,原因可以参考TextView源码中checkForRelayout方法
     */
    fun showMessage(str: String) {
        LogUtil.logI("showMessage:$str")
        mResult.text = str
    }


三。创建JNI的方法,并动态注册


1.创建JNI中的方法。传入参数有两个,字符串和activity对象。

void nativeRefresh(JNIEnv *env, jclass clazz, jstring title, jobject activity) {
   
}

2.注册JNI方法。和上一章一样,直接贴代码不详细说了。

static JNINativeMethod method_table[] = {
        // 第一个参数a 是java native方法名,
        // 第二个参数 是native方法参数,括号里面是传入参的类型,外边的是返回值类型,
        // 第三个参数 是c/c++方法参数,括号里面是返回值类型,
        {"spliceString",        "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",      (jstring *) nativeSpliceString},
        {"encryptionStr",       "(Ljava/lang/String;)Ljava/lang/String;",      (jstring *) nativeEncryptionStr},
        {"staticencryptionStr", "(Ljava/lang/String;)Ljava/lang/String;",      (jstring *) nativeStaticEncryptionStr},
        {"readStrByPath",       "(Ljava/lang/String;)Ljava/lang/String;",      (jstring *) nativeReadStrByPath},
        {"refresh",             "(Ljava/lang/String;Landroid/app/Activity;)V", (jstring *) nativeRefresh},
};

3.接下来我们就要取实现nativeRefresh方法了。

因为刷新操作我们要在线程中执行,所以我们要把参数传入到线程中。

所以我们先构建Param结构体:

struct Param {
    const char *charTitle;
    jobject activity;
    jstring title;
    int i;
};

4.生成线程,然后把上一步创建的结构体带入到线程中。

首先对param赋值,然后创建线程,把param带入

void nativeRefresh(JNIEnv *env, jclass clazz, jstring title, jobject activity) {
    param.title = static_cast<jstring>(env->NewGlobalRef(title));
    param.charTitle = env->GetStringUTFChars(title, JNI_FALSE);
    param.activity = env->NewGlobalRef(activity);
    param.i = 1;
    pthread_t pt;
    __android_log_print(ANDROID_LOG_INFO, "lxltestjni", "nativeRefresh start,tmp.c:%d", param.i);
    pthread_create(&pt, NULL, freshAndroidPage, &param);//这里的param无用
}

freshAndroidPage是方法,创建线程时传入方法名,则线程启动后就会执行freshAndroidPage方法。

5.实现freshAndroidPage方法

freshAndroidPage方法中的代码,就是在线程中执行了。

我们定时通知安卓进行刷新,sleep5毫秒。

首先获取activity对象,然后通过对象获取activity这个类对象,通过类对象获取到方法ID。PS:这一套和反射很像。

然后把传入的字符串charTitle和次数i进行字符串拼接,生成jstring对象,然后调用CallVoidMethod的方法,调用java层的showMessage方法,进行界面刷新。

最后达到10次之后,还要释放一下线程资源。

void *freshAndroidPage(void *arg) {
    JNIEnv *env;
    g_vm->AttachCurrentThread(&env, nullptr);
    Param tmp = param;
    jobject activity = tmp.activity;
    const char *title = env->GetStringUTFChars(tmp.title, JNI_FALSE);
    int i = 0;
    __android_log_print(ANDROID_LOG_INFO, "lxltestjni",
                        "freshAndroidPage start,tmp.charTitle:%s,title:%s", tmp.charTitle, title);
    while (i++ < 10) {
        __android_log_print(ANDROID_LOG_INFO, "lxltestjni", "i:%d,charTitle:%s", i, tmp.charTitle);
        jclass cls = env->GetObjectClass(activity);
        jmethodID id = env->GetMethodID(cls, "showMessage", "(Ljava/lang/String;)V");

        strchr(tmp.charTitle, i);
        std::string result = tmp.charTitle;
        result.append(std::to_string(i));
        jstring message = env->NewStringUTF(result.c_str());
        env->CallVoidMethod(activity, id, message);
        sleep(5);
    }
    g_vm->DetachCurrentThread();
}

四。CMakeLists中做好声明


1.创建文件Cmake文件,同上一章,略。


五。验证效果

1.JAVA层的调用方法比较简单,传入文本和activity对象即可

if (position == 5) {
            //C层启动一个线程,主动通知java层的刷新界面。
            dynamicRegister.refresh("ttA", this)
            return
        }

2.点击之后,我们就可以看到下面的文本框不断的进行刷新,最后的数字不断的进行累加。

 附:项目地址和源码

android_all_demo/DemoClient at master · aa5279aa/android_all_demo · GitHub

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

失落夏天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值