Android-JNI-03JNI深入
更新时间:2023-05-26 15:26:01 阅读量: 实用文档 文档下载
- Androidjni编程推荐度:
- 相关推荐
JNI深入
宝贵建议请发送至:-编程,始于黑马
Android课程同步笔记
Beta0.01版
By阳哥
JNI深入
Android-JNI-03JNI深入
1.JNI开发中常见错误(★★)
1.1动态库名称写错,或者不存在
static{
System.loadLibrary("hell0");
}
当我们在写上面代码的时候如果不小心将hello写成了hell0。或者libhello.so动态库不存在,那么系统启动时会
报如下异常。
1.2Android.mk配置文件写错
如果改配置文件中的某个参数名称被写错,那么我们在调用ndk-build.cmd
命令的时候很可能报如下异常。出现这类异常,就需要我们检查我们的Android.mk文件,看是否写错。
JNI深入
1.3目标文件名画蛇添足导致的错误
如果我们将Android.mk中的目标文件名:LOCAL_MODULE:
=hello写成了LOCAL_MODULE:=hello.so,那么当我们使用ndk进行编译的时候会出现如下错误。
上面的异常告诉我们,LOCAL_MODULE不能有文件拓展名。
1.4源文件名写错
如果我们将Android.mk中的源文件名:LOCAL_SRC_FILES
:=hello.c
写成了LOCAL_SRC_FILES:=helo.c(少些了一个单词),那么当使用ndk进行编译的时候会出现如下错误:
1.5平台使用错误
如果我们编译的时候用的是arm平台,但是将目标文件运行在了x86平台上,那么会产生如下错误。
JNI深入
如果想让我们编译的动态库既支持arm平台又支持x86平台,那么我们可以在工程中的jni目录下添加Application.mk文件。关于Application.mk的配置在本人的上一个文档中有说明,这里就不再介绍。
1.6C语言中被Java调用的方法名写错比如C语言中的方法jstringJava_com_itheima_jnihello_MainActivity_helloC
把helloC写成了heloC,那么将会报如下错误。
2.自动生成JNI头文件(★★)
对于一些特殊的Java方法名,我们很难写出其对应的JNI方法名,比如:publicnative
Stringa_b__c_d();这时候我们可以通过我们的JDK工具自动生成头文件。该工具位于JDK中,如果我们给电脑配置JAVA_HOME则可以直接在命令行中使用,使用法则很简单。如下图所示:
JNI深入
下面演示如何使用javah工具生成我们MainActivity.java中native方法的头文件。
将命令行控制台切换到我们MainActivity.class(注意:是.class不是.java)所在目录。本人的目录如下:执行javah命令:javahcom.itheima.jnihello.MainActivity,发现报了如下错误:
:出现上面的错误时因为我用的是jdk7版本,jdk7在编译MainActivity.class类的时候会查找其所有的父类,但是在当前命令行中是不可能有MainActivity类的父类路径的。有两种办法可以解决上述问题:
1)改成jdk6版本
2)将native方法定义在另外一个独立的类中
本人在这里采用第二种方法来解决该问题,因此我创建一个类,将所有的native方法都定义在该类中。JNIMethod.java代码清单如下:
packagecom.itheima.jnihello.jni;
publicclassJNIMethod{
publicnativeStringa_b__c_d();
}
再次执行javah命令:javahcom.itheima.jnihello.jni.JNIMethod
JNI深入
这次发现没有报错,生成的头文件在如下位置:
打开生成的头文件,将生成的方法签名拷贝出来添加到我们的hello.c源文件中即可。
3.JNI的值传递(★★★)
本章我们通过一个案例来介绍几种常见的JNI值传递场景。
JNI深入
创建一个新的Android工程《JNI值传递》。
在工程中创建com.itheima.jnipassdata.DataProvider类。在该类中定义三个native方法。代码清单如下:publicclassDataProvider{
/**
*模拟用C语言计算一些负责算法
*@paramx
*@paramy
*@return
*/
publicnativeintadd(intx,inty);
/**
*模拟用C语言加密字符串
*@paramstr
*@return
*/
publicnativeStringencideString(Stringstr);
/**
*模拟用C语言进行一些图像算法
*@paramcolorArray
*@return
*/
publicnativeint[]changeColor(int[]colorArray);
}
使用javah工具生成头文件
生成的头文件在相对于命令行的当前目录下。
在工程中创建jni目录,在改jni目录中创建Hello.c文件,将上一步生成的方法拷贝到Hello.c文件中,并实现里面的方法。Hello.c代码清单如下。
JNI深入
#include<stdio.h>
#include<malloc.h>
#include"jni.h"
//把java的string转化成c的字符串
char*Jstring2CStr(JNIEnv*env,jstringjstr)
{
char*rtn=NULL;
jclassclsstring=(*env)->FindClass(env,"java/lang/String");
//String
jstringstrencode=(*env)->NewStringUTF(env,"GB2312");//"gb2312"jmethodIDmid=(*env)->GetMethodID(env,clsstring,"getBytes",
"(Ljava/lang/String;)[B");//getBytes(Str);
jbyteArraybarr=
(jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode);//
String.getByte("GB2312");
jsizealen=(*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);//释放内存空间
returnrtn;
}
jintJava_com_ithiema_jnipassdata_DataProvider_add
(JNIEnv*env,jobjectobj,jintx,jinty){
returnx+y;
};
jstringJava_com_ithiema_jnipassdata_DataProvider_encideString
(JNIEnv*env,jobjectobj,jstringjstr){
char*str=Jstring2CStr(env,jstr);
char*hello="hello";
strcat(str,hello);
//下面的两种写法是一样的效果
//return(*(*env)).NewStringUTF(env,str);
return(*env)->NewStringUTF(env,str);
};
jintArrayJava_com_ithiema_jnipassdata_DataProvider_changeColor
JNI深入
(JNIEnv*env,jobjectobj,jintArray
jarray){
intsize=(*env)->GetArrayLength(env,jarray);
int*arr=(*env)->GetIntArrayElements(env,jarray,0);
inti;
for(i=0;i<size;i++){
arr[i]+=10;
}
returnjarray;
使用NDK工具将上面的C代码编译成动态库文件,首先得在工程的jni目录下添加Android.mk和Application.mk文件
在MainActivity类中调用C语言,MainActivity.java代码清单如下。
JNI深入
布局文件比较简单,就不再给出。运行上面代码,效果图如下:
3.1让C语言输出Log日志
让我们的C语言也能在LogCat中输出一些信息是一个很简单但也很常见的实际需求。为了方便演示我们直接在本章节中创建的工程中演示如何让C语言打印LogCat日志。
在Android.mk中添加如下属性:
LOCAL_LDLIBS:=-llog
在C源码头部引入如下头文件和宏定义
#include<android/log.h>
#defineLOG_TAG"System.out"
#defineLOGD(...)__android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)#defineLOGI(...)__android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
在C代码中可以将LOGD()或者LOGI()函数当做C语言中的printf()函数使用。
JNI深入
这里我将add方法添加添加了一条日志输入。
jintJava_com_ithiema_jnipassdata_DataProvider_add
(JNIEnv*env,jobjectobj,jintx,
jinty){
LOGI("x+y=%d",x+y);
returnx+y;
};
重新编译hello.so动态库,既然我们修改了Android.mk和C源码文件,那么一定要记得重新使用NDK编译C语言为动态库,否则日志不会输入而且也不报异常。再次部署程序到模拟器,调用add方法,发现在LogCat中成功输出了如下信息:
4.案例-调用美图秀秀的动态库(★★)
在美图秀秀1.0版本的时候程序员对其代码并没有加密以及反反编译等处理,因此我们可以将其apk反编译出来。里面关于图形的核心算法都是通过so库来实现的,我们可以拿过来直接使用。
声明:本文档中使用的美图秀秀只用于学习交流Android技术,禁止用于其他目的!
美图秀秀1.0版本下载地址:
将下载好的mtxx.apk进行反编译,将反编译好的资源作为原料备用。关于如何反编译美图秀秀请见本文档最后一章。
:解压好的目录打开lib包,发现只有armeabi一个文件夹,说明该so文件只能在arm架构的CPU上运行。创建一个新的Android工程《黑马美图秀秀》。将反编译好的libmtimage-jni.so拷贝到工程的libs->armeabi(该目录需要手动创建)目录下。通过反编译的jar包发现美图秀秀的jni方法都定义在JNI.java类中,我们用jd-gui工具将反编译的jar包打开,找到JNI.java,然后拷贝其中的native方法到我们的工程中。本人的工程目录结构如下所示:
JNI深入
:我们使用了美图秀秀的JNI源码,那么我们的JNI.java名字以及其所在的包名必须严格跟原美图秀秀保持一致,不然程序从so动态库中是找不到目标方法的。原因很简单,因为C中方法名是根据JNI.java中的方法全限定名生成的。
编写activity_main.xml布局文件,布局文件清单如下:
<LinearLayoutxmlns:android="/apk/res/android"
xmlns:tools="/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/iv"
/>
<Button
android:layout_gravity="right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="lomo"
android:onClick="click"
/>
</LinearLayout>
在工程的res->drawable-hdpi目录下放入一张图片。
JNI深入
编写MainActivity.java代码,在改类中实现核心方法
publicclassMainActivityextendsActivity{
privateImageViewiv;
privateBitmapbitmap;
static{
System.loadLibrary("mtimage-jni");
}
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(yout.activity_main);
bitmap=BitmapFactory.decodeResource(getResources(),
R.drawable.beautiful);
iv=(ImageView)findViewById(R.id.iv);
iv.setImageBitmap(bitmap);
}
publicvoidclick(Viewview){
JNIjni=newJNI();
//获取Bitmap的宽和高
intwidth=bitmap.getWidth();
intheight=bitmap.getHeight();
//创建一个整型数组,存放每个像素点
int[]pixels=newint[width*height];
/**
*获取bitmap的像素数组
*第一个参数是int[]
*第二个参数是第获取一个像素的偏移量,这里是0,也就是从第一个像素开始获取
*第三个参数每行获取多少个像素,当然bitmap的宽度就是每行的像素个数
*第四个参数、第五个参数分别是开始获取像素的x、y坐标,这个坐标是相对于bitmap本身来说的,因此是0,0
*第六、七个参数分别是每行、每列获取像素的个数
*/
bitmap.getPixels(pixels,0,width,0,0,width,height);
//调用美图秀秀API
JNI深入
jni.StyleLomoB(pixels,
width,height);
//重新根据pixels数组创建一个Bitmap对象
BitmapnewBitmap=Bitmap.createBitmap(pixels,width,height,
bitmap.getConfig());
iv.setImageBitmap(newBitmap);
}
}
运行上面打代码,效果如下:
1)处理前
2)处理后
JNI深入
5.C语言调用Java代码(★★★)
之前我们学习的JNI都是Java代码调用C代码,本章节中演示C代码如何调用Java
代码。
创建一个新的Android工程《C语言调用Java》,在工程中创建jni目录,在改目录下放置Android.mk和jni.h
文件(从老工程中拷贝)。工程目录结构如下:
编写DataProvider.java文件,在该文件中编写native方法,DataProvider.java代码清单如下:
publicclassDataProvider{
//C代码调用该方法发送短信
publicvoidmethodInJava(){
System.out.println("我是Java中的方法,我被调用了");
SmsManagermanager=SmsManager.getDefault();
manager.sendTextMessage("5556",null,"helloI'mfromJava",null,null);}
//C代码调用该方法传递一个字符串,获取一个字符串
publicStringmethodInJava2(Stringstr){
return"hello:"+str;
JNI深入
}
//C代码调用一个空参返回值为void的实例方法
publicvoidmethodInJava3(){
System.out.println("我是Java中的方法3,我被调用了");
}
//C语言调用静态方法
publicstaticvoidmethodInJava4(){
System.out.println("我是静态方法。被调用了。");
}
//定义四个native方法,这些方法在MainActivity中被调用,这些方法在C代码中回调上面的Java代码
publicnativevoidcallCMethod();
publicnativeStringcallCMethod2(Stringstr);
publicnativevoidcallCMethod3();
publicnativevoidcallCMethod4();
}
在jni中创建calljava.c文件,在该文件中实现调用java的方法。代码清单如下。
#include<jni.h>
#include<stdio.h>
voidJava_com_itheima_callJava_DataProvider_callCMethod(JNIEnv*env,jobjectobj){
//类似java的反射,获取java对象
jclassclazz=(*env)->FindClass(env,"com/itheima/callJava/DataProvider");//根据方法签名获取目标方法
jmethodIDmethodID=(*env)->GetMethodID(env,clazz,"methodInJava","()V");//调用目标方法
(*env)->CallVoidMethod(env,obj,methodID);
}
jstringJava_com_itheima_callJava_DataProvider_callCMethod2(JNIEnv*env,jobjectobj,jstringstr){
jclassclazz=(*env)->FindClass(env,"com/itheima/callJava/DataProvider");jmethodIDmethodID=
(*env)->GetMethodID(env,clazz,"methodInJava2","(Ljava/lang/String;)Ljava/lang/String;");
jstringjstr=(*env)->CallObjectMethod(env,obj,methodID,str);
returnjstr;
}
voidJava_com_itheima_callJava_MainActivity_callCMethod3(JNIEnv*env,jobjectobj){
jclassclazz=(*env)->FindClass(env,"com/itheima/callJava/DataProvider");
JNI深入
jmethodIDmethodID
=(*env)->GetMethodID(env,clazz,"methodInJava3","()V");jobjecto=(*env)->AllocObject(env,clazz);
(*env)->CallVoidMethod(env,o,methodID);
}
voidJava_com_itheima_callJava_DataProvider_callCMethod4(JNIEnv*env,jobjectobj){
jclassclazz=(*env)->FindClass(env,"com/itheima/callJava/DataProvider");jmethodIDmethodID=
(*env)->GetStaticMethodID(env,clazz,"methodInJava4","()V");
(*env)->CallStaticVoidMethod(env,clazz,methodID);
}
使用NDK工具,将calljava.c编译成动态库文件。(NDK的使用在上一篇文档中有详细的介绍,这里就不再说明)
在MainActivity.java中调用C语言,代码清单如下:
布局文件比较简单这里就不再给出。
JNI深入
6.用C++实现JNI(★★)
C++语言是面向对象的编程语言,源于C语言,部分语法通用。本章节中主要介绍如何使用C++完成简单的JNI
开发。
创建一个新Android工程《cpp实现jni》,创建jni包,在该包下创建JNI.java文件,在该类中写naive方法。因为我们是C++项目,因此需要给当前工程添加nativeSupport。右键点击项目,在弹出的对话框中选择AndroidTools,然后选择Addnative
Support,弹出如下对话框:
这个名字是C++源文件名,因为没有实际的业务意义一次我们这里就随便输入一个了。
在工程中创建com.itheima.cppjni.jni包,在该包下创建JNI.java,JNI.java代码清单如下:
publicclassJNI{
publicnativevoidhelloFormCPP();
}
JNI深入
使用javah工具生成头文件,并将生成的头文件拷贝到工程根目录下的jni目录中。
为了让编译器提示C++语言,我们需要给工程添加C++库。
右键点击工程,选择
Properties,在弹出的对话框中选择C/C++General->PathsAndSymbols,弹出如下对话框:
点击图中红色框中的Add按钮,在弹出的对话框(如下图)中点击File
system,然后选择ndk安装目录,选择如下目录:D:\software\ndkr9\android-ndk-r9b\platforms\android-19\arch-arm\usr\include
JNI深入
然后点击OK。
:上面目录中红色的是本人的ndk所在目录,大家找到自己的ndk所在目录即可。然后随便选择一个platform即可,但是推荐大家选择一个高版本的平台。
在gaga.cpp中引入上面生成的头文件。同时编辑gaga.cpp,代码清单如下:
#include<jni.h>
#include"com_itheima_cppjni_jni_JNI.h"
JNIEXPORTjstringJNICALLJava_com_itheima_cppjni_jni_JNI_helloFromCPP(JNIEnv*env,jobjectobj){
returnenv->NewStringUTF("gagafromcpp");
};
编写MainActivity.java,在该方法中加载动态库
publicclassMainActivityextendsActivity{
static{
System.loadLibrary("gaga");
}
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(yout.activity_main);
}
publicvoidclick(Viewview){
JNIjni=newJNI();
StringhelloFromCPP=jni.helloFromCPP();
Toast.makeText(this,helloFromCPP,1).show();
}
}
将上面工程部署到一个arm架构的模拟器上。在部署的时候观察控制台,发现控制台输出如下信息
****BuildofconfigurationDefaultforprojectcpp代码jni开发****
D:\software\ndkr9\android-ndk-r9b\ndk-build.cmdall
C:\Users\thinkpad\workspace\cpp代码jni开发>remThisisaWindowscmd.exescriptusedtoinvoketheNDK-specificGNUMakeexecutable
JNI深入
C:\Users\thinkpad\workspace\cpp代码jni开发>call
"D:\software\ndkr9\android-ndk-r9b\find-win-host.cmd"NDK_WIN_HOST
AndroidNDK:WARNING:
APP_PLATFORMandroid-19islargerthan
android:minSdkVersion8in./AndroidManifest.xml
[armeabi]Compile++thumb:gaga<=gaga.cpp
[armeabi]StaticLibrary:libstdc++.a
[armeabi]SharedLibrary:libgaga.so
[armeabi]Install:libgaga.so=>libs/armeabi/libgaga.so
****BuildFinished****
:通过控制台,我们发现当我们的工程添加本地支持以后,当我们在部署的时候eclipse会自动的完成动态库的编译工作。而且我们还发现在生成动态之前先生成了libstdc++.a静态库然后才生成了动态库。
上面代码运行效果如下图所示:
7.案例-锅炉压力监测(★★★)
需求:硬件设备可以监测锅炉的压力,监测代码逻辑是用C语言编写。客户端用java代码每一秒调用一次C语言,
正在阅读:
Android-JNI-03JNI深入05-26
中小学科普知识竞赛试题(145)05-28
攻破一把智能门锁的N种方法05-06
东北师大语言学及应用语言学考研经验09-22
阅读与写作训练二10-08
道岔标准化作业程序10-30
生物工程设备试题题库11-24
2022-2022学年第一学期六年级语文期末模拟试卷含答案04-15
围绕发展抓党建 抓好党建促发展08-08
人事专员实习周记 (5000字)03-08
- 教学能力大赛决赛获奖-教学实施报告-(完整图文版)
- 互联网+数据中心行业分析报告
- 2017上海杨浦区高三一模数学试题及答案
- 招商部差旅接待管理制度(4-25)
- 学生游玩安全注意事项
- 学生信息管理系统(文档模板供参考)
- 叉车门架有限元分析及系统设计
- 2014帮助残疾人志愿者服务情况记录
- 叶绿体中色素的提取和分离实验
- 中国食物成分表2020年最新权威完整改进版
- 推动国土资源领域生态文明建设
- 给水管道冲洗和消毒记录
- 计算机软件专业自我评价
- 高中数学必修1-5知识点归纳
- 2018-2022年中国第五代移动通信技术(5G)产业深度分析及发展前景研究报告发展趋势(目录)
- 生产车间巡查制度
- 2018版中国光热发电行业深度研究报告目录
- (通用)2019年中考数学总复习 第一章 第四节 数的开方与二次根式课件
- 2017_2018学年高中语文第二单元第4课说数课件粤教版
- 上市新药Lumateperone(卢美哌隆)合成检索总结报告
- JNI
- 深入
- Android
- 03
- 大学古代文学论文
- 工商常识性执法知识问答
- 2012-2013学年八年级物理上学期期中考试试题(无答案) 新人教版
- 论文格式装订顺序、范文20085614311209
- 基金大幅加仓
- 中科院研究生院集成电路专业课程设置
- 2006年12月四川省公务员考试《行政职业能力测验》真题答案及解析
- 向日的葵花非常词汇-----国际组织专有名词科普
- 最简易声控电路(声控灯,声控开关,声控门铃)
- 【参考文档】201X成人英语三级考试时间-推荐word版 (3页)
- 2015第三届中国汉字听写大会全部词语(13场)
- LCD典型结构的配合公差
- 基于STC12C5410AD的太阳能路灯控制器设计
- 2 2012年江苏省镇江市高二学业水平测试模拟试卷(化学)
- 男士化妆品市场现状
- 刍议高中学生数学学习方法的转变
- 高考英语词汇(练习用)
- 酒店计算机机房管理制度
- 七氟丙烷系统施工方案之令狐文艳创作
- 《图形的放大和缩小》教学设计