Android studio NDK开发
来源:互联网 发布:linux mint美化 编辑:程序博客网 时间:2024/06/06 07:27
上一篇博客,因为第一次弄,上传的Demo代码忘记了加博客地址,然后下载的次数有几十次,但是博客浏览却是个位数,还是自己检查有没有编辑错误点的(>_ <)
知错就要改, 这次一定不能忘了!内心里狂烈得嘶吼…_,不说笑了,写博客如果可以起到帮助作用是一个方面,对自己知道的东西的梳理,归纳是另一个方面。技术分享,本身就是一种乐趣。
闲话不多说,在项目开发中,有时候总免不了NDK方面的开发,那使用NDK有哪些方面的好处?
1.可以提高代码的安全性。因为so反编译相对比较困难,所以也就间接得提升了代码的安全性,提高了程序的安全性。
2.可以很方便得使用目前已知的C或C++开源库
3.便于平台间的移植。通过C/C++实现的动态库,可以很方便得在其他平台上使用。
4.可以提高程序,在某些特定情形下的执行效率,不过并不能明显提升Android程序的性能。
这是一些大神总结的几点,也相对容易理解,使用NDK有这些方面的优点,那NDK是什么,是一个什么具体的概念?
NDK是Android提供的一个工具集合,通过NDK可以在Android中更加方便得通过JNI来访问本地代码,比如C或C++。简单得理解,就是方便访问C/C++等方面的本地代码。
怎么使用NDK,NDK怎么开发?记得以前用Eclipse进行NDK开发的时候,还比较麻烦,记得当时好像是下的Cygwin弄的,时间有点长,有些忘了,就是记得好像有些麻烦。通过Android studio进行NDK开发,相对方便很多,首先我们要下载NDK(如果自己电脑上没有)。下载可以选择官网下载(需要翻墙),也可以选择直接在as内部下载,(我使用的As是2.3正式版,在SDK Tools选项里没有找到NDK的选项,也不知道为什么),也可以百度NDK下载,自行找资源下载。我在博客的最后提供一个了android-ndk-r11b版本的NDK下载链接,有32位和64位,朋友们如果需要,可以根据需要进行下载。
NDK下载好,我们需要进行一些配置,首先打开AS的Project Structure选项,指定NDK的位置,比如我电脑上NDK的目录位置是:F:\NDK\ndk-r11b\android-ndk-r11b,指定:
指定好NDK位置,项目的local.properties里,应该会自动添加上NDK目录信息,
要使用ndk的命令,还要配置一下电脑的环境变量,新建变量,指向NDK目录
然后将新建的NDK环境变量,添加到Path的末尾(ps:Path最后添加的不要加”;”哦)
配置好NDK环境变量,打开cmd窗口,输入ndk-build命令,如果
说明NDK配置成功。配置好NDK环境,然后我们终于可以开始进行NDK开发,新建一个安卓moudle,创建一个用来供调用的Java类,定义native方法,
public class MyJni { static { System.loadLibrary("TestJNI");//加载(JNI)so库 完整规范:libTestJNI.so } //定义native方法 //静态native方法 public static native String get();//JNI调C/C++ public static native void invokeJavaMethod();//JNI调Java}
然后选择Make Project,build当前moudle,生成.class文件(这里定义的native方法可能报错,没关系,直接build编译)。之后,打开As的终端,
通过javah命令来生成.h头文件。使用javah命令,需要定义到要编译的类的上一级,比如我这里MyJni.java文件所在的完整目录是:F:\MyBlog\app\jnindkdemo\src\main\java\com\example\jnindkdemo\MyJni.java
定义到上一级
然后使用javah命令,来生成.h头文件,
javah -jni com.example.jnindkdemo.MyJni
如果直接这样调用javah来生成.h头文件,可能会报下面这个编码错误,
这里是因为如果没有指定编码,javah会默认调用操作系统的默认编码,我的电脑是Windows8操作系统,默认编码是GBK,所以会出现上面的错误。既然是没有指定编码那就指定编码,调用javah命令时指定编码
编译完成,在moudle的java目录下,
如果生成了.h文件,说明.h头文件生成成功。然后,右键moudle,新建jni目录
因为我这里使用的是C++进行的实现,所以选择jni目录右键,创建一个myjni.cpp的文件(ps:我已经创建过,这里主要做演示)
然后将生成的.h文件里的内容,全部复制到myjni.cpp中。到这一步,就可以开始编写定义的native方法的实现。
.h文件中的全部代码:
/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_example_jnindkdemo_MyJni */#ifndef _Included_com_example_jnindkdemo_MyJni#define _Included_com_example_jnindkdemo_MyJni#ifdef __cplusplusextern "C" {#endif/* * Class: com_example_jnindkdemo_MyJni * Method: get * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_com_example_jnindkdemo_MyJni_get (JNIEnv *, jclass);/* * Class: com_example_jnindkdemo_MyJni * Method: invokeJavaMethod * Signature: ()V */JNIEXPORT void JNICALL Java_com_example_jnindkdemo_MyJni_invokeJavaMethod (JNIEnv *, jclass);#ifdef __cplusplus}#endif#endif
myjni.cpp实现后的本地代码:
//// Created by Tangshuiming99 on 2017/5/4.//#include <jni.h>#include <stdio.h>/* Header for class com_example_jnindkdemo_MyJni */#ifndef _Included_com_example_jnindkdemo_MyJni#define _Included_com_example_jnindkdemo_MyJni#ifdef __cplusplusextern "C" {#endif//调用Java代码方法void callJavaMethod(JNIEnv *env, jclass thiz){ //找到Java类 jclass clazz = env->FindClass("com/example/jnindkdemo/MainActivity"); if(clazz == NULL){ printf("It's error for find MainActivity class"); return; } //找到要调用的方法 (Ljava/lang/String;)V方法签名 Ljava/lang/String:方法参数类型 V:void 返回值类型 jmethodID id = env->GetStaticMethodID(clazz, "calledByJNI", "(Ljava/lang/String;)V"); if(id == NULL){ printf("It's error for find calledByJni method"); return; } jstring data = env->NewStringUTF("data send to calledByJNI in myjni.cpp"); env->CallStaticVoidMethod(clazz, id, data);}/* * Class: com_example_jnindkdemo_MyJni * Method: get * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_com_example_jnindkdemo_MyJni_get (JNIEnv *env, jclass thiz){ return env->NewStringUTF("Hello, I am from JNI!(我来自JNI(impl with c++)-Java调JNI"); };/* * Class: com_example_jnindkdemo_MyJni * Method: invokeJavaMethod * Signature: ()V */JNIEXPORT void JNICALL Java_com_example_jnindkdemo_MyJni_invokeJavaMethod (JNIEnv *env, jclass thiz){ //JNI调Java方法 callJavaMethod(env, thiz); };#ifdef __cplusplus}#endif#endif
完成了JNI本地代码的编写,NDK的开发大概完成了一大半,接下来我们要配置ndk的gradle参数,来生成对应的so。打开moudle的build.grade,在defaultConfig下新添ndk配置
然后打开项目下的gradle.properties,添加android.useDeprecatedNdk=true,表示允许NDK版本差异。做完这些,选择Make Project,编译so文件,如果
说明so编译成功。到这里,NDK的主要开发步骤已经完成,接下来就剩最简单的调用。右键main目录,创建jniLibs目录,将生成的so文件全部复制进去
然后在MainActivity中,直接调用native方法,MainActivity代码:
public class MainActivity extends AppCompatActivity { private static final String TAG = "TAG MainActivity"; private Button JNIInvokeCppSta, JNIInvokeJava, JNIInvokeCpp; private TextView textView; private static Context context; private void setupView(){ JNIInvokeCppSta = (Button)findViewById(R.id.JNIInvokeCpp_static_button); JNIInvokeJava = (Button)findViewById(R.id.JNIInvokeJava_button); JNIInvokeCpp = (Button)findViewById(R.id.JNIInvokeCpp_button); textView = (TextView)findViewById(R.id.textView); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setupView(); addListener(); context = getApplicationContext(); } private void addListener() { JNIInvokeCppSta.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //Java调JNI(静态native方法) String str = MyJni.get(); textView.setText(str); } }); JNIInvokeJava.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //JNI调Java方法 MyJni.invokeJavaMethod(); } }); } //开放给Jni调用的方法 public static void calledByJNI(String dataFromJni){ Log.w(TAG, "方法被Jni调用,接收到Jni传递过来的数据:" + dataFromJni); if(context != null){ Toast.makeText(context, "JNI调用Java方法,接收到数据:" + dataFromJni, Toast.LENGTH_SHORT).show(); } } @Override protected void onDestroy() { context = null; super.onDestroy(); }}
运行程序,点击Java调用JNI按钮,
点击JNI调Java按钮,查看log:
05-05 15:23:37.288 28171-28171/com.example.jnindkdemo W/TAG MainActivity: 方法被Jni调用,接收到Jni传递过来的数据:data send to calledByJNI in myjni.cpp
证明Java调JNI(C++),JNI调Java,全部调用成功。这样,一次相对简单的相互调用完成。我们来总结一下,NDK大概的开发过程:
1.下载NDK,配置NDK环境(如果没有配置过)
2.定义用来调用的Java类,定义native方法
3.编译生成class文件
4.生成.h文件
5.编写本地代码(C/C++)
6.配置ndk grade脚本参数,编译生成so
7.使用生成的so,来供Java、JNI相互调用
看到这里,可能有的朋友会奇怪:目前定义的natvie方法都是静态的,那如果是非静态方法?
如果是非静态方法,那我们就多加一步对象构建,然后再通过对象调用方法就行了。比如,我们现在新定义一个非静态的native方法
//非静态native方法 public native String cycleReturn(String str);
重新编译class文件,重新生成.h头文件,编写新增的cycleReturn本地方法实现(ps:本来想传入一个字符串到JNI,然后给字符串添加一个JNI或C++的后辍传出来,所以取名cycleReturn。但是C++的语法记得不多,最后只能返回一条新字符串出来>_<):
JNIEXPORT jstring JNICALL Java_com_example_jnindkdemo_MyJni_cycleReturn (JNIEnv *env, jobject thiz, jstring string){ return env->NewStringUTF("I from JNI, cycle return(c++)!"); };
编写完本地方法实现,重新编译生成新so,然后将新so替换掉jniLIB中的so,在MainActivty添加新JNI的调用:
JNIInvokeCpp.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //Java调JNI(非静态native方法) MyJni myJni = new MyJni(); String s = myJni.cycleReturn("sss"); textView.setText(s); } });
运动程序:
证明,非静态native方法也调用成功。
博客中,难怪有不正确错误的地方,欢迎大家不吝指出。如果需要转载,请注明出处,谢谢。
NDK下载
源码下载
- Android studio NDK开发
- Android Studio开发NDK
- Android Studio ndk开发
- Android studio开发NDK
- Android Studio NDK开发
- Android Studio NDK 开发
- android studio ndk 开发
- Android Studio NDK开发
- Android Studio NDK开发
- Android Studio NDK开发
- Android Studio NDK开发
- Android Studio NDK 开发
- Android Studio NDK 开发
- android studio ndk开发
- Android Studio NDK开发
- android studio ndk 开发
- android studio ndk 开发
- Android studio NDK开发
- nginx环境配置https
- Error: java.util.concurrent.ExecutionException: com.android.ide.common.process.ProcessException:
- 常见内存拷贝函数:memcpy()、memmove()、strcpy()的实现及区别
- RecyclerView的点击事件
- linux shell 脚本 历史文件清理脚本,按天,按月,清理前N天的历史文件,删除指定大小历史文件,历史文件归档清理
- Android studio NDK开发
- 关于struts后台向页面传值及jsp接收笔记
- 第145课:Spark面试经典系列之Yarn生产环境下资源不足问题和网络的经典问题详解
- 记录一个小问题就是轮播图就一张的问题--------源于自己的马虎
- linux命令之which
- 类与对象.
- 【源码分析】CountDownTimer倒计时为何如此优雅
- redis学习教程之一基本命令
- Unity3DGame学习笔记(7):DOTween