前两天发现一本相当可以的书Java 性能权威指南,粗略的看了两天,对于我这种还在去中级的路上的人,看着的确是挺累的,所以也就歇了几天没有写博客了。看了那么高深的东西还是要继续回来巩固一下实战水平,所以今天继续来讲我的这个应用的实现过程,在上篇文章实现PHP服务器+Android客户端(Retrofit+RxJava)第四天客户端与服务器通信的实现之后相信大家对简单的数据通信已经不在话下了,今天我要说的东西其实是为了后续讲我要在这个应用中要用到的两个框架打下基础,两个非常好的框架:fresco,ijkplayer,fresco是图片加载框架,ijkplayer是视频加载播放框架,这两个框架都用到了ndk开发,什么是ndk开发这里就不多说了,自行百度!
ndk环境配置
可直接参考: Android Studio建立jni开发环境
我用的是android studio,这里也只讲android studio的ndk环境配置,首先下载最新的ndk版本,地址:NDK Downloads,下载之后解压即可,先配置android studio的ndk路径,点击”File”,”Project Structure”,大致效果如下:
看到下面的ndk location,选择刚刚解压的ndk就行
其实还有一种方法下载ndk
在Toolbare可以找到Android的更新图标:
在SDK Tools的Tab页中,勾选LLDB 2.1和NDK:
但是上面这种方法比较的慢,还是自己下载比较快
随后添加环境变量 NDK_ROOT 为: D:\Android\android-ndk-r10d(解压路径)
在环境变量 PATH 下追加 :%NDK_ROOT%;
不写Hello World
每次学一门语言的时候,我们老是hello world,我就不再这hello world了,在这里我实现的效果是传入一个数,做个平方,然后输出。比如输入5,然后输出25.
首先右键点击App(对应的module)新建jni目录:
新建java类:
package com.sqq.beginndk;
import android.content.Context;
import android.widget.Toast;
/**
* Created by sqq on 2016/6/21.
*/
public class LoadRetJni {
Context con;
static {
System.loadLibrary("getRet");
}
public LoadRetJni(Context context){
con = context;
}
/**
* 这个函数的目的是为了展示底层c语言怎么调用
* java函数
* @param value 这个value值来自本地c代码
*/
public void showMsg(int value){
Toast.makeText(con, "The Value" + value, Toast.LENGTH_LONG);
}
public native int getSquare(int in);
}
gradle中配置支持ndk
defaultConfig {
applicationId "com.sqq.beginndk"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
//指定.so的目录
sourceSets.main{
jniLibs.srcDir 'src/main/libs'
}
ndk{
moduleName 'getRet'
}
}
还需要在gradle.properties中设置android.useDeprecatedNdk=true
接下来生成 .h文件
javah -d src/main/jni/ -classpath build/intermediates/classes/debug/ com.sqq.beginndk.LoadRetJni
其中-d是生成.h文件的保存目录
-classpath是指定.class所在的目录,项目build成功之后,会在build/intermediates/classes/debug/目录里生成.class文件。
com.sqq.beginndk.LoadRetJni是包名加上类名。
就可以在jni目录下得到一个com.sqq.beginndk.LoadRetJni.h的文件。
实现这个头文件中方法
新建.c文件实现getSquare方法。直接复制头文件中的声明(记得把参数加上),然后实现其中的方法就行了,具体代码如下:
#include "com.sqq.beginndk.LoadRetJni.h"
JNIEXPORT void JNICALL JNIEXPORT jstring JNICALL Java_com_sqq_beginndk_LoadRetJni(JNIEnv *env, jobject object) {
jclass clazz = (*env)->GetObjectClass(env,object);
jmethodID id = (*env)->GetMethodID(env,clazz,"showMsg","(I)V");
jint ret= in*in;
(*env)->CallVoidMethod(env,object,id,ret);
(*env)->DeleteLocalRef(env,clazz);
return ret;
}
添加Application.mk
APP_MODULES := getRet
APP_ABI :=all
添加Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE :=getRet
LOCAL_SRC_FILES := com_sqq_beginndk_LoadRetJni.c
include $(BUILD_SHARED_LIBRARY)
最后一步,在../app/src/main/jni目录下,运行命令
ndk-build
随后你会发现生成了so文件,这时候你就可以运行了,但是不要高兴的太宰,你可能会发现有文件冲突的错误,解决的办法就是把src/main目录下生成的libs和obj文件删除就行了,在运行就可以了,会显示25,toast会输出the Values 25。
说到这里,其实大家可能也和我一样不知道为什么libs文件和build下的文件为什么都会有so文件,而且两者还冲突,删除build下的还没有用,它会自动又生成,这个先不管,我要实现的效果是实现了。
最后还要说的是,如何在其他的工程中使用我们自己生成的so文件,首先创建java类,代码就是上面的java类一样,如下:
package com.sqq.beginndk;
import android.content.Context;
import android.widget.Toast;
/**
* Created by sqq on 2016/6/21.
*/
public class LoadRetJni {
Context con;
static {
System.loadLibrary("getRet");
}
public LoadRetJni(Context context){
con = context;
}
/**
* 这个函数的目的是为了展示底层c语言怎么调用
* java函数
* @param value 这个value值来自本地c代码
*/
public void showMsg(int value){
Toast.makeText(con, "The Value" + value, Toast.LENGTH_LONG);
}
public native int getSquare(int in);
}