继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Android Studio 2.3.3 图解配置NDK开发环境以及Hello Word To jni

繁华开满天机
关注TA
已关注
手记 134
粉丝 15
获赞 68

LZ-Says:半夜睡觉滚了地上了,无奈之下醒来了,想想最近几天因为一个括号导致JNI迟迟不能开展,心里面无奈又崩溃,索性直接起来整完得了~

前言

当前毕业的时候,感觉自己掌握了全世界,随着参加工作的时间一天天的增长,突然觉得丫的,啥都不会啊,要学的东西还是太多太多了。不过近来被飞大姐洗脑成功,<font color=#FF0000>万事不过几行代码而已,干它~

So 今天为大家带来简单的jni配置,使用,以及运行我们的第一个简单小demo`

Hi Jni

总是再说jni,jni,那么jni到底是什么东西,我们一起来看看:

JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。

JNI标准至少要保证本地代码能工作在任何Java 虚拟机环境。

可以看到Android上层的Application和ApplicationFramework都是使用Java编写,底层包括系统和使用众多的Libraries都是C/C++编写的,所以上层Java要调用底层的C/C++函数库必须通过Java的JNI来实现。

Jni使用场景

当你开始着手准备一个使用JNI的项目时,请确认是否还有替代方案。应用程序使用JNI会带来一些副作用。下面给出几个方案,可以避免使用JNI的时候,达到与本地代码进行交互的效果:

1、JAVA程序和本地程序使用TCP/IP或者IPC进行交互。

2、当用JAVA程序连接本地数据库时,使用JDBC提供的API。
3、JAVA程序可以使用分布式对象技术,如JAVA IDL API。

这些方案的共同点是,JAVA和C处于不同的线程,或者不同的机器上。这样,当本地程序崩溃时,不会影响到JAVA程序。

下面这些场合中,同一进程内JNI的使用无法避免:

1、程序当中用到了JAVA API不提供的特殊系统环境才会有的特征。而跨进程操作又不现实。

2、你可能想访问一些己有的本地库,但又不想付出跨进程调用时的代价,如效率,内存,数据传递方面。
3、JAVA程序当中的一部分代码对效率要求非常高,如算法计算,图形渲染等。

<font color=#FF0000>总之,只有当你必须在同一进程中调用本地代码时,再使用JNI。

Jni缺陷

一旦使用JNI,JAVA程序就丧失了JAVA平台的两个优点:

1、程序不再跨平台。要想跨平台,必须在不同的系统环境下重新编译本地语言部分。

2、程序不再是绝对安全的,本地代码的不当使用可能导致整个程序崩溃。一个通用规则是,你应该让本地方法集中在少数几个类当中。这样就降低了JAVA和C之间的耦合性

Jni作用

JNI可以这样与本地程序进行交互:

1、你可以使用JNI来实现“本地方法”(native methods),并在JAVA程序中调用它们。

2、JNI支持一个“调用接口”(invocation interface),它允许你把一个JVM嵌入到本地程序中。本地程序可以链接一个实现了JVM的本地库,然后使用“调用接口”执行JAVA语言编写的软件模块。

简单了解以上内容后,我们开启正题,在Android开发中,我们该怎么使用jni,或者说是在Android Studio中,我们该怎么使用jni呢?表急,往下瞅瞅~

话说,我们开发android应用程序基础不就是下载官方相关的SDK,ADT啥啥啥的,同理jni也一样。

现在为大家简单介绍NDK~如下。

Hi NDK

Android NDK 是一套允许使用原生代码语言(例如 C 和 C++)实现部分应用的工具集。在开发某些类型应用时,这有助于重复使用以这些语言编写的代码库。

同理,官方也为我们提供了一个小例子,简单走马观花看一下:

public class MyActivity extends Activity {  /**
  * 使用 C/C++ 语言实现的原生方法
  */
  public native void computeFoo();
}

native方法,当年看到这个东西感觉好高大上,没想到而今我也能玩玩,哈哈,GGG~

官方对于NDK是这样说的:

NDK 不适用于大多数初学的 Android 编程者,对许多类型的 Android 应用没什么价值。 因为它不可避免地会增加开发过程的复杂性,所以通常不值得使用。 但如果您需要执行以下操作,它可能很有用:

  • 从设备获取卓越性能以用于计算密集型应用,例如游戏或物理模拟;

  • 重复使用您自己或其他开发者的 C 或 C++ 库。

丫的,爷儿们好奇瞅瞅不行啊?

行~!

哈哈~

简单有个印象后,我们开始配置相关内容,为什么要说这个配置呢,主要有以下几个原因:

  • 虽说配置是傻瓜式无脑操作,但是对于LZ小白这样的人来说,依然觉得是一件很高大上的事儿,何况,丫的,连配置都不会,还开发个卵子?

  • 凡事儿亲历亲为,不经历,怎能有成长?

配置之前,我们还需要了解我们需要配置or下载哪儿些工具,以及这些东西都是干嘛的,不然稀里糊涂的,糟心。

同理,我们也需要去简单了解下使用NDK好处:

1、代码的保护。由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大;
2、可以方便地使用现存的开源库。大部分现存的开源库都是用C/C++代码编写的;
3、提高程序的执行效率。将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率;
4、便于移植。用C/C++写得库可以方便在其他的嵌入式平台上再次使用。

NDK以及所需构建工具简介

  • NDK:这个还需要再说嘛?

  • CMake:外部构建工具;

CMake是一个开源的跨平台系列工具,旨在构建,测试和打包软件。

CMake用于使用简单的平台和编译器独立的配置文件来控制软件编译过程,并生成可以在选择的编译环境中使用的本地makefile和工作空间。

CMack工具套件由Kitware创建,以响应对开源项目(如ITK和VTK)的强大的跨平台构建环境的需求。

官方地址:https://cmake.org/ 有兴趣可以简单了解下~

  • LLDB:Android Studio 上面调试本地代码的工具

LLDB是下一代高性能调试器。它被构建为一组可重用的组件,可以高度利用较大的LLVM项目中的现有库,例如Clang表达式解析器和LLVM反汇编程序。

在Mac OS X中,LLDB是Xcode中的默认调试器,支持在桌面和iOS设备和模拟器上调试C,Objective-C和C ++。

LLDB项目中的所有代码都可以使用标准的 LLVM许可证(一种开放源代码“BSD风格”)许可证。

LLDB目前将调试信息转换成clang类型,以便它可以利用clang编译器基础架构。这允许LLDB在表达式中支持最新的C,C++,Objective C和Objective C ++语言特性和运行时间,而无需重新实现任何此功能。在编写表达式的函数,拆卸指令和提取指令详细信息等时,还可以利用编译器来处理所有ABI细节。

主要优点包括:

  1. C,C ++,Objective C的最新语言支持 ;

  2. 可以声明局部变量和类型的多行表达式;

  3. 支持时使用JIT表达式;

  4. 当JIT不能使用时,评估表达式中间表示(IR)

官方地址:http://lldb.llvm.org/

以上简单了解下就好了,至于为啥要这么搞,就是为了方便以后有需要直接翻出来看看~

NDK配置(包含构建工具,调试工具)

<font color=#FF0000>下载安装NDK,有俩种方式,其实都一样,只是一个需要手动下载,解压,配置目录,一个Android Studio自动完成以上操作。

配置NDK俩种方式

手动下载NDK

NDK下载地址如下:

https://developer.android.google.cn/ndk/downloads/index.html

大家可自行选择相应版本进行下载。

下载完成后,解压本地目录,在Android Studio中配置路径即可

强大的Android Studio走起~

1.点击Project Structure,选择 Download Android NDK;

2.耐心等待吧,LZ下载比较快,解压比较慢~

3.下载解压完成,自动录入地址,省事儿哈~

到此,关于NDK下载安装俩种方式图解完毕~

我们看一下ndk目录各个作用,简单了解下。

  • docs: 帮助文档

  • build/tools:linux的批处理文件

  • platforms:编译c代码需要使用的头文件和类库

  • prebuilt:预编译使用的二进制可执行文件

  • sample:jni的使用例子

  • source:ndk的源码

  • toolchains:工具链

  • ndk-build.cmd:编译打包c代码的一个指令,需要配置系统环境变量

配置构建工具以及调试工具

1.点击SDK Manager,选择下载安装CMake以及LLDB;

<center>

简单看一下版本信息,果断OK~

<center>

没啥可说的,等待,不过分分钟搞定~

<center>

到此,基本配置已完成,但是如何查验NDK是否成功安装了呢?

配置下环境变量瞅瞅呗~

配置环境变量

复制NDK地址,按如下图示进行操作即可。
<center>

<center>

cmd直接输入ndk-build回车

<center>

在这里吐槽下LZ之前遇到的坑。

LZ目录习惯命名为HLQWorkSofe(Android) or HLQWorkSofe(Java),在之前从来没有出现过问题,但是在NDK时候,却怎么也不行,最后无奈下只能把()去掉。


<font color=#FF0000>搞得LZ都快放弃了,最后一试,好了,给我郁闷的,大家注意啊~

创建个项目玩一玩

创建项目时,记得勾选下面的Include C++ support哦~

导入C++支持库,不然没法玩哈~
<center>

一路next下之后,关于最后一步选择时,在此作特殊说明下。

<center

  • C++ Standard:使用下拉列表选择您希望使用哪种 C++ 标准。选择 Toolchain Default 会使用默认的 CMake 设置;

  • Exceptions Support:如果您希望启用对 C++ 异常处理的支持,请选中此复选框。如果启用此复选框,Android Studio 会将 -fexceptions 标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake;

  • Runtime Type Information Support:如果您希望支持 RTTI,请选中此复选框。如果启用此复选框,Android Studio 会将 -frtti 标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake。

点击Finish之后,我们稍等片刻~

查看默认生成例子

<center

下面我们一起来探究下官方提供的例子,看看从例子中我们能得知什么对我们有用的信息。

从上图可看到,和以前唯一不同的就是多出了cpp目录以及External Build Files俩个目录,那么这俩个都有什么用呢?一起来看看。

  • cpp 目录存放所有 native code 的地方,包括源码,头文件,预编译项目等。对于新项目,Android Studio 创建了一个 C++ 模板文件:native-lib.cpp,并且将该文件放到了你的 app 模块的 src/main/cpp/ 目录下。这份模板代码提供了一个简答的 C++ 函数:stringFromJNI(),该函数返回一个字符串:”Hello from C++”

  • External Build Files 目录是存放 CMake 或 ndk-build 构建脚本的地方。有点类似于 build.gradle 文件告诉 Gradle 如何编译你的 APP 一样,CMake 和 ndk-build 也需要一个脚本来告知如何编译你的 native library。对于一个新的项目,Android Studio 创建了一个 CMake 脚本:CMakeLists.txt,并且将其放到了你的 module 的根目录下

首先还是来看看代码吧,比较直观也比较好理解。

1.查看activity代码

package cn.hlq.hlqjnipro;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.widget.TextView;public class MainActivity extends AppCompatActivity {    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }    @Override
    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
    }    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();
}

首先static静态块去加载so库,本地编写native方法,调用方法输出内容。

2.查看native-lib.cpp代码

#include <jni.h>#include <string>extern "C"JNIEXPORT jstring JNICALLJava_cn_hlq_hlqjnipro_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {    std::string hello = "Hello from C++";    return env->NewStringUTF(hello.c_str());
}

简单可以看到,首先定义hello变量,之后return。

3.查看CMakeLists.txt

简单的翻译了下,如有不正之处欢迎指出`

# 使用Android Studio使用CMake的更多信息,阅读文档:https://d.android.com/studio/projects/add-native-code.html# 设置CMake的最低版本构建本机所需库cmake_minimum_required(VERSION 3.4.1)# 创建和名称库,使它是静态的或共享,并提供了相对路径的源代码# 可以定义多个图书馆,CMake将为你构建这些内容# Gradle自动与你的APK包共享库add_library( # 设置库名称
             native-lib             # 集库作为一个共享库
             SHARED             # 提供了一个相对路径你的源文件
             src/main/cpp/native-lib.cpp )# 搜索指定预先构建的库和存储路径变量。因为CMake包括系统库搜索路径中默认情况下,只需要指定想添加公共NDK库的名称,在CMake验证库之前存在完成构建find_library( # 设置path变量的名称
              log-lib              # 在CMake定位前指定的NDK库名称
              log )# 指定库CMake应该链接到目标库中,可以链接多个库,比如定义库,构建脚本,预先构建的第三方库或者系统库target_link_libraries( # 指定目标库
                       native-lib                       # 目标库到日志库的链接 包含在NDK
                       ${log-lib} )

一开始还觉的差不多点呢,结果看到这里越来越蒙圈,还是先运行一下,看看结果吧。

按照刚才的简单分析,这个demo输出的结果应该为:Hello from C++ 。一起来验证一下呗~

<center

嗯,确实,输出了,有些似懂非懂的,继续研究~

经过查询,发现一个之前从未关注的内容,那就是从编译到运行示例 APP 的流程到底是怎样呢,如下:

  1. Gradle 调用外部构建脚本,也就是 CMakeLists.txt;

  2. CMake 会根据构建脚本的指令去编译一个 C++ 源文件,也就是 native-lib.cpp,并将编译后的产物扔进共享对象库中,并将其命名为 libnative-lib.so,然后 Gradle 将其打包到 APK 中;

  3. 在运行期间,APP 的 MainActivity 会调用 System.loadLibrary() 方法,加载 native library。而这个库的原生函数,stringFromJNI(),就可以为 APP 所用了;

  4. MainActivity.onCreate() 方法会调用 stringFromJNI(),然后返回 “Hello from C++”,并更新 TextView 的显示;

注意:Instant Run 并不兼容使用了 native code 的项目。Android Studio 会自动禁止 Instant Run 功能。

如果你想验证一下 Gradle 是否将 native library 打包进了 APK,你可以使用 APK Analyzer:

  • 选择 Build > Analyze APK。

  • 从 app/build/outputs/apk/ 路径中选择 APK,并点击 OK。

  • 如下图,在 APK Analyzer 窗口中,选择 lib/<ABI>/,你就可以看见 libnative-lib.so

<center

转自:http://blog.csdn.net/wl9739/article/details/52607010

仿造demo来一下,首先布局中新增一个TextView,activity中编写一个native方法,调用native方法并输出结果,如下:

        TextView tv1 = (TextView) findViewById(R.id.sample_text_1);
        tv1.setText(hlqFromJNI());
    public native String hlqFromJNI();

在cpp中这么玩下:

#include <jni.h>#include <string>extern "C" {JNIEXPORT jstring JNICALLJava_cn_hlq_hlqjnipro_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {    std::string hello = "Hello from C++";    return env->NewStringUTF(hello.c_str());
}JNIEXPORT jstring JNICALLJava_cn_hlq_hlqjnipro_MainActivity_hlqFromJNI(
        JNIEnv *env,
        jobject /* this */) {    std::string hello = "真恶心啊 Fuck";    return env->NewStringUTF(hello.c_str());
}

}

大家有没有注意到Java_cn_hlq_hlqjnipro_MainActivity_hlqFromJNI这个名称,方法名必须为Java_包名全路径_方法名

一般情况下,Gradle 会将你的本地库构建成 .so 文件,然后将其打包到你的 APK 中。如果你想 Gradle 构建并打包某个特定的 ABI 。你可以在你的 module 层级的 build.gradle 文件中使用 ndk.abiFilters 标签来指定他们:

android {
  ...
  defaultConfig {
    ...
    externalNativeBuild {
      cmake {...}      // or ndkBuild {...}
    }

    ndk {      // Specifies the ABI configurations of your native
      // libraries Gradle should build and package with your APK.
      abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',                   'arm64-v8a'
    }
  }
  buildTypes {...}
  externalNativeBuild {...}
}

提供给别人使用

ReBuild Project之后,将生成好的so库提供有需要的人即可,不过要记得调用的时候包名要一致,不然会报一个找不到so的问题。

LZ简单写了一个测试demo,

比较恶心的就是包名这块,等以后再看看有没有好的解决办法吧。

拓展APP构建流程

查看官方文档中,突然发现有关构建流程内容,一起了解下,如下:

构建流程涉及许多将您的项目转换成 Android 应用软件包 (APK)的工具和流程。构建流程非常灵活,因此了解它的一些底层工作原理会很有帮助。

典型 Android 应用模块的构建流程通常依循下列步骤:

1.编译器将您的源代码转换成 DEX(Dalvik Executable) 文件(其中包括运行在 Android 设备上的字节码),将所有其他内容转换成已编译资源;

2.APK 打包器将 DEX 文件和已编译资源合并成单个 APK。不过,必须先签署 APK,才能将应用安装并部署到 Android 设备上;

3.APK 打包器使用调试或发布密钥库签署您的 APK:

  1. 如果您构建的是调试版本的应用(即专用于测试和分析的应用),打包器会使用调试密钥库签署您的应用。Android Studio 自动使用调试密钥库配置新项目;

  2. 如果您构建的是打算向外发布的发布版本应用,打包器会使用发布密钥库签署您的应用

4.在生成最终 APK 之前,打包器会使用 zipalign 工具对应用进行优化,减少其在设备上运行时的内存占用

构建流程结束时,您将获得可用来进行部署、测试的调试 APK,或者可用来发布给外部用户的发布 APK。

参考文献

  1. http://baike.baidu.com/link?url=QrA-HKewzfPKP5UejCXH8JhE4yQs5MhwG6EBYI8imO9k8zYIlM0h2DYffNSqdK8dG6LZLfT0dK5ocK2NEsOUJq ;

  2. http://blog.csdn.net/tongseng/article/details/53005123

  3. http://www.cnblogs.com/bastard/archive/2012/05/19/2508913.html

  4. http://blog.csdn.net/baidu_26352053/article/details/53931045

  5. https://developer.android.google.cn/ndk/guides/concepts.html

  6. https://developer.android.google.cn/studio/build/index.html;

  7. http://blog.csdn.net/wl9739/article/details/52607010

结束语

这篇文章写的够揪心,个人感觉乱糟糟的,实际学习的时候也是乱糟糟的,几乎都是靠着各种搜索,况且还有一些不靠谱的博文误导或者说是版本有些老。

望观看者海涵`有问题及时沟通。

在此祝阳阳越来越美,bus越来越6~
本次比赛主题帖的链接:http://www.apkbus.com/thread-276228-1-1.html

原文链接:http://www.apkbus.com/blog-904057-68132.html

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP