由于需要在PC和安卓之间进行h264编码打包rtp传输,需要在安卓端进行rtp包接收和解码,因此需要进行NDK开发;
下载安装Android Studio 1.4后看介绍发现较之之前版本(1.3以前),对NDK的支持变好,配置NDK不用以前那样写mk文件;
于是参考http://www.cnblogs.com/zhuyp1015/p/4976116.html
《Android Studio1.4.x JNI开发基础-基本环境配置》,基本上完成配置,但自己这由于一些出入,总遇到些莫名其妙的问题,所以还是自己记录一遍已做备忘,同时作为第一篇博客;
步骤基本和http://www.cnblogs.com/zhuyp1015/p/4976116.html相同,但还是贴一遍;
-------------------
一、首先准备基本工具,Android Studio (>=1.3.x), NDK(ndk-r10-e)。打开Android Studio 建立一个空工程,关联上NDK,操作步骤方式如下图:
下一步
这里我选择安卓4.4以上;
二、设置ndk路径
file->project structure
gradle version 输入2.5
三、开始设置gradle
1、首先设置TopLevel gradle,也就是Project gradle,这里比较简单,在dependencies中设置:
classpath ‘com.android.tools.build:gradle-experimental:0.2.0’
注意这里要把之前的类似classpath ‘com.android.tools.build:gradle:1.3.0’ 注释掉。
2、接着设置 Module gradle,这一步是比较麻烦的。由于我们在创建工程的时候自动生成的这个gradle文件内容比较多,而且如果要使用NDK的话这个gradle变化比较大,这里直接贴出需要使用NDK的gradle,然后来进行说明。
apply plugin: 'com.android.model.application'
model {
android {
compileSdkVersion = 23//根据自己需要以及安装的内容选择版本
buildToolsVersion = "23.0.2"//没有的话可用sdk manager安装
packagingOptions.with {
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
}
defaultConfig.with {
applicationId = "com.zyp.ndktest"
minSdkVersion.apiLevel = 19 // Unable to load class com.android.build.gradle.managed.ProductFlavor_Impl
targetSdkVersion.apiLevel = 21
versionCode = 1
versionName = "1.0"
}
}
android.buildTypes {
//debug {
//}
release {
minifyEnabled = false
proguardFiles += file('proguard-rules.pro')
}
}
compileOptions.with {
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
}
android.ndk {
moduleName = "NdkSample"//设置的so文件名
cppFlags += "-std=c++11"
cppFlags += "-fexceptions"
cppFlags += "-I${file("src/main/jni//include")}".toString()
ldLibs += ["android", "log"]
stl = "gnustl_shared"
}
//将会在app\build\intermediates\binaries\debug目录下生成相印文件,内有响应平台的NdkSample.so文件
android.productFlavors {
create("arm7") {
ndk.abiFilters.add("armeabi-v7a")
ndk.abiFilters.add("armeabi")
}
create("arm8") {
ndk.abiFilters.add("arm64-v8a")
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:23.0.0'
compile 'com.android.support:design:23.0.1'
}
这个gradle的配置有几点需要注意的:
i.首先是 apply plugin: ‘com.android.application’ 变成了 apply plugin: ‘com.android.model.application’。
ii.所有值的设置都要写成 xxx = yyyy的形式,比如: applicationId = “com.zyp.ndktest” (自动生成的gradle 则可能是: applicationId = “com.zyp.ndktest” ),否则会爆这种错误:Error:Cause: org.gradle.api.internal.ExtensibleDynamicObject, 当出现此类错误,检查是否都用了 “=”的方式。
iii.buildTypes 需要从android{} 中取出来,写成android.buildTypes{}的形式,否则会出现这种错误:Error:Unable to load class ‘org.gradle.nativeplatform.internal.DefaultBuildType_Decorated’.
iv.defaultConfig{} 需要写成defaultConfig.with{} 的形式,否则会报这种错误:Error:Cause: com.android.build.gradle.managed.AndroidConfig_Impl.
v.在defaultConfig.with{} 中 需要写成
minSdkVersion.apiLevel = 19
targetSdkVersion.apiLevel = 23
也就是比自动生成的多 .apiLevel ,否则会报这种错误:Unable to load class com.android.build.gradle.managed.ProductFlavor_Impl.
vi.增加compileOptions.with{} 需要选择JavaVersion.VERSION_1_7,否则会报这种错误:Bad class file magic or version.
vii.最后一点,在gradleWrapper中使用的是2.5,则android.ndk {} 中类似cppFlags 的添加使用 += 的方式,否则需要使用 .add的方式
四、创建jni文件夹,然后编写Native代码
添加cpp/h文件:
NDKTEST.h:
#ifndef NDKTEST_NDKTEST_H
#define NDKTEST_NDKTEST_H
#include "jni.h"
#include <stdio.h>
#include <string.h>
extern "C" {
JNIEXPORT jstring JNICALL
Java_nbicc_ffmpeg_1test_MainActivity_sayHello(JNIEnv *env, jclass type,
jstring filename);
}
#endif //NDKTEST_NDKTEST_H
NDKTEST.cpp:
#include "NDKTEST.h"
JNIEXPORT jstring JNICALL Java_nbicc_ffmpeg_1test_MainActivity_sayHello
(JNIEnv *env, jclass cls, jstring j_str)
{
const char *c_str = nullptr;
char buff[128] = {0};
jboolean isCopy;
c_str = env->GetStringUTFChars(j_str, &isCopy);
printf("isCopy:%d\n",isCopy);
if(c_str == NULL)
{
return NULL;
}
printf("C_str: %s \n", c_str);
sprintf(buff, "hello %s", c_str);
env->ReleaseStringUTFChars(j_str, c_str);
return env->NewStringUTF(buff);
}
五、编译生成libNDKTEST.so
此时在app\build\intermediates\binaries\debug\目录下已经生成了so文件
六、在java中使用jni编译得到的so文件
打开项目新建得到的或者你自己建立的Activity
开头加入:
import android.util.Log;
定义函数:
public native static String sayHello(String str);
引入jni编译得到的so文件:
static {
System.loadLibrary("NDKTEST");
}
//注意这里不需要前缀lib以及扩展名.so,会自动到app\build\intermediates\binaries\debug\目录下寻找相应的libNDKTEST.SO
修改protected void onCreate:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String ret = sayHello("zhuzhu");
Log.i("JNI_INFO", ret);
}
完整的代码为:
package nbicc.ndktest;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
public native static String sayHello(String str);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String ret = sayHello("string input");
Log.i("JNI_INFO", ret);
}
static {
System.loadLibrary("NDKTEST");
}
}
七、连上手机或用模拟器运行
结果发现程序安装到手机后,运行时提示已经停止运行;
查看android monitor上的提示:
No implementation found for java.lang.String nbicc.ndktest.MainActivity.sayHello(java.lang.String) (tried **Java_nbicc_ndktest_MainActivity_sayHello** and **Java_nbicc_ndktest_MainActivity_sayHello__Ljava_lang_String_2**)
意思是sayHello在NDKTEST.h和NDKTEST.cpp上的名字不对,改成上面加粗的名字中的任意一个即可
JNIEXPORT jstring JNICALL
Java_nbicc_ndktest_MainActivity_sayHello(JNIEnv *env, jclass type,
jstring filename);
}
其实按照以前版本的ndk配置方法,.h文件是在java层定义
public native static String sayHello(String str);后build->Make project,通过javah生成的,接口函数名称Java_nbicc_ndktest_MainActivity_sayHello也是自动生成的,在这里反过来改名,更加方便一些;
重新编译,然后手机上运行,OK通过了