Android NDK JNI 梳理

Java

Animal

package cn.com.codingce.mediaandroid.entity;


import android.util.Log;

/**
* JNI调Java使用
*/
public class Animal {

protected String name;
public static int num = 0;

protected int age;

public Animal(String name) {
this.name = name;
}

public Animal(String name, int age) {
this.name = name;
this.age = age;
}

public Animal() {

}

public void setName(String name) {
Log.e("Animal", "Animal: " + name);
this.name = name;
}

public static void setNum(int num) {
Animal.num = num;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return this.name;
}

public int getNum() {
return num;
}

public static void eat() {
System.out.println("staticMethod\t" + "eat\t" + "food\t");
}

public String getNameAndAge() {
return this.name + "\t" + this.age;
}

@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

Activity

package cn.com.codingce.mediaandroid.activity.jni;

import android.graphics.Color;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import cn.com.codingce.mediaandroid.R;
import cn.com.codingce.mediaandroid.entity.Animal;

public class JniActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_jni);
int num = 1;
int i = 0;

Animal animal = new Animal("花椒", 20);

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 80, 10, TimeUnit.SECONDS, new SynchronousQueue(true), new ThreadPoolExecutor.AbortPolicy());
Future<Boolean> future;
for (; i < num; i++) {
int finalI = i;
future = threadPoolExecutor.submit(() -> {
try {
useJNICreateJavaObject(finalI, Thread.currentThread().getName(), animal);
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
});
try {
Boolean boolean1 = future.get();
System.out.println(boolean1);
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}
createUI();
}

protected void createUI() {
//创建布局
FrameLayout.LayoutParams startParams = new FrameLayout.LayoutParams(200, 120);
startParams.gravity = Gravity.CENTER_HORIZONTAL;
Button startButton = new Button(this);
startButton.setTextColor(Color.BLUE);
startButton.setText("按钮");
startButton.setVisibility(View.VISIBLE);
startButton.setOnClickListener(view -> Toast.makeText(JniActivity.this, "点击", Toast.LENGTH_SHORT).show());
addContentView(startButton, startParams);
}

private native void useJNICreateJavaObject(int i, String threadName, Animal animal);

}

C++

jni_practice.h

//
// Created by Inke219223m on 2022/6/30.
//

#ifndef MEDIAANDROID_JNI_PRACTICE_H
#define MEDIAANDROID_JNI_PRACTICE_H


#include <jni.h>
#include <iostream>
#include "LogUtils.h"
#include "../CLog.h"
#include "pthread.h"
#include "unistd.h"
#include "queue"
using namespace std;

class jni_practice {

};

extern "C"
JNIEXPORT void JNICALL Java_cn_com_codingce_mediaandroid_activity_jni_JniActivity_useJNICreateJavaObject
(JNIEnv *, jobject, jint i, jstring thread_name, jobject inputAnimal);


//1、一般线程
//线程对象
pthread_t pthread;

void *threadDoThings(void *data) {
LOGD("jni thread do things");
pthread_exit(&pthread);
}

//2、线程锁
//产品队列,里面是int的队列
std::queue<int> queueInit;
//生产者线程
pthread_t pthread_produc;
//消费者线程
pthread_t pthread_customer;
//线程锁
pthread_mutex_t mutexInit;
//条件对象
pthread_cond_t cond;

void initMutex();

void *customerThread(void *data);

void *ptoducThread(void *data);

void *threadDoThings(void *data);

#endif //MEDIAANDROID_JNI_PRACTICE_H

jni_practice.cpp

//
// Created by Inke219223m on 2022/6/30.
//


#include "jni_practice.h"

using namespace std;

extern "C"
JNIEXPORT void JNICALL
Java_cn_com_codingce_mediaandroid_activity_jni_JniActivity_useJNICreateJavaObject(JNIEnv
*env,
jobject thiz,
jint
i,
jstring threadName,
jobject inputAnimal) {
//外层线程信息
const char *threadNameWithC = env->GetStringUTFChars(threadName, JNI_FALSE);
LOGE("线程Name: %s, %d", threadNameWithC, i)
/// Java层传递对象处理 START
//想要获取的字段 id
jfieldID fid;
//字段对应的具体的值
jstring inputJstr;

//将 Java 的字符串转换为 Native 的字符串
const char *str;
//获取 Java class
jclass inputAnimalCls = env->GetObjectClass(inputAnimal);
//获取对应字段的 id
fid = env->GetFieldID(inputAnimalCls, "name", "Ljava/lang/String;");
//如果字段为 NULL ,直接退出,查找失败
if (fid == nullptr) {
return;
}
//获取字段对应的值
inputJstr = (jstring) env->GetObjectField(inputAnimal, fid);
str = env->GetStringUTFChars(inputJstr, nullptr);
if (str == nullptr) {
return;
}
LOGD("input Object name is %s", str);
/// Java层传递对象处理 END

/// 创建对象 START
//获取 animal 对应的 jlcass 对象
jclass animalClazz = env->FindClass("cn/com/codingce/mediaandroid/entity/Animal");
//获取构造函数的 jmethodID
jmethodID animal_constructor = env->GetMethodID(animalClazz, "<init>",
"(Ljava/lang/String;I)V");
jstring name = env->NewStringUTF("INKE");
//调用构造函数创建 animal 对象
jobject animal = env->NewObject(animalClazz, animal_constructor, name, 20);
/// 创建对象END

/// 普通操作 START
//类中方法
//get 方法
jmethodID getName = env->GetMethodID(animalClazz, "getName", "()Ljava/lang/String;");
//获取 get 方法返回的 Name
auto jstringGetName = (jstring) env->CallObjectMethod(animal, getName);
const char *charName = env->GetStringUTFChars(jstringGetName, JNI_FALSE);
//__android_log_print(ANDROID_LOG_INFO, "mmm", "method = %s, msg = %s", __FUNCTION__, charName);
LOGE("Create Object Get name: %s", charName)

//set 方法
//新 Name
jstring nameMod = env->NewStringUTF("NewNameSetAchieve");
//获取 set 方法
jmethodID setName = env->GetMethodID(animalClazz, "setName", "(Ljava/lang/String;)V");
env->
CallVoidMethod(animal, setName, nameMod
);

//获取成员变量 name
jfieldID jfieldIdName = env->GetFieldID(animalClazz, "name", "Ljava/lang/String;");
//获取成员变量 name 的值
auto jstr = (jstring) env->GetObjectField(animal, jfieldIdName);
//jstring 转换成 char
const char *charNameJstr = env->GetStringUTFChars(jstr, JNI_FALSE);
LOGE("Get Object New Name: %s", charNameJstr)

//获取成员变量
jfieldID jfieldIdAge = env->GetFieldID(animalClazz, "age", "I");
//获取成员变量 Age 的值
jint jintAge = (jint)
env->
GetIntField(animal, jfieldIdAge
);
LOGE("Get Object Age:%d", jintAge)
/// 普通操作 END

/// 静态操作 START
//获取静态变量 num
jfieldID jfieldIdNum = env->GetStaticFieldID(animalClazz, "num", "I");
//获取静态变量 num 的值
jint num = (jint)
env->
GetStaticIntField(animalClazz, jfieldIdNum
);
cout << "获取静态变量 num 的值: " <<
num;

//修改静态变量 num 的值
jint jint1 = 2000;
env->
SetStaticIntField(animalClazz, jfieldIdNum, jint1
);
//获取静态方法 eat()
jmethodID jmethodId = env->GetStaticMethodID(animalClazz, "eat", "()V");
//调用静态方法 eat()
env->
CallStaticVoidMethod(animalClazz, jmethodId
);
jint num1 = (jint)
env->
GetStaticIntField(animalClazz, jfieldIdNum
);
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
"env->GetStaticIntField(animalClazz, jfieldIdNum): %d", num1);
/// 静态操作 END

/// 多线程 START
//创建线程
pthread_create(&pthread, nullptr, threadDoThings, nullptr
);

//生产者消费者模型
//初始化时,先往队列中添加10个产品
// for (
// int i = 0;
// i < 10; i++) {
// queueInit.push(i);
// }
//生产者消费者
//initMutex();
/// 多线程 END

/// 资源释放 START //
/// 引用型对象需要释放,包括 jstring, jclass, jobject, jbyteArray 等等;从作用域的角度分为两类:Global and Local References https://www.jianshu.com/p/c867e4abcb5f ///
//jstring
env->DeleteLocalRef(name);
env->DeleteLocalRef(jstringGetName);
env->DeleteLocalRef(nameMod);
env->DeleteLocalRef(jstr);
env->DeleteLocalRef(inputJstr);
//jbyteArray
//env->ReleaseByteArrayElements(imgdata, imgByte, 0);
//jclass
env->DeleteLocalRef(animalClazz);
//如果你要返回 animal 则不需要释放
env->DeleteLocalRef(animal);
/// 资源释放 END //

}

void *ptoducThread(void *data) {
while (queueInit.size() < 50) {
LOGD("生产者生产一个产品")
//操作队列前先加锁
pthread_mutex_lock(&mutexInit);
queueInit.push(1);
if (queueInit.size() > 0) {
LOGD("生产者通知消费者有产品产生,产品数量为:%d", queueInit.size());
//有了产品通知消费者
pthread_cond_signal(&cond);
}
//解锁线程
pthread_mutex_unlock(&mutexInit);
//休息4秒,单位是秒
sleep(4);
}
pthread_exit(&pthread_produc);
}

void initMutex() {
//初始化锁对象 对应pthread_mutex_destroy销毁锁对象
pthread_mutex_init(&mutexInit, nullptr);
//初始化条件变量 对应pthread_cond_destroy销毁条件变量
pthread_cond_init(&cond, nullptr);
//创建生产者线程,并传递参数
pthread_create(&pthread_produc, nullptr, ptoducThread, (void *) "product");
//创建消费者线程
pthread_create(&pthread_customer, nullptr, customerThread, nullptr);
}

void *customerThread(void *data) {
char *prod = (char *) data;
LOGD("%s", prod)
//这里用死循环,时间情况应该给一个变量来控制跳出循环
while (1) {
//操作队列前先加锁
pthread_mutex_lock(&mutexInit);
if (queueInit.size() > 0) {
queueInit.pop();
LOGE("消费者消费一个产品,产品数量为:%d", queueInit.size());
} else {
LOGE("产品消费完了,等待生产者生产......");
//阻塞线程等待生产者的通知
pthread_cond_wait(&cond, &mutexInit);
}
//解锁线程
pthread_mutex_unlock(&mutexInit);
//休息0.5秒,usleep单位是微妙
usleep(500 * 1000);
}
pthread_exit(&pthread_customer);
}