jni开发流程
这里使用Cmake方式,简单方便(需要AS2.2以上)
1.准备环境 下载NDK、CMake、LLDB打开AS Settings->Android SDK->SDK tools,点击右下角Show Package Details下载最新的NDK、Cmake、LLDB
- 在项目main目录下新建cpp文件夹用于存放C/C++代码(文件夹名字可以随意取)
- 在项目目录下创建CMakeLists目录下创建CMakeLists.txt(文件名不可改),在CMakeLists中输入以下内容内容:
cmake_minimum_required(VERSION 3.4.1)
add_library( # Specifies the name of the library.
#设置生成库的名字
test-lib
# Sets the library as a shared library.可以暂时不用管,就写SHARED
SHARED
# 设置c++代码路径(C++代码相对于CMakeList的路径),有多个C++文件则添加多个
src/main/cpp/test.cpp
src/main/cpp/test1.cpp
)
# 从系统库中查找依赖库
find_library( # Sets the name of the path variable.
# 设置依赖库的名字,下面链接库的时候会用到
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
# 查找log依赖库
# {sdk-path}/ndk-bundle/sysroot/usr/include/android/log.h
log)
# 配置库的依赖关系(链接关系)
target_link_libraries( # Specifies the target library.
# 目标库
test-lib
# Links the target library to the log library
# included in the NDK.
# 依赖库,可以是多个
${log-lib})
- 修改项目目录下gradle.build文件,在android{}和defaultConfig{}里面分别增加externalNativeBuild:
defaultConfig {
applicationId "anative.test.example.com.testnative"
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ''
abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
}
}
}
externalNativeBuild {
cmake {
path file('CMakeLists.txt')
}
}
- 在java代码中新增native接口
package anative.test.example.com.testnative.jni;
public class Test {
public native String getNativeString();
public native void showToast(Context context);
public native void getSignature(Context context);
}
- 这个时候这些native接口会报红,我们把鼠标放在函数上点击键盘alt+enter,选择create function xxx就会
在cpp文件中自动生成相对应的native方法(也有可能AS在main下创建一个jni文件夹然后在里面创建一个.c文件,可以把这些copy到自己创建的文件中可以把这个系统
创建的文件添加到CMakeLists中去):
#include <jni.h>
extern "C"
JNIEXPORT jstring JNICALL
Java_anative_test_example_com_testnative_cpp_Test_getNativeString(JNIEnv *env, jobject instance) {
return env->NewStringUTF(returnValue);
}
extern "C"
JNIEXPORT void JNICALL
Java_anative_test_example_com_testnative_cpp_Test_showToast(JNIEnv *env, jobject instance,
jobject context) {
extern "C"
JNIEXPORT jstring JNICALL
Java_anative_test_example_com_testnative_cpp_Test_getSignature(JNIEnv *env, jobject instance, jobject context) {
// TODO
return env->NewStringUTF(returnValue);
}
- 用C/C++实现这些函数(c和c++的实现有点区别,在这里我全部使用c++实现,所以我创建的jni文件以.cpp结尾)
#include <jni.h>
extern "C"
JNIEXPORT jstring JNICALL
Java_anative_test_example_com_testnative_cpp_Test_getNativeString(JNIEnv *env, jobject instance) {
return env->NewStringUTF("this is from jni");
}
extern "C"
JNIEXPORT void JNICALL
Java_anative_test_example_com_testnative_cpp_Test_showToast(JNIEnv *env, jobject instance,
jobject context) {
jclass toastClass = env->FindClass("android/widget/Toast");
//获取Toast.LENGTH_SHORT静态对象
jfieldID toastLengthId = env->GetStaticFieldID(toastClass, "LENGTH_SHORT", "I");
jint toastLength = env->GetStaticIntField(toastClass, toastLengthId);
//调用makeText返回toast对象
jmethodID makeTextId = env->GetStaticMethodID(toastClass, "makeText",
"(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;");
jstring message = env->NewStringUTF("use jni to show toast");
jobject makeText = env->CallStaticObjectMethod(toastClass, makeTextId, context, message,
toastLength);
//调用toast.show()方法
jclass showClass = env->GetObjectClass(makeText);
jmethodID showId = env->GetMethodID(showClass, "show", "()V");
env->CallVoidMethod(makeText, showId);
}
extern "C"
JNIEXPORT jstring JNICALL
Java_anative_test_example_com_testnative_cpp_Test_getSignature(JNIEnv *env, jobject instance,
jobject context) {
jclass cContext = env->GetObjectClass(context);
//获取context.getPackageName方法id
jmethodID packageNameId = env->GetMethodID(cContext, "getPackageName", "()Ljava/lang/String;");
//获取packageName
jstring packageName = static_cast<jstring>(env->CallObjectMethod(context, packageNameId));
//获取PackageManager.GET_SIGNATURES
jclass cPackageManager = env->FindClass("android/content/pm/PackageManager");
jfieldID getSignaturesId = env->GetStaticFieldID(cPackageManager, "GET_SIGNATURES", "I");
jint getSignatures = env->GetStaticIntField(cPackageManager, getSignaturesId);
//调用getPackageManager方法获取PackageManager对象
jmethodID getPackageManagerId = env->GetMethodID(
cContext, "getPackageManager", "()Landroid/content/pm/PackageManager;");
jobject packageManager = env->CallObjectMethod(context, getPackageManagerId);
//调用getPackageInfo方法返回packageInfo对象
jmethodID getPackageInfoId = env->GetMethodID(
env->GetObjectClass(packageManager), "getPackageInfo",
"(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
jobject packageInfo = env->CallObjectMethod(packageManager, getPackageInfoId, packageName,
getSignatures);
//以上就完成获取PackageInfo对象的获取,PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
//获取signatures成员
jfieldID signaturesId = env->GetFieldID(
env->GetObjectClass(packageInfo), "signatures", "[Landroid/content/pm/Signature;");
//从包信息获得签名数组 Signature[] signatures = packageInfo.signatures;
jobjectArray singnatures = (jobjectArray) env->GetObjectField(packageInfo, signaturesId);
//得到应用签名signatures[0]
jobject signature = env->GetObjectArrayElement(singnatures, 0);
//将signature对象转化为string
jmethodID toCharStringId = env->GetMethodID(
env->GetObjectClass(signature), "toCharsString", "()Ljava/lang/String;");
jstring result = (jstring) env->CallObjectMethod(signature, toCharStringId);
return result;
}
- 然后build工程,我们在build/intermediates/cmake/obj下面看到我们生成的.so库了
- 在代码里面加载我们的so库之后就可以使用这些native方法了
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
System.loadLibrary("test-lib");
final Test test = new Test();
findViewById(R.id.test1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("test", test.getNativeString());
}
});
findViewById(R.id.test2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
test.showToast(MainActivity.this);
}
});
findViewById(R.id.test3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("test", test.getSignature(MainActivity.this));
}
});
}
}
- 点击运行就ok(AS会自动把.so库放到apk里面,当然我们也可以把.so库拿出来手动放到工程里面)
网友评论