持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情
文章目录
4、新建com_ndk_test_JniClient.c文件完成头文件中函数的实现
5、在MainActivity.java中完成对JniClient.java中函数的调用
上篇文章我整理了一下以前的知识,主要理清了NDK环境安装搭建、以及内部samples的调用,想了解可参考:Android开发之JNI 和 NDK编程(一)内部samples的调用 而这篇文章将简单了解一下我们平时在Android开发中所调用的so库是怎么生成的?
实践成功,项目地址::D:\yyh_installSoft\androidTools\NDK_Info\code\TestNdk
工作场景
正在工作时,突然被同事问到:“秘钥放在哪里比较安全,X总说我这样写不行。”
其实很多时候,app都需要用到一些敏感的数据,例如加密的秘钥、一些渠道的APP_ID等,但如果被别人反编译拿到这些数据,就很有可能会造成损失。
那怎么增加被获取的难度?就是将这些数据放到C/C++的文件内,然后将其生成为so库,交给app应用层去调用。因为C/C++反汇编的难度,怎么都比反编译一个apk要难,这样就能更加有效地保证敏感数据的安全了。
下面我们将介绍so库生成的过程:
****1、 新建一个android项目工程TestNdk
在TestNdk工程中新建一个JniClient.java(为了调用C/C++代码),其内容如下:
package com.ndk.test;
public class JniClient {
static public native String AddStr(String strA, String strB);
static public native int AddInt(int a, int b);
}
****2、 生成 .h 的c++头文件
如下图:选中eclipse项目中的java类,点击方框按钮即可运行javah命令
最后会在项目目录下,生成一个jni文件夹,里面生成了 .h 的c++头文件(需要在当前工程中刷新一下才能看到)。
com_ndk_test_JniClient.h的文件内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_ndk_test_JniClient */
#ifndef _Included_com_ndk_test_JniClient
#define _Included_com_ndk_test_JniClient
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_ndk_test_JniClient
* Method: AddStr
* Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_ndk_test_JniClient_AddStr
(JNIEnv *, jclass, jstring, jstring);
/*
* Class: com_ndk_test_JniClient
* Method: AddInt
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_ndk_test_JniClient_AddInt
(JNIEnv *, jclass, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
有关javah命令的配置可参见: 在eclipse中配置javah命令生成 .h 的c++头文件
3、在jni目录下新建一个Android.mk文件
其内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := TestNdk
LOCAL_SRC_FILES := com_ndk_test_JniClient.c
include $(BUILD_SHARED_LIBRARY)
4、在jni目录下新建com_ndk_test_JniClient.c文件完成头文件中函数的实现
其内容如下:
#include "com_ndk_test_JniClient.h"
#include <stdlib.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C"
{
#endif
/*
* Class: com_ndk_test_JniClient
* Method: AddStr
* Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_ndk_test_JniClient_AddStr
(JNIEnv *env, jclass arg, jstring instringA, jstring instringB)
{
jstring str = (*env)->NewStringUTF(env, "HelloWorld from JNI !");
return str;
}
/*
* Class: com_ndk_test_JniClient
* Method: AddInt
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_ndk_test_JniClient_AddInt
(JNIEnv *env, jclass arg, jint a, jint b)
{
return a + b;
}
#ifdef __cplusplus
}
#endif
当编辑com_ndk_test_JniClient.c并保存后,刷新一下(刷新没有,就选中project然后clean一下工程试试),就可以看到TestNkd工程下的obj/local/armeabi目录下将自动生成libTestNdk.so库、libs目录下也会自动生成一个libTestNdk.so库,如下图:
5、在MainActivity.java中完成对JniClient.java中函数的调用
package com.ndk.test;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends Activity {
static {
System.loadLibrary("TestNdk");
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.main);
//第一个方法传入的两个参数没有做操作,直接返回hello jni,不用管
String str = JniClient.AddStr("test", "test");
//第二个方法暂时不实现
// int iSum = JniClient.AddInt(5, 2);
// String strSum = "5 + 7 = " + iSum;
TextView tv1 = new TextView(this);
tv1.setText(str);
setContentView(tv1);
}
}
6、运行TestNdk工程
在手机界面中可以看到显示来自com_ndk_test_JniClient.c 文件中的“HelloWorld from JNI ! "了。
有关使用so库
一般的情况下,都是自己的项目,那使用的做法就很简单,将所有架构的so文件,复制到libs文件夹中,然后在你需要调用方法的地方直接调用就行了。
但是如果是需要提供给别人用的,那就不能这样了。因为别人复制了你的so文件之余,还需要新建工具类的全路径,一模一样的那种,再复制工具类(MyJavaJNI.java)进去,才能正常使用。
这时候我们应该将工具类打包成aar或者jar文件,一起提供给别人使用,这样别人就不用那么麻烦了。