Android之JNI开发入门

1.交叉编译

======

  • 在一个平台上去编译另一个平台上可以执行的本地代码

  • cpu平台 arm x86 mips

  • 操作系统平台 Windows Linux max os

  • 原理 模拟不同平台的特性去编译代码

2. jni开发工具

===========

  • ndk native develop kit

  • ndk 目录

  • docs 帮助文档

  • platforms 好多平台版本文件夹 选择时选择项目支持的最小版本号对应的文件夹

  • 每一个版本号的文件夹中放了 不同cpu架构的资源文件

  • include文件夹 jni开发中常用的 .h头文件

  • lib 文件夹 google打包好的 提供给开发者使用的 .so文件

  • samples google官方提供的样例工程 可以参考进行开发

  • android-ndk-r9d\build\tools linux系统下的批处理文件 在交叉编译时会自动调用

  • ndk-build 交叉编译的命令

  • cdt eclipse的插件 高亮C代码 C的代码提示

3. jni helloworld

==================

- jni开发的步骤


1.写Java代码 声明本地方法,用到native关键字,本地方法不用去实现

2.项目根目录下创建jni文件夹

3.在jni文件夹下创建.c文件

  • 本地函数命名规则:Java_包名_类名_本地方法名

  • JNIENV* env JNIEnv 是JNINativeInterface这个结构体的一级指针

  • JniNativeInterface这个结构体定义了大量的函数指针

  • env 就是结构体JniNativeInterface这个结构体的二级指针

  • (*env)->调用结构体中的函数指针

  • 第二个参数jobject 调用本地函数的java对象就是这个jobject

jstring Java_com_xfhy_jnihelloworld_MainActivity_helloFromC(JNIEnv* env,jobject thiz){

char* str = “hello from c!”;

//到jni.h中找到如下方法 jstring (NewStringUTF)(JNIEnv, const char*);

return (*env)->NewStringUTF(env,str);

}

函数中必须有2个形参:JNIEnv* env,jobject thiz;如果java中的native函数有形参的话,则需要把这些形参加在JNIEnv* env,jobject thiz这个2个形参之后.

4.导入

4. jni开发中的常见错误

===============

  • java.lang.UnsatisfiedLinkError: Native method not found: 本地方法没有找到

  • 本地函数名写错

  • 忘记加载.so文件 没有调用System.loadlibrary

  • findLibrary returned null

  • System.loadLibrary("libhello"); 加载动态链接库时 动态链接库名字写错

  • 平台类型错误 把只支持arm平台的.so文件部署到了 x86cpu的设备上

在jni目录下创建 Application.mk 在里面指定

APP_ABI := armeabi x86

APP_PLATFORM := android-14

  • javah 命令:生成java代码中本地方法名对应的C语言的函数名

使用方法:javah com.xfhy.jnihelloworld.MainActivity

  • jdk 1.7 项目 src目录下运行javah

  • jdk 1.6 项目 bin目录下 classes文件夹

  • javah native方法声明的java类的全类名

5.jni简便开发流程

===========

  • ① 写java代码 native 声明本地方法

  • ② 添加本地支持 右键单击项目->andorid tools->add native surport

  • 如果发现 finish不能点击需要给工作空间配置ndk目录的位置

  • window->preferences->左侧选择android->ndk 把ndk解压的目录指定进来

  • ③ 如果写的是.c的文件 先修改一下生成的.cpp文件的扩展名 不要忘了 相应修改Android.mk文件中LOCAL_SRC_FILES的值

  • ④ javah生成头文件 在生成的头文件中拷贝c的函数名到.c的文件

  • ⑤ 解决CDT插件报错的问题

  • 右键单击项目选择 properties 选测 c/c++ general->paths and symbols->include选项卡下->点击add…->file system 选择ndk目录下 platforms文件夹 对应平台下(项目支持的最小版本)

usr 目录下 arch-arm -> include 确定后 会解决代码提示和报错的问题

  • ⑥编写C函数 如果需要单独编译一下c代码就在c/c++视图中找到小锤子,点一下小锤子就编译了

  • 如果想直接运行到模拟器上 就不用锤子了,直接右键run as,然后就自动编译了

  • ⑦ java代码中不要忘了 System.loadlibrary();

static {

System.loadLibrary(“hello”); //加载动态链接库

}

6. Java向C传递一些基本的类型,处理

======================

将一个jstring转换成一个c语言的char* 类型工具方法

char* _JString2CStr(JNIEnv* env, jstring jstr) {

char* rtn = NULL;

jclass clsstring = (*env)->FindClass(env, “java/lang/String”);

jstring strencode = (*env)->NewStringUTF(env,“GB2312”);

jmethodID mid = (*env)->GetMethodID(env, clsstring, “getBytes”, “(Ljava/lang/String;)[B”);

jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid, strencode); // String .getByte(“GB2312”);

jsize alen = (*env)->GetArrayLength(env, barr);

jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);

if(alen > 0) {

rtn = (char*)malloc(alen+1); //“\0”

memcpy(rtn, ba, alen);

rtn[alen]=0;

}

(*env)->ReleaseByteArrayElements(env, barr, ba,0);

return rtn;

}

int类型

JNIEXPORT jint JNICALL Java_com_xfhy_javapassdata_JNI_add

(JNIEnv * env, jobject clazz, jint x, jint y){

return x+y; //直接返回x+y

}

String类型

JNIEXPORT jstring JNICALL Java_com_xfhy_javapassdata_JNI_sayHelloInC

(JNIEnv *env, jobject clazz, jstring str){

//将jstring转换成char* 类型

char* cstr = _JString2CStr(env,str);

//调用C语言的strlen测量cstr字符串的长度

int length = strlen(cstr);

int i=0;

for(i=0; i<length; i++){

*(cstr+i) += 1; //将字符串+1

}

return (env)->NewStringUTF(env,cstr); //将char 类型转换成String类型返回

}

int[]类型

JNIEXPORT jintArray JNICALL Java_com_xfhy_javapassdata_JNI_arrElementsIncrease

(JNIEnv *env, jobject clazz, jintArray jArray) {

//jsize (GetArrayLength)(JNIEnv, jarray); 返回数组长度

int length = (*env)->GetArrayLength(env,jArray);

//jint* (GetIntArrayElements)(JNIEnv, jintArray, jboolean*); 最后一个参数表示是否拷贝,可以不用传值

//返回int* 返回该数组的首地址 这样就可以直接通过该指针直接操作该数组了

int* cArray = (*env)->GetIntArrayElements(env,jArray,NULL);

int i;

for(i=0; i<length; i++) {

*(cArray+i) += 10;

}

return jArray; //直接将原数组返回(这时已经是修改过了的)

}

7.C代码中向logcat输出内容

=================

1.Android.mk文件增加以下内容

LOCAL_LDLIBS += -llog

2.C代码中增加以下内容

#include <android/log.h>

#define LOG_TAG “xfhy”

#define LOGD(…) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, VA_ARGS)

#define LOGI(…) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, VA_ARGS)

  • define C的宏定义 起别名 #define LOG_TAG “xfhy” 给”xfhy”起别名LOG_TAG

  • #define LOGI(…) android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS),ANDROID_LOG_DEBUG表示优先级 debug ANDROID_LOG_INFO表示info 这些在log.h中可以看到

  • 给 __android_log_print函数起别名 写死了前两个参数 第一个参数 优先级 第二个参数TAG

  • VA_ARGS:是可变参数的固定写法

  • LOGI(…)在调用的时候 用法跟printf()一样

8. C代码回调java方法

===============

首先需要了解:

Java反射

public class Demo {

public static void main(String[] args) {

//1.获取字节码对象

Class util = Utils.class;

try {

//2.获取Method对象 方法名,参数类型

Method method = util.getMethod(“test”, String.class);

//3.通过字节码对象创建一个Obejct

Object obj = util.newInstance();

//4.通过对象调用方法 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法

method.invoke(obj, “hello”);

} catch (Exception e) {

e.printStackTrace();

}

}

public class Utils {

public void test(String string) {

System.out.println(string);

}

}

}

C代码回调java方法

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

img

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

  • 无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!!
  • 我希望每一个努力生活的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。

当我们在抱怨环境,抱怨怀才不遇的时候,没有别的原因,一定是你做的还不够好!

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

21年的面试题**,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

[外链图片转存中…(img-3XO2sjhy-1713788080271)]

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

  • 无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!!
  • 我希望每一个努力生活的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。

当我们在抱怨环境,抱怨怀才不遇的时候,没有别的原因,一定是你做的还不够好!

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值