您的位置:首页 > 移动开发 > Android开发

Android基础之Jni开发流程详解(雷惊风)

2017-06-13 15:26 447 查看
   在Android开发中难免会用到Jni开发,首先这是什么东西呢,不由自主的就像搬英文,Java native Interface-java本地化接口,这个东西本来是java的一部分,我们都知道Android上层都是Java写的,内核采用的Linux内核,而Linux内核中绝大多数代码都是用C写的,Native framework层代码是用C++实现的,所以如果我们想要使用framework层C++代码实现的功能的话,就得借助Jni来实现了。

   在Android中个人感觉可以分为两种Jni,一种是系统Jni,一种是我们自己写的Jni,那么有人会问了,系统的Jni是什么时候注册的呢?这里我大致跟大家说一下,Android系统启动过程中会首先启动Linux Kernel,Linux Kernel会创建系统的第一个进程---Init进程,然后由Init进程fork出Zygote进程,对于这个Zygote进程,大家应该了解,他是第一个贯穿于Java层与C/C++层的进程,Zygote在启动过程中会调用AndroidRuntime.cpp中的startVm方法创建虚拟机,然后调用startReg方法注册系统Jni。详细的就不多说了,AndroidRuntime.cpp文件路径为:frameworks\base\core\jni,在jni文件路径下有很多系统级别的C++文件,可以看一下:



下边说一下我们如何去实现一个自己的Jni,首先我们需要下载安装NDK,进行配置。这个步骤就不说了,可以百度一下。下面开始进入主题。

首先我们新建一个项目,检查NDK是否配置正确,在项目上右击-->Open Module Settings,如下图:





查看是否为你ndk所在路径。

 新建.java文件添加native方法,如下代码:

public class MyNdkDemo {
public native String getNdkString();
}

 在Android studio下部工具栏选择命令行工具Terminal,他会自动将路径指定到我们的工程下,如下:



然后cd到我们java路径下,如直接cd app\src\main\java,如下图所示:



 执行javah命令生成MyNdkDemo对应的.h头文件,命令为:javah -encoding utf-8 com.jason.jasonndkdemo.MyNdkDemo,这里我一开始没有添加-encoding utf-8 出现了编译错误,如下:

D:\lylsoft\android\androidstudio\mydemo\JasonNdkDemo\app\src\main\java>javah com.jason.jasonndkdemo.MyNdkDemo

错误: 编码GBK的不可映射字符

所以如果你在设置过程中遇到了同样的问题,可以尝试制定一下编码格式。

没问题的话执行完javah命令后会在我们项目中生成一个MyNdkDemo文件对应的头文件,它的命名规则为包名+类名,每一层都是“_”分割的,如下:



在项目的app路径上右键创建一个Jni Folder,如下图:



将我们的头文件move到jni文件夹,如下:



 同文件夹下创建对应的.cpp文件,命名与.h文件相同,只是一个是.h一个是.cpp的,看一下:



看一下我们的.h头文件类内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_jason_jasonndkdemo_MyNdkDemo */

#ifndef _Included_com_jason_jasonndkdemo_MyNdkDemo
#define _Included_com_jason_jasonndkdemo_MyNdkDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     com_jason_jasonndkdemo_MyNdkDemo
* Method:    getNdkString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_jason_jasonndkdemo_MyNdkDemo_getNdkString
(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

你会发现在它的内部有一个类似Java中抽象方法的东西,就是这个Java_com_jason_jasonndkdemo_MyNdkDemo_getNdkString(JNIEnv *, jobject);这个方法也就是我们在MyNdkDemo.java中定义的那个native方法对应在头文件中的方法,它的命名规则为Java+包名+类名+方法名,中间都是“_”。然后我们将方法拷贝到我们实现类.cpp文件中,进行实现,添加#include 如下代码:

//
// Created by jason on 2017/6/13.
//
#include "com_jason_jasonndkdemo_MyNdkDemo.h"
JNIEXPORT jstring JNICALL Java_com_jason_jasonndkdemo_MyNdkDemo_getNdkString
(JNIEnv *env, jobject obj){
return env->NewStringUTF("成功");
}

配置项目的gradle.properties,添加如下配置:

#允许我们使用已经过时的NDK版本
android.useDeprecatedNdk=true

在app下的build.gradle中的defaultConfig下添加关于NDK的配置,如下:

ndk {
moduleName = "myndkDemo"
ldLibs "log", "z", "m"
abiFilters("armeabi", "armeabi-v7a", "x86")
//moduleName是随便写的,与将来在Java类中使用System.loadLobrary(“本地库名称”);以及生成的.so文件名称对应;
//ldLibs是要用到的jni库,一般由google提供,比如上边引入的log库可以让我们在C代码中使用LogCat日志;
//abiFilters指的是我们要生成哪些平台的so文件,这里生成arm平台和x86平台;
}

配置好后Build->Make Project,如下:



如果没有问题的话,会在app\build\intermediates\ndk\debug\lib目录下生成对应的.so文件,命名规则为lib+我们在build.grable中配置的名称,如下图:



将我们生成的三个基于不同内核框架的.so动态库拷贝到我们项目的libs下,便完成了我们使用前的生成流程,如下图:



接下来我们便可以在我们的Activity中通过静态代码块加载我们生成的.so动态库了看如下代码:

public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("MyNdkDemo");
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

public void click(View view) {
MyNdkDemo myNdkDemo = new MyNdkDemo();
Toast.makeText(this, myNdkDemo.getNdkString(), Toast.LENGTH_SHORT).show();
}
}

看System.loadLibrary()方法实现源码,他最终调用到了Runtime中的nativeLoad(name, loader, librarySearchPath)方法实现的方法注册,最终我们调用myNdkDemo.getNdkString()方法实现从java层调用底层C++的代码,看一下最终的效果吧,如下:



看看,完美调用了我们定义在.cpp文件中的方法,本篇文章中我只是简单的实现了一下我们如何自己在java文件中定义native方法,并在C++中实现,生成对应的.so动态库,最终成功调用。后续抽时间补充一篇关于Android上Jni实现原理分析的文章。本篇文章就写到这里,我已将demo上传至网络,点击下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: