android 手动配置地址环境,Android Studio 2.3.3 图解配置NDK开发环境以及Hello Word To jni...

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

前言

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

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程序当中的一部分代码对效率要求非常高,如算法计算,图形渲染等。

总之,只有当你必须在同一进程中调用本地代码时,再使用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)的强大的跨平台构建环境的需求。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细节。

主要优点包括:C,C ++,Objective C的最新语言支持 ;

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

支持时使用JIT表达式;

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

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

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

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

配置NDK俩种方式

手动下载NDK

NDK下载地址如下:

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

下载完成后,解压本地目录,在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;

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

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

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

配置下环境变量瞅瞅呗~

配置环境变量

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

cmd直接输入ndk-build回车

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

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

搞得LZ都快放弃了,最后一试,好了,给我郁闷的,大家注意啊~

创建个项目玩一玩

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

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

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

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之后,我们稍等片刻~

查看默认生成例子

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

从上图可看到,和以前唯一不同的就是多出了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 #include 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++ 。一起来验证一下呗~

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

经过查询,发现一个之前从未关注的内容,那就是从编译到运行示例 APP 的流程到底是怎样呢,如下:Gradle 调用外部构建脚本,也就是 CMakeLists.txt;

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

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

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//,你就可以看见 libnative-lib.so

仿造demo来一下,首先布局中新增一个TextView,activity中编写一个native方法,调用native方法并输出结果,如下:TextView tv1 = (TextView) findViewById(R.id.sample_text_1);

tv1.setText(hlqFromJNI());public native String hlqFromJNI();

在cpp中这么玩下:#include #include 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:如果您构建的是调试版本的应用(即专用于测试和分析的应用),打包器会使用调试密钥库签署您的应用。Android Studio 自动使用调试密钥库配置新项目;

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

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

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

参考文献

结束语

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值