1 创建一个java类,定义native方法
public static native boolean login(String name,String pw);
2 Build–>make project编译java代码
编译后,会在module目录下的生成.class文件,目录如图一
注意: .class文件的存放地址根据自己build–intermediates的具体情况而定
3 生成class文件后,编译生成.h头文件
打开Terminal命令窗口
进入到main目录下
输入如下命令
D:\dayuproject\ITCustom\app\src\main>javah -d jni -classpath D:\dayuproject\ITCustom\app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes tao.deepbaytech.com.itcustom.jni.LoginTest
注意 classes 后面有空格,即classes之前都是路径,需用\连接,而classes之后,从包名开始,使用.连接。
输入此命令后,按回车,运行命令后,就会在main目录下生成jni文件夹,并且,文件夹下会生成.h头文件
4 将jni文件剪切放到根目录下
5 在jni目录下,新建Android.mk文件和Application.mk文件用于配置本地方法
Android.mk
#LOCAL_PATH 获取当前路径一般都是call my-dir
LOCAL_PATH:=$(call my-dir)
#清除上次编译获取的编译环境变量会保存LOCAL_PATH
include $(CLEAR_VARS)
#libtestjni.so
#指定编译生成的模块名字叫什么 生成的.so名字回家上lib前缀和.so后缀
LOCAL_MODULE := loginjni
#指定要编译的.c源文件叫什么名字
LOCAL_SRC_FILES := test.cpp
LOCAL_LDLIBS := -llog
#生成动态链接库 .so文件
include $(BUILD_SHARED_LIBRARY)
Application.mk
APP_ABI := armeabi
#它表示CPU的构架平台类型.目前市场上常见的构架平台有armeabi,x86和mips,其中在移动设备中占据主要地位的是armeabi,这也是大部分apk中只包含armeabi类型的so库的原因,如果不配置此文件,就会生成arm64-V8a armeabi-v7a x86 x86_64
6 在build.gradle中配置externalnativeBuild
externalNativeBuild {
ndkBuild {
path '../jni/Android.mk'
}
}
####注意 externalNativeBuild{}对应的是ExternalNativeBuild
ExternalNativeBuild的属性:
1.cmake:CMake工具编译选项。
2.ndkBuild:ndk-build选项。
在externalNativeBuild{}中有2个模块,cmake{}和ndkBuild{}模块
------------------------------------------------
cmake{}对应的是CmakeOptions
CmakeOption的属性:
1.path:你的CmakeLists.txt编译脚本的相对路径。
--------------------------------------------------
ndkBuild{}对应的是NdkBuildOptions
NdkBuildOptions的属性:
1.path:你的Android.mk文件的相对路径。
--------------------------------------------------
externalNativeBuild{}的用法:
externalNativeBuild{
ndkBuild{
path file("src\\main\\jni\\Android.mk")
}
cmake {
path "src/main/cpp/CMakeLists.txt"
}
}
7 配置好后,利用Build-make project编译,就会生成.so文件
8 在module的main目录下创建jniLibs文件夹,将生成的.so文件拷贝到该文件夹下
因为在AS中,默认识别so文件的目录即为jniLibs,所以需要将so文件放置在此目录下,如果想要使用其他目录,可以按照如下方式修改App的build.gradle文件,其中jniLibs.srcDirs选项指定了新的存放so库的目录
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
因为在eclipse中,so文件是放在libs目录下的,所以,一般情况下,在build.gradle中配置此方法,也有去重的作用
9 在java代码中加载编译后生成的so类库,调用本地方法
static {
System.loadLibrary("loginjni");
}
全部代码如下
public class LoginTest {
private static LoginTest mLoginTest;
private LoginTest(){}
public static LoginTest getInstance(){
if (mLoginTest==null){
synchronized (LoginTest.class){
if (mLoginTest==null){
mLoginTest=new LoginTest();
}
}
}
return mLoginTest;
}
public static native boolean login(String name,String pw);
static {
System.loadLibrary("loginjni");
}
}
10 根据生成的.h文件,在jni目录中创建一个c文件,定义一个函数实现本地方法,函数名必须使用本地方法的全类名,点改为下划线
##.h文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class tao_deepbaytech_com_itcustom_jni_LoginTest */
#ifndef _Included_tao_deepbaytech_com_itcustom_jni_LoginTest
#define _Included_tao_deepbaytech_com_itcustom_jni_LoginTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: tao_deepbaytech_com_itcustom_jni_LoginTest
* Method: login
* Signature: (Ljava/lang/String;Ljava/lang/String;)Z
*/
JNIEXPORT jboolean JNICALL Java_tao_deepbaytech_com_itcustom_jni_LoginTest_login
(JNIEnv *, jobject, jstring, jstring);
#ifdef __cplusplus
}
#endif
#endif
##对应的c文件
#include
#include
#ifdef __cplusplus
extern "C" {
#endif
//java程序还是应用的入口 在C中只是实现一些函数 等待着java去调用
//native函数命名规则 Java_包名_类名(包含native方法的java类)_native方法名
//JNIEnv* env
//JNIEnv 是结构体 JNINativeInterface的一级指针
//env是JNIEnv的一级指针 所以env就是结构体JNINativeInterface的二级指针
//JNINativeInterface 这个结构体中声明了大量的函数指针 这些函数指针在Jni开发中做用非常重要
//jobject thiz 哪个java对象通过jni调用到这个方法 这个jobject 就是这个java对象
//对于当前的案例来说 这个jobject就是MainActivity
//jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
// jobject thiz )
//JNIEnv* env,jobject thiz 这两个参数是必须传入的
JNIEXPORT jboolean JNICALL Java_tao_deepbaytech_com_itcustom_jni_LoginTest_login
(JNIEnv * env, jobject thiz, jstring name, jstring pw){
return 0;
}
}
调用
public void send(View view) {
boolean login = LoginTest.getInstance().login(mName.getText().toString(), mPw.getText().toString());
if (login){
startActivity(new Intent(JniActivity.this,LoginActivity.class));
}else {
Toast.makeText(getApplicationContext(), "用户名或密码错误", Toast.LENGTH_SHORT).show();
}
}