JNI開發(fā)系列閱讀
1. 前言
1.1 Android SDK介紹
Android是基于Linux內核的一個手機操作系統(tǒng)歹河,谷歌提供了開發(fā)包(Android SDK)衣屏,程序員可以通過開發(fā)包開發(fā)Android App(應用程序)室抽。Android SDK提供Java語言接口览祖,因此Android應用是使用Java開發(fā)的。
1.2 使用純Java開發(fā)App的缺點
在某些場合下扭仁,使用純Java開發(fā)Android應用程序不完美垮衷,比如:
- 有高性能算法,Java語言無法滿足
- 有跨平臺需求乖坠,希望將App移植到iOS
- 已有代碼的重用
1.3 引入NDK
早在Android 1.6(2009年)時搀突,google就提供了NDK(native development kit),NDK包括了一套Android的交叉編譯環(huán)境和開發(fā)庫熊泵,利用它可以編寫C/C++程序仰迁,并編譯成Android環(huán)境下使用的動態(tài)庫,Java代碼通過Jni規(guī)范戈次,調用C/C++寫的動態(tài)庫轩勘。
目前最新的Android Studio 2.2中,集成了C/C++開發(fā)環(huán)境怯邪,開發(fā)人員在使用C/C++更加簡單了绊寻。
2. 課程內容
NDK中文官方開發(fā)技術文檔地址
下載配置NDK
配置NDK
如果不配置NDK路徑,會報NDK沒有配置錯誤
JNI開發(fā)HelloWorld
把 Include C++ support的勾打上
選擇C++11和Toolchain Default均可悬秉,C++11有更多的新特性和功能
點擊Finish后澄步,進入工程目錄,如圖所示和泌,除了java文件夾外多了一個cpp文件夾村缸,cpp就是存放c和c++代碼的文件夾
配置NDK開發(fā)環(huán)境中遇到的坑
Failed to find CMake
什么,CMake是什么鬼武氓,原來梯皿,在Android Studio 2.2 后,NDK開發(fā)更加人性化了县恕,使用了CMake东羹,一款外部構建工具吨述,可與 Gradle 搭配使用來構建原生庫钾菊。如果您只計劃使用 ndk-build桩盲,則不需要此組件苇羡。還有LLDB,一種調試程序罩锐,Android Studio 使用它來調試原生代碼侦高。
點擊Install CMake and sync project停巷,提示如下錯誤
Gradle sync failed: Failed to find CMake.
Install from Android Studio under File/Settings/Appearance & Behavior/System Settings/Android SDK/SDK Tools/CMake.
Expected CMake executable at D:\android-sdk\cmake\bin\cmake.exe.
Consult IDE log for more details (Help | Show Log)
原來是我使用了代理,因為之前Google的鏈接需要翻墻才能夠使用恕酸,所以配置了某代理堪滨,但是該代理不管用,在設置中把代理去掉即可尸疆。在Google在中國開了發(fā)布會后椿猎,Google的鏈接可以使用了惶岭,Android開發(fā)官網也可以上了寿弱,而且翻譯了大量的技術文檔,方便了英語不太好的同學
打開 SDK Manager按灶,安裝上CMake和LLDB
更多更詳細的NDK開發(fā)文檔症革,請看Android官方中文文檔向您的項目添加 C 和 C++ 代碼
2.3 Android Java代碼調用C++代碼
Java部分代碼
public class Jni {
static {
System.loadLibrary("bc-lib"); // libbc-lib.so
}
private static Jni obj = new Jni();
private Jni(){}
public static Jni instance(){
return obj;
}
// native接口
public native boolean Login(String username, String password, String type);
public native boolean Reg(String username, String password, String mobile, String email, String id);
public native boolean LocationChange(double lng, double lat);
public native boolean StartOrder(double lng1, double lat1, double lng2, double lat2);
}
C++部分代碼
JNIEXPORT jboolean JNICALL Java_cn_xueguoliang_hc_Jni_Login
(JNIEnv *env, jobject /* Jni object */, jstring jUsername, jstring jPassword, jstring type)
{
return (jboolean)User::instance()->Login(j2c(env, jUsername), j2c(env, jPassword),
j2c(env, type));
}
JNIEXPORT jboolean JNICALL Java_cn_xueguoliang_hc_Jni_Reg
(JNIEnv *env, jobject /* Jni object */,
jstring jUsername, jstring jPassword, jstring mobile, jstring email, jstring id)
{
return (jboolean)User::instance()->Reg(
j2c(env, jUsername),
j2c(env, jPassword),
j2c(env, mobile),
j2c(env, email),
j2c(env, id));
}
JNIEXPORT jboolean JNICALL Java_cn_xueguoliang_hc_Jni_LocationChange
(JNIEnv *, jobject, jdouble lng, jdouble lat)
{
User::instance()->LocationChange(lng, lat);
return (jboolean)true;
}
JNIEXPORT jboolean JNICALL Java_cn_xueguoliang_hc_Jni_StartOrder
(JNIEnv *, jobject, jdouble lng1, jdouble lat1, jdouble lng2, jdouble lat2)
{
return (jboolean)Order::instance()->start(lng1, lat1, lng2, lat2);
}
2.4 C++代碼調用Java代碼
Java代碼
public class Jni {
static {
System.loadLibrary("native-lib");
}
private static Jni obj = new Jni();
public static Jni instance()
{
return obj;
}
public native void HelloWorld();
void callByCpp()
{
Log.e("JniCallback", "hello java");
}
}
C++代碼
extern "C"
void
Java_com_example_xueguoliang_test_Jni_HelloWorld(
JNIEnv* env,
jobject This ) {
std::string hello = "Hello from C++";
jclass jniClass = env->FindClass("com/example/xueguoliang/test/Jni");
jmethodID jmethodID1 = env->GetMethodID(jniClass, "callByCpp", "()V");
env->CallVoidMethod(This, jmethodID1);
return;
}
2.5 Java和C++字符串轉換
jstring c2j(JNIEnv* env, string cstr)
{
return env->NewStringUTF(cstr.c_str());
}
string j2c(JNIEnv* env, jstring jstr)
{
string ret;
jclass stringClass = env->FindClass("java/lang/String");
jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B");
// 把參數(shù)用到的字符串轉化成java的字符
jstring arg = c2j(env, "utf-8");
jbyteArray jbytes = (jbyteArray)env->CallObjectMethod(jstr, getBytes, arg);
// 從jbytes中,提取UTF8格式的內容
jsize byteLen = env->GetArrayLength(jbytes);
jbyte* JBuffer = env->GetByteArrayElements(jbytes, JNI_FALSE);
// 將內容拷貝到C++內存中
if(byteLen > 0)
{
char* buf = (char*)JBuffer;
std::copy(buf, buf+byteLen, back_inserter(ret));
}
// 釋放
env->ReleaseByteArrayElements(jbytes, JBuffer, 0);
return ret;
}
2.6 javah和javap
javah用于生成native接口定義鸯旁,比如
javah -d ../cpp/ com.example.xueguoliang.test.Jni
javap用于生成java函數(shù)的簽名噪矛,比如
javap -s Jni