NDK学习笔记(一)
来源:互联网 发布:网络主播年收入排行榜 编辑:程序博客网 时间:2024/05/22 04:45
NDK学习笔记(一)
我们都知道Android应用是用Java写的,但是有时候我们需要使用C/C++来进行开发,或者说需要与底层进行一些交互。Java本身提供了JNI方法,但是在Android开发中其存在技术障碍。比如程序更加复杂,兼容性难以保障,无法访问Framework API,Debug难度更大等。于是于是NDK就应运而生了。NDK全称是Native Development Kit。NDK的发布,使“Java+C”的开发方式终于转正,成为官方支持的开发方式。NDK将是Android平台支持C开发的开端。
使用NDK的优点
- 便于移植,用C/C++写得库可以方便在其他的平台上再次使用。
- 代码的保护,由于java层代码很容易被反编译,而C/C++库反编译难度较大。
- 提高程序的执行效率,将要求高性能的应用逻辑使用C/C++开发,从而提高应用程序的执行效率。
- 访问现有开源库,需要访问底层的API或引用一些只有C/C++的库。
配置NDK环境
如果你已经下载了NDK,那么就可以设置其路径,在File->Project Structure里的Android NDK location,默认为你的SDK路径\ndk-bundle。如果没有下载,直接点击download下载。当然你也可以
自己下载其它版本放置任意位置,然后手动设置路径。
设置成功的话,会在Project的local.properties文件下添加NDK的路径:
ndk.dir=D\:\\Android\\Sdk\\ndk-bundle
如果没有,可手动添加。另在Build时,如果出现错误如下:
Error:(12, 0) Error: NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin. For details, see http://tools.android.com/tech-docs/new-build-system/gradle-experimental. Set "android.useDeprecatedNdk=true" in gradle.properties to continue using the current NDK integration.
是由于你的NDK版本与AS版本不一致造成的,只需在gradle.properties文件下添加一行:android.useDeprecatedNdk=true
即可。
(如果你想使用命令行的话,需要添加系统环境变量,新建变量名ANDROID_NDK_HOME,对应变量值为ndk根目录地址 ,即$SDKpath\ndk-bundle,然后将ANDROID_NDK_HOME添加到Path中去(%ANDROID_NDK_HOME%;),确定OK.)
自己编写Native文件
好了,现在我们可以来编写自己的第一个NDK应用了。最简单的例子,C输出一个字符串,然后在Android里调用显示出来。首先还是跟平常一样,New一个Project。接着New一个Java Class,我们将其命名为JniUtils。我们将在这里加载动态库,创建native方法。(其实也可以在MainActivity里做这些,但是为了让代码看起来更清晰,我们还是选择分开写。另外你也可以不在app Module里做这些,而是放在一个独立的module里。)
先创建一个native方法,即JNI接口,它是衔接C/C++与Java之间的桥梁。
public class JniUtils { static{ System.loadLibrary("ndk"); } public static native String getStringFromC();}
这个时候我们看到getStringFromC的颜色是红色的,那是因为没有找到这个方法的实现。接下来我们就要编写这个方法了。
如果默认是Android视图的话,调整至Project视图。在$ProjectName/app/src/main下,New一个Folder,类型为JNI Folder。这个目录下主要存放jni文件,即c/cpp文件以及头文件等。接下来创建C文件有两种方式,取决于你的gradle版本,我们分开说,比较一下:
稳定版插件
如果你的Project下的build.gradle类似这样:
dependencies { classpath 'com.android.tools.build:gradle:2.1.0' }
既是稳定版插件。
上一步完成后clean project 再rebuild project ,看看$ProjectName/app/build/intermediats/classes/是否存在,再打开Terminal输入指令:
cd app/build/intermediates/classes/debug
然后再输入指令:
javah -jni com.example.yaoobs.ndkjnidemo.JniUtils
发现classes/debug下多了一个com_example_yaoobs_ndkjnidemo_JniUtils.h。将其剪切至之前创建的JNI Folder下,New一个C/C++ Source File,命名为jniUtils.c。按照com_example_yaoobs_ndkjnidemo_JniUtils.h里面的方法名修改jniUtils.c。
实验性插件(Gradle Experimental Plugin)
Gradle Experimental Plugin基于Gradle的新特性开发,目的在于减少项目配置时间,并提供更好的NDK支持。
(注意,这只是一个插件,需要gradle本身的支持,并且与其版本对应。笔者的gradle版本是2.1.0,对应的gradle-experimental是 0.7.0。)
使用插件,我们需要先在项目的build.gradle里修改gradle的引用:
buildscript { ... dependencies {// classpath 'com.android.tools.build:gradle:2.1.0' classpath 'com.android.tools.build:gradle-experimental:0.7.0' }...}
接着修改module下的build.gradle,这里有一些语法上的不同,旧的注释掉了,可以对比着看:
//apply plugin: 'com.android.application'apply plugin: 'com.android.model.application'////android {// compileSdkVersion 23// buildToolsVersion "23.0.2"// defaultConfig {// applicationId "com.example.yaoobs.ndkjnidemo"// minSdkVersion 14// targetSdkVersion 23// versionCode 1// versionName "1.0"// ndk {// moduleName = "ndk"// abiFilters 'armeabi', 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a', 'mips', 'x86_64'// }// buildTypes {// release {// minifyEnabled false// proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'// }// }//}model { android { compileSdkVersion = 23 buildToolsVersion = "23.0.2" defaultConfig.with { applicationId = "com.example.yaoobs.ndkjnidemo" minSdkVersion.apiLevel = 14 targetSdkVersion.apiLevel = 23 versionCode = 1 versionName = "1.0" } ndk { moduleName = "ndk" toolchain = 'clang' //必加项,否则c文件报错 CFlags.addAll(['-Wall']) } buildTypes { release { minifyEnabled = false proguardFiles.add(file('proguard-rules.txt')) } } productFlavors { create("arm") { ndk.abiFilters.add("armeabi") } create("arm7") { ndk.abiFilters.add("armeabi-v7a") } create("arm8") { ndk.abiFilters.add("arm64-v8a") } create("x86") { ndk.abiFilters.add("x86") } create("x86-64") { ndk.abiFilters.add("x86_64") } create("mips") { ndk.abiFilters.add("mips") } create("mips-64") { ndk.abiFilters.add("mips64") } create("all") } }}dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha1' compile 'com.android.support:appcompat-v7:23.0.0'}
完成后你就会发现JniUtils.class里的native方法getStringFromC(),按Alt+Enter会出现提示,如下图:
回车,会发现JNI Folder下创建了一个jniutils.c文件。
编写Native文件
首先,添加头文件,类似java中的导包,这里首先必加的是jni.h,这是java与c/c++之间语言转换的核心文件,具体可以查看ndk目录下D:\android-sdk\ndk-bundle\platforms\android-23\arch-arm\usr\include\jni.h,另一个这里需要处理字符串,所以还需要包含string.h的头文件,同样可以在上面目录include中找到.
然后,添加java中native方法的实现。jstring:返回值类型,Java_com_example_yaoobs_HelloJni_stringFromJNI(JNIEnv *env, jobject jobj):实现的方法名,固定格式,Java_所要实现的方法名所在java类用下划线替代点的引用地址_方法名(JNI环境变量 env,JNI环境对象 jobj);其中env和jobj方法中可能用不到,但也必须申明,源码中是这样说明的,大概作用就是env作为了一个JNINativeInterface指针,是java与c/c++之间的一个功能环境变量中间桥梁,我们先不深入。
完整的jniutils.c:
#include <string.h>#include <jni.h>jstringJava_com_example_yaoobs_ndkjnidemo_JniUtils_getStringFromC( JNIEnv* env, jobject thiz ) { return (*env)->NewStringUTF(env, "Hello NDK! ");}
最后在Activity里调用:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((TextView)findViewById(R.id.txt)).setText(JniUtils.getStringFromC()); }}
运行成功:
直接使用动态链接库
大部分情况下,我们使用NDK不需要自己编写Native文件,只要把动态链接库,即.so文件直接拿来用就好了。许多第三方SDK都提供了完整的解决方案。
AS中so文件存放的目录默认是在main/jniLibs下,不过我们还是习惯把so文件和jar包一样都放在app/libs下,不过需要在gradle中添加一项配置,如下:
android {... sourceSets { main { jniLibs.srcDirs = ['libs'] } }...
这样在系统加载so的时候会去libs目录下加载。
接着刚才的例子,在app\build\intermediates\ndk\debug\lib下面,会发现各种架构的so文件,命名是以lib+modulename组成的。
你可以把它们全部复制到libs下面,然后删掉jniutils.c,看看运行会不会报错。
源码已上传GitHub,地址:https://github.com/Yaoobs/NdkJniDemo
参考文章:
http://blog.csdn.net/yilip/article/details/45200861
http://www.jianshu.com/p/9aff422204eb
http://www.jianshu.com/p/d8cde65cb4f7
http://www.taoweiji.cn/2016/08/02/ndk/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
http://www.cnblogs.com/zhuyuliang/p/5007016.html
- NDK学习笔记(一)
- Android NDK 学习笔记(一)
- Android学习笔记-NDK开发(一)
- Android NDK 学习笔记(一)
- NDK学习(一)
- Android NDK 开发系列 学习笔记(一)
- NDK开发笔记(一)
- Android NDK (学习笔记一) —— 认识NDK
- android_c++ 高级编程NDK学习笔记一
- 我的NDK学习开发笔记(一)
- NDK开发学习(一)
- NDK开发笔记(一)---环境搭建
- NDK笔记一 20160225
- Android NDK学习教程(一)--第一个NDK程序
- OpenCV for Android Studio 学习笔记(一):NDK及OpenCV的安装配置
- (2016-10-28)Android-NDK学习笔记(一)生成简单的SO文件
- NDK Build 学习笔记<一>:fedora环境搭建
- Jni开发与Android NDK开发学习笔记(一)
- uninstall wine
- 大型网站的架构设计问题,大型高并发高负载网站的系统架构
- 蓝牙L2CAP剖析(二)
- 雷达数据扫描及聚类方法
- Android学习之Notification
- NDK学习笔记(一)
- 可以换行的RadioGroup
- ansible学习笔记(一) -- roles
- 在Eclipse下使用ant构建
- 实战 Walker 之闪屏 界面的实现
- Canvas4——图形绘制处理
- nm 目标文件格式分析
- Kafka设计解析(一)- Kafka背景及架构介绍
- lucene在项目中真实使用