android开发5年了,老实说,前几年都是在混日子,技术基本原地踏步毫无长进,近一年半突然开窍了,长江后浪拍前浪,安于现状的程序员死在沙滩上,所以基本上从基础上,把以前自己怕的,不想去看的都列计划看了一大部分,JNI也是一个门槛吧,想到看到就头疼,不行逼着自己搞。C语言大学学了,但是自己基本完全没记住学了什么,花了2个月时间看了基础,指针,总算大体不复杂的代码能看懂了,学C也有一个好处,底层的种种原理对于上层来说都是通用的,以前不理解的现在能稍微理解了.好吧废话不多说,直接进入主题,JNI开发第一篇:android调JNI和JNI调android基本用法。后续会有一系列,随着学习继续更新。
工具android studio 3.2.1 创建JNI程序直接看到最后,用默认的下面两个看自己的选择了,应用过后会生成带有JNI开发的android工程
android studio3.2.1中有一个非常好的功能点,引入JNI之后会把android源码中的一些库文件全部展示给你,这样在之后设计到对android底层库的调用的时候不需要再自己翻源码找头文件什么的了,这点非常赞啊。
工程自带一个DEMO例子,会显示来自JNI的一断文字,这个等于就是应用层调JNI的基础展示了
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
// CallMethod();
// CallStaticMethod();
}
public void test1(String a){
Toast.makeText(getApplicationContext(),a,1).show();
}
public static void test2(){
Log.d("debug","这是来自JNI的消息");
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
我们来分析下基本的调用流程,先加载动态库文件,这个其实做android的应该都不陌生就是各种SO文件,然后有一个方法
public native String stringFromJNI() 这个就是要调我们JNI中执行的方法了,必须要用native,没有函数的实现。再来看JNI层的代码
extern "c" 是表示支持C语言。 后面的一串我们看中间的白色修饰,表示这个函数的返回值类型是jstring,什么是jstring?jni封装了自己的数据结构 jstring jobject jint等等,对应java的基本数据类型,string和引用对象,不知道的这个可以自行查看很简单。
我们继续看函数名称,很长一大串啊我滴妈,看着就头疼,其实不用怕,这个都算固定格式的,可以通过javah命令自动生成,在AS3.2.1 中更方便,写好native体,按照提示就可以一键生成了。Java开头,跟包名类名方法名,中间用_隔开即可非常简单,就是看着吓人
里面的具体内容就比较简单了,JNIEnv指正 调用方法创建一个Jstring 对象返回个JAVA层,Jstring 对应java中的string对象。通过JNI返回给JAVA的内容,都要转成JNI的数据结构再返回
基本调用就是这样,是不是非常简单。漫过了心里的一个坎了就,接下来说JNI调用JAVA。如果对JAVA反射很熟的话,下面的内容你会觉得更加容易 Come on
我在MainActivity中创建了两个变量,一个静态,一个非静态,通过JNI修改它们的值
MainActivity写好方法体
构建JNI中的方法
方法声明我们就跳过不说了,我们看看里面具体的操作。JNI跳JAVA非常想反射
方法体里面有两个参数JNIEnv jobject,前一个是JNI的环境指针,后一个代表JAVA调用的当前对象,
JNI通过调用的对象获取到class的对象,JNI中jclass,通过jclass可以获得class中的很多属性,比如方法,属性等等。JNI调用的基本流程都是一样的,拿到class,先获取方法或者属性的ID,然后通过这个ID来拿到设置属性或者调用方法。
我们来说说具体的调用
GetrFieldID/GetMethodID传入jclass 第二个参数属性/方法的名称,第三个参数是属性/方法的签名。签名是JNI中的调用的重点,可以简单点的说是,用在属性上描述的是属性的类型,用在方法上面面试方法的参数类型和返回值类型,通过这个再结合名称就可以找到唯一的方法或者变量ID。具体签名对照下表。
拿到ID之后就好办了,属性通过getObjectField传入对象引用 属性ID就可以获得这个对象的值了,可以在这上面进行操作啦。当然属性有静态的,可以通过getStaticField传入class和属性ID来操作
方法的话拿到ID,可以通过Call<type>method()来进行操作,type表示的是返回值的类型
基本互相调用流程就是这样,很简单,没有我以前想的那么可怕,凡是还是要上手才行
代码https://github.com/loulousky/StartJni