Android之JNI开发
最近看了个领导给的小项目,里面有使用到JNI,就学习了一下,在这里对我的学习过程做一个总结和记录。
demo的运行效果如下:
首先是安装NDK和一些其他的插件,由于我使用的Android Studio版本为3.0.1(3.2版本以上不存在此问题),所以需要下载一个ndk16的包,tool-chain目录下将报错信息里面缺少的部分放进去。
在MainActivity的同级下建立cpp文件夹用以存放相应的Java类:
之后先写类中的方法:
public static native String stringFromNDK();
通过Android自带的Terminal进入build文件夹的debug目录下,并输入命令,进行编译
之后就会在目录下看到生成的.h文件
在Java目录的同级下建立jni目录,用以存放.h头文件和.mk文件,将上面生成的.h文件剪切到此文件夹下
在jni目录下,创建和.h文件名称相同的.c文件
之后在.c文件中写入相应的代码
// // Created by zhang.qiongwen on 2019/11/26. // //这里的include的内容即为你生成的同名.h文件 #include "com_example_zhangqiongwen_jni_demo_cpp_HelloJNI.h"
//这里的方法会写在.h文件中,只是没有方法体,也没有声明传入的参数,只是指定了传入的类型,可以直接拿过来用,需要将传入的JNIEnv的几个参数补上 JNIEXPORT void JNICALL Java_com_example_zhangqiongwen_jni_1demo_cpp_HelloJNI_HelloJNIFromNDK (JNIEnv *env, jobject job, jobject jobject1){ jclass jclass1 = (*env)->FindClass(env,"com/example/zhangqiongwen/jni_demo/TestClassOfJNI"); jmethodID jmethod = (*env)->GetMethodID(env, jclass1, "makeToastInContext","()V"); (*env)->CallVoidMethod(env, jobject1, jmethod); };
再在jni目录中创建两个.mk文件
Android.mk文件内容如下:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS)
//这是编译生成的文件名字 LOCAL_MODULE := jni-test
//这里我编译了两个文件,中间以空格隔开 LOCAL_SRC_FILES := com_example_zhangqiongwen_jni_demo_cpp_HelloNDK.c com_example_zhangqiongwen_jni_demo_cpp_HelloJNI.c include $(BUILD_SHARED_LIBRARY)
Application.mk文件内容如下:
APP_ABI := all
还需要在build.gradle文件中添加依赖(ndkbuild指定.mk文件的路径,sourceSets指定的是生成的so库的路径)
以及在gradle.properties中添加一行
Android.useDeprecatedNdk=true
然后rebuild以下项目,如果能在响应的目录下找到so库文件,就说明大功告成了
在src文件夹的同级下建立lib文件夹,将so库放进去
就可以在java文件中调用so库了
以上就是如何使用JNI来从Java调用C中的方法,那么当C需要调用Java中的方法时,该如何做呢
其他步骤基本与以上的一致,只是在编写.c文件时候需要多一个查询方法签名的步骤。
通过Terminal进入debug目录中,输入javap -s +需要查询的类 来对签名进行查询
再编写.c文件:
// // Created by zhang.qiongwen on 2019/11/26. // #include "com_example_zhangqiongwen_jni_demo_cpp_HelloJNI.h" JNIEXPORT void JNICALL Java_com_example_zhangqiongwen_jni_1demo_cpp_HelloJNI_HelloJNIFromNDK (JNIEnv *env, jobject job, jobject jobject1){
//获取类名 jclass jclass1 = (*env)->FindClass(env,"com/example/zhangqiongwen/jni_demo/TestClassOfJNI");
//获取方法名
//这里就需要填入之前查询到的方法的签名 jmethodID jmethod = (*env)->GetMethodID(env, jclass1, "makeToastInContext","()V");
//调用函数 (*env)->CallVoidMethod(env, jobject1, jmethod); };
工程所有代码如下:
结构目录:
activity_mian.xml:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.zhangqiongwen.jni_demo.MainActivity"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@+id/blacksheepwar" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintVertical_bias="1" android:text="somethingfornothing" android:id="@+id/somthingfornothing" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintTop_toTopOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintBottom_toTopOf="@id/somthingfornothing" app:layout_constraintVertical_chainStyle="packed" android:text="blacksheepwar" android:id="@+id/blacksheepwar" /> </android.support.constraint.ConstraintLayout>
MainActivity.java:
package com.example.zhangqiongwen.jni_demo; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast; import com.example.zhangqiongwen.jni_demo.cpp.HelloJNI; import com.example.zhangqiongwen.jni_demo.cpp.HelloNDK; public class MainActivity extends AppCompatActivity { static HelloJNI mHelloJni = new HelloJNI(); Button mButtonTest; Button mButtonTest2; TestClassOfJNI mTestClassOfJNI = new TestClassOfJNI(this, MainActivity.this); static{ System.loadLibrary("jni-test"); // System.loadLibrary("jni-"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButtonTest = findViewById(R.id.somthingfornothing); mButtonTest2 = findViewById(R.id.blacksheepwar); mButtonTest.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mHelloJni.HelloJNIFromNDK(mTestClassOfJNI); } }); mButtonTest2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getBaseContext(), HelloNDK.stringFromNDK(), Toast.LENGTH_LONG).show(); } }); } public void makeToast(){ Toast.makeText(this, "this has been used by C", Toast.LENGTH_LONG).show(); } }
TestClassOfJNI:
public class TestClassOfJNI { Context mMainContext; MainActivity mMainActivity; public TestClassOfJNI(Context mContext, MainActivity mMainActivity){ this.mMainContext = mContext; this.mMainActivity = mMainActivity; } public void makeToastInContext(){ mMainActivity.makeToast(); } }
HelloJNI:
public class HelloJNI { public HelloJNI(){ } public native void HelloJNIFromNDK(TestClassOfJNI a); }
HelloNDK:
public class HelloNDK { public HelloNDK(){ } public static native String stringFromNDK(); }
HelloJNI.c:
#include "com_example_zhangqiongwen_jni_demo_cpp_HelloJNI.h" JNIEXPORT void JNICALL Java_com_example_zhangqiongwen_jni_1demo_cpp_HelloJNI_HelloJNIFromNDK (JNIEnv *env, jobject job, jobject jobject1){ jclass jclass1 = (*env)->FindClass(env,"com/example/zhangqiongwen/jni_demo/TestClassOfJNI"); jmethodID jmethod = (*env)->GetMethodID(env, jclass1, "makeToastInContext","()V"); (*env)->CallVoidMethod(env, jobject1, jmethod); };
HelloNDK.c:
#include "com_example_zhangqiongwen_jni_demo_cpp_HelloNDK.h" jstring Java_com_example_zhangqiongwen_jni_1demo_cpp_HelloNDK_stringFromNDK(JNIEnv *env,jobject thiz){ return (*env)->NewStringUTF(env,"I am Str from jni libs!"); }