Android_JNI_2

Android JNI

上篇博客介绍了JNI的基本的使用,但是不是在多进程中进行的,此片讲述是在另外一个线程中进行回调Java代码,完成回调,demo也是一个异步的回显操作,因为也是刚开始学习,仅仅做个记录。

  • C++层回调Java层(多线程)

a. demo只是在C++开辟线程完成回调Java层代码,和上篇在单一线程有些不同,也需要理解一些关于Java的概念。
b. JVM,即JVM虚拟机在整个程序中只有一个,所以可以以一个全局的方式进行保存。
c. JNIEnv*则是每个线程都是不同的,可以通过VM进行获取,此处也是自己刚开始学习时犯的错误,以为可以在不同线程进行传递,后面通过看了一些资料了解到。
d. demo如下图,通过START按钮完成随机数生成并且更新UI,FINISH按钮完成线程销毁,其中更新UI是在C++线程入口进行每一秒进行一次回调Java层刷新UI,这里为了方便将jobject对象等变成全局,方便访问,实际中应进行封装。

在这里插入图片描述


MainActivity.java

package com.example.androidjni;

import android.os.Handler;
import android.os.Looper;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }
    public static TextView textView = null;
    public static Button StartButten = null;
    public static Button FinishButten = null;
    public static String Flag ="finish";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        textView = (TextView) findViewById(R.id.sample_text);

        final NativeUtils nativeUtils = new NativeUtils();

        StartButten = findViewById(R.id.button);
        FinishButten = findViewById(R.id.button2);


        StartButten.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(Flag == "finish"){
                    Flag ="start";
                    nativeUtils.JNICALLJavaWorkBack(Flag);
                }
                else{
                    // NOTHING TO_DO
                }
            }
        });

        FinishButten.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Flag = "finish";
                nativeUtils.JNICALLJavaWorkBack(Flag);

            }
        });

    }// end onCreate

}

NativeUtils.java


package com.example.androidjni;

import android.os.Looper;

public class NativeUtils {

    // C++层调用的方法
    public native void JNICALLJavaWorkBack(String flag);

    // Java层具体实现
    public native String GetRandomNumber();
    public void JNICALLJavaWork(){

            //TO_DO
            // 1. 首先获取随机值
            // 2. 因为更新UI的原因,所以每次开次一个线程完成UI线程的更新
            // 3. Handler机制,通过将Runable或者Meesage放入UI线程Looper中

            final String randomNum = GetRandomNumber();
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            MainActivity.textView.setText(randomNum);
                        }
                    });
    }
}


Bridge.hpp

#ifndef ANDROIDJNI_BRIDGE_H
#define ANDROIDJNI_BRIDGE_H



#include <jni.h>
#include <thread>

struct Arg;


struct Arg{
public:
    JavaVM* _vm ;
    jobject _obj;
    std::string _IsRunning ;

    Arg(JavaVM* vm,jobject obj,std::string IsRunning):_vm(vm),_obj(obj),_IsRunning(IsRunning){}

};

#endif //ANDROIDJNI_BRIDGE_H

CmakeLists.txt

#将其他源文件进行加入,否则找不到
add_library( # Sets the name of the library.
        native-lib

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        src/main/cpp/native-lib.cpp
        src/main/cpp/jni-utils.cpp)

jni-lib.cpp

#include <jni.h>
#include <random>
#include <time.h>
#include <string>
#include <unistd.h>
#include <iostream>
#include <pthread.h>
#include <assert.h>
#include "Bridge.hpp"

// 为了方便直接变成全局,实际除了JavaVM都应该进行封装
static JavaVM* g_vm = NULL;
static jobject g_obj = NULL;
static pthread_t  tid;
static std::string IsRunning = "satrt";

// Random
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_momo_androidjni_NativeUtils_GetRandomNumber(JNIEnv *env, jobject instance) {

    // TODO
    srand((unsigned)(time(NULL)));
    std::string returnValue = std::to_string(rand());
    return env->NewStringUTF(returnValue.data());
}


// ThreadHandler
void* ThreadHandler(void* arg){
    // 1. 通过全局的jvm,获取当前线程的JNIEnv对象
    JNIEnv* env;
    g_vm->AttachCurrentThread((JNIEnv**)(&env),NULL);
    // 2. 通过env获取jclass对象
    jclass cls = env->GetObjectClass(g_obj);
    jobject jobject1 = env->AllocObject(cls);
    jmethodID  mid = env->GetMethodID(cls,"JNICALLJavaWork","()V");
    while(1){
        if(IsRunning == "start"){
            env->CallVoidMethod(g_obj,mid);
            sleep(1);
        }else if(IsRunning == "finish"){
            pthread_exit(NULL);
        }
    }
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_androidjni_NativeUtils_JNICALLJavaWorkBack(
        JNIEnv *env, jobject instance, jstring flag) {

    // TODO(Test)
    // 1. 因为env对象是每个线程都会有一个,所以需要获得jvm
    env->GetJavaVM(&g_vm);
    g_obj = env->NewGlobalRef(instance);

    // 2. 构造调用methond的参数
    jboolean iscopy;
    std::string Cflag = env->GetStringUTFChars(flag, &iscopy);

    // 3. 具体处理
    if (Cflag == "start") {
        IsRunning = "start";
        pthread_create(&tid, NULL, ThreadHandler, NULL);
        pthread_detach(tid);
    } else if (Cflag == "finish") {
        IsRunning = "finish";
    } else {
        assert(false);
    }
}

这篇文章也是为了记录下自己在学习遇到的一些问题,如有错误可以给我私信,这里表示感谢。下面会学习GitHub上开源的一个跨平台工具,希望自己可以不断去学习嘻嘻。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值