IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android NDK开发基础,Android入门你值得拥有 -> 正文阅读

[移动开发]Android NDK开发基础,Android入门你值得拥有


在kotlin中使用externl关键字声明jni方法:

class NativeDemo {
external fun stringFromJNI(): String;
}


有两种方式可以实现对应的native代码:  
(1)静态注册  
在cpp目录下,新建native\_lib.cpp,添加对应的native实现:

#include <jni.h>
#include

// JNIEXPORT JNICALL、参数里的前两个参数JNIEnv* env,jobject obj等是固定格式;固定参数中的jobject obj表示this
extern “C”
JNIEXPORT jstring JNICALL
Java_com_bc_sample_NativeDemo_stringFromJNI(
JNIEnv* env,
jobject obj) {
std::string hello = “Hello from C++”;
return env->NewStringUTF(hello.c_str());

// static的native方法,参数默认是jclass
extern “C”
JNIEXPORT void JNICALL
Java_com_bc_sample_NativeDemo_stringFromJNI(
JNIEnv* env,
jclass clazz) {

}


(2)动态注册  
在cpp目录下,新建native\_lib.cpp,在JNI\_OnLoad时调用env->RegisterNatives进行注册(JNI\_OnLoad是在动态库被加载时由系统进行调用):

// 需要注册jni方法所在的类
static const char *jniClassName = “com/bc/sample/NativeDemo”;

// 需要注册的jni方法,分别表示java方法名、方法签名、native函数指针
// 方法签名可以用javap命令查看:
// javap -s /Users/bc/Demo/app/build/intermediates/javac/debug/classes/com/bc/sample/NativeDemo.class
static JNINativeMethod methods[] = {{“stringFromJNI”, “()Ljava/lang/String”, (jstring *) native_stringFromJNI}};

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
jint result = -1;

if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK)
    return JNI_ERR;

if (!registerNatives(env))
    return JNI_ERR;

result = JNI_VERSION_1_6;
return result;

}

static int registerNatives(JNIEnv *env) {
jclass clazz = env->FindClass(jniClassName);
if (clazz == NULL)
return JNI_FALSE;

jint methodSize = sizeof(methods) / sizeof(methods[0]);
if (env->RegisterNatives(clazz, methods, methodSize) < 0)
    return JNI_FALSE;

return JNI_TRUE;

}

// native实现
jstring native_stringFromJNI(JNIEnv *env, jobject obj) {
return env->NewStringUTF(“hello”);
}


4.1 JavaVM 和 JNIEnv
-------------------

JNI 定义了两个关键数据结构“JavaVM”和“JNIEnv”,两者本质上都是指向函数表的二级指针。  
Android中每个进程只允许有一个JavaVM。JNIEnv作用域为单个线程,可通过JavaVM的getEnv来获得当前线程的JNIEnv,JNIEnv可通过GetJavaVM来获得JavaVM。  

// 保存JavaVM,方便在子线程中获取
static JavaVM *_my_jvm;

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
env->GetJavaVM(&_my_jvm)
JNIEnv *env = NULL;
jint result = -1;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK)
return JNI_ERR;
return result;
}


4.2 线程与JavaVM
-------------

在java代码中,可以通过Thread.start()启动一个线程;  
对于在native代码中通过pthread\_create() 或 std::thread 启动的线程,是没有JNIEnv的,也就无法调用JNI,可以使用 AttachCurrentThread() 或 AttachCurrentThreadAsDaemon() 函数将JavaVM附加到线程,附加后的线程可以调用JNI代码:

// 保存JavaVM,方便在子线程中获取
static JavaVM *_my_jvm;
// 保存java层obj,方便在子线程获取后回调结果
static java_obj;

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
env->GetJavaVM(&_my_jvm)
JNIEnv *env = NULL;
jint result = -1;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK)
return JNI_ERR;
return result;
}

jstring native_stringFromJNI(JNIEnv *env, jobject obj) {
java_obj = env->NewGlobalRef(obj);
}

void native_callback() {
JNIEnv *env = NULL;
_my_jvm->AttachCurrentThread(&env, nullptr);
jmethodId method_call_back = env->GetMethodId(user_class, “callback”, “()V”);
env->CallVoidMethod(java_obj, method_call_back);
// 使用完后需要detach
_my_jvm->DetachCurrentThread();
}


4.3 JNI数据类型
-----------

### 4.3.1 基础数据类型

/* jni.h源码 */

/* Primitive types that match up with Java equivalents. /
/
jni基础数据类型与java一一对应,二者可直接转换 /
typedef uint8_t jboolean; /
unsigned 8 bits /
typedef int8_t jbyte; /
signed 8 bits /
typedef uint16_t jchar; /
unsigned 16 bits /
typedef int16_t jshort; /
signed 16 bits /
typedef int32_t jint; /
signed 32 bits /
typedef int64_t jlong; /
signed 64 bits /
typedef float jfloat; /
32-bit IEEE 754 /
typedef double jdouble; /
64-bit IEEE 754 */

/* “cardinal indices and sizes” */
typedef jint jsize;

#ifdef __cplusplus
/*

  • Reference types, in C++
    */
    class _jobject {};
    class _jclass : public _jobject {};
    class _jstring : public _jobject {};
    class _jarray : public _jobject {};
    class _jobjectArray : public _jarray {};
    class _jbooleanArray : public _jarray {};
    class _jbyteArray : public _jarray {};
    class _jcharArray : public _jarray {};
    class _jshortArray : public _jarray {};
    class _jintArray : public _jarray {};
    class _jlongArray : public _jarray {};
    class _jfloatArray : public _jarray {};
    class _jdoubleArray : public _jarray {};
    class _jthrowable : public _jobject {};

typedef _jobject* jobject;
typedef _jclass* jclass;
typedef _jstring* jstring;
typedef _jarray* jarray;
typedef _jobjectArray* jobjectArray;
typedef _jbooleanArray* jbooleanArray;
typedef _jbyteArray* jbyteArray;
typedef _jcharArray* jcharArray;
typedef _jshortArray* jshortArray;
typedef _jintArray* jintArray;
typedef _jlongArray* jlongArray;
typedef _jfloatArray* jfloatArray;
typedef _jdoubleArray* jdoubleArray;
typedef _jthrowable* jthrowable;
typedef _jobject* jweak;

#else /* not __cplusplus */

/*

  • Reference types, in C.
    /
    typedef void
    jobject;
    typedef jobject jclass;
    typedef jobject jstring;
    typedef jobject jarray;
    typedef jarray jobjectArray;
    typedef jarray jbooleanArray;
    typedef jarray jbyteArray;
    typedef jarray jcharArray;
    typedef jarray jshortArray;
    typedef jarray jintArray;
    typedef jarray jlongArray;
    typedef jarray jfloatArray;
    typedef jarray jdoubleArray;
    typedef jobject jthrowable;
    typedef jobject jweak;

#endif /* not __cplusplus */


### 4.3.2 string类型

extern “C”
JNIEXPORT jstring JNICALL
Java_com_bc_sample_NativeDemo_stringFromJNI(
JNIEnv* env,
jobject obj, jstring str) {
jstring loacl_str = env->NewStringUTF(“local_str”);
const char * c = env->GetStringUTFChars(str, false);
// 删除避免内存泄漏
env->ReleaseStringUTFChars(str, c);
return env->NewStringUTF(hello.c_str());
}


### 4.3.3 引用类型

例如:java中定义了一个User类如下:

package com.bc.sample;

class User {
public String userName;
public static String TAG;

public void setName() {
}

public static void show() {
}

}


在jni中调用User相关方法的方式如下:

extern “C”
JNIEXPORT jobject JNICALL
Java_com_bc_sample_NativeDemo_objectFromJNI(
JNIEnv* env,
jobject obj, jobject user) {
jclass user_class = env->GetObjectClass(user);
// 1.1 变量
jfieldID user_name_id = env->GetFiledId(user_class, “userName”, “Ljava/lang/String”);
jstring name = env->NewStringUTF(“jack”);
env->SetObjectFiled(user, user_name_id, name);
// 1.2 静态变量
env->GetStaticFieldID(user_class, “TAG”, “Ljava/lang/String”);

// 2.1 方法
jmethodId method_set_name = env->GetMethodId(user_class, "setName", "()V");
env->CallVoidMethod(user, method_set_name);
// 2.2 静态方法
jmethodId method_show = env->GetStaticMethodId(user_class, "show", "()V");
env->CallStaticObjectMethod(user_class, method_show);

// 3 构造函数,用<init>表示
jclass usr_class = env->FindClass("com/bc/sample/User");
jmethodId method_init = env->GetMethodId(user_class, "<init>", "()V");
jobject usr = env->NewObject(usr_class, method_init);
jobjectArray usr_array = env->NewObjectArray(5, usr_class, nullptr);

// 局部引用超过512会报错local reference table overflow (max=512);所以用完后要释放
env->DeleteLocalRef(user_class);

}


### 4.3.4 数组类型

不管是基础类型的数组jintArray、jbooleanArray还是引用类型的数组jobjectArray,都是继承了jarray,是一个引用类型。

extern “C”
JNIEXPORT jobject JNICALL
Java_com_bc_sample_NativeDemo_objectFromJNI(
JNIEnv* env,
jobject obj, jintArray int_array,
jstringArray str_array,
jobjectArray user_array) {
int len = env->GetArrayLength(user_array);
// 1 基本数据类型数组
jint n = env->GetIntArrayElement(int_array, 0);
// 2 string数据类型数组
jstring str = static_cast(env->GetObjectArrayElement(str_array, 0));
const char * c = env->GetStringUTFChars(str, false);
env->ReleaseStringUTFChars(str, c);

// 3 引用类型数组;获得jobject后可按4.3.3中获得user数据
jobject user = env->GetObjectArrayElement(user_array, 0);
env->DeleteLocalRef(user);

}


4.4 java签名
----------

可以使用javap命令生成类的方法及参数签名:

javap -s java.lang.String


java类型及签名对应关系如下:

基本类型

V void 一般用于表示方法的返回值
Z boolean
B byte
C char
S short
I int
J long
F float
D double

引用类型前要加L

String Ljava/lang/String
Class Ljava/lang/Class

数组前要加[

int[] [I
Object[] [Ljava/lang/Object

方法签名:括号内表示参数,括号后表示返回类型(引用类型后要用;分隔)

String fun() () Ljava/lang/String;
int fun( int i, String str) (ILjava/lang/String;)I


4.5 JNI全局引用 局部引用 弱引用
--------------------

static java_obj;

jstring native_stringFromJNI(JNIEnv *env, jobject obj) {
// 局部引用
jclass str_clazz = env->FindClass(“Ljava/lang/String”);
env->DeleteLocalRef(str_clazz);
// 全局引用,手动调用DeleteGlobalRef后才释放
java_obj = env->NewGlobalRef(obj);
env->DeleteGlobalRef(obj);
// 弱引用,使用前需要判断是否为null
java_obj = env->NewWeakGlobalRef(obj);
jboolean is_null = env->IsSameObject(java_obj, nullptr);
}


4.6 JNI异常捕获
-----------

假设java中的user类中定义如下

class User {
public void setName() throws RuntimeException {
}

public native void stringFromJNI();

}


在native层调用java代码发生异常时,可按如下方式捕获:

jstring native_stringFromJNI(JNIEnv *env, jobject obj) {
jclass user_class = env->GetObjectClass(user);
jmethodId method_set_name = env->GetMethodId(user_class, “setName”, “()V”);
env->CallVoidMethod(user, method_set_name);
// 1.如果调用java方法setName时发生异常;可使用如下方法判断是否发生crash并捕获;否则会直接crash
jthrowable throwable_occurred = env->ExceptionOccurred();
if (throwable_occurred) {
env->ExceptionDescribe();
env->ExceptionClear();
}

// 2.或者当异常发生时,native层可以向java层抛出一个异常,在java层完成try-catch
jclass throwable_clazz = env->FindClass("java/lang/RuntimeException");
env->ThrowNew(throwable_clazz, "exception occurred");

}


4.7 JNI线程
---------

native开发常用的线程库:

1.  Posix API:<pthread.h>;  
    
2.  c++11支持的<thread.h>(本文不介绍);

#include<pthread.h>
// 线程互斥锁
pthread_mutex_t mutex;
// 线程条件变量
pthread_cond_t cont;

// 需要在子线程中执行的方法
void *sayHello(void * arg) {
pthread_mutex_lock(&mutex);
jstring local_arg = static_cast (arg);
LOG(local_arg);
pthread_mutex_unlock(&mutex);
return nullptr;
}

jstring native_stringFromJNI(JNIEnv *env, jobject obj) {
// 1 创建线程
pthread_t handle;
jstring loacl_str = env->NewStringUTF(“local_str”);
int result = pthread_create(&handle, nullptr, sayHello, loacl_str);

// 2 线程同步常用方法 
// pthread_mutex_lock() pthread_mutex_unlock()
pthread_mutex_init(&mutex, nullptr);
pthread_mutex_destroy(&mutex);
// pthread_cond_wait() pthread_cond_signal()
pthread_cond_init(&cond, nullptr);
pthread_cond_destroy(&cond);

总结

最后小编想说:不论以后选择什么方向发展,目前重要的是把Android方面的技术学好,毕竟其实对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

这里附上我整理的几十套腾讯、字节跳动,京东,小米,头条、阿里、美团等公司19年的Android面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](

)**

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

技术进阶之路很漫长,一起共勉吧~

本文已被腾讯CODING开源托管项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录,自学资源及系列文章持续更新中…
阿里、美团等公司19年的Android面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

[外链图片转存中…(img-1Lxz33Z2-1631187511236)]

**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](

)**

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

技术进阶之路很漫长,一起共勉吧~

本文已被腾讯CODING开源托管项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录,自学资源及系列文章持续更新中…

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-09-11 00:00:22  更:2021-09-11 00:00:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:34:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码