JNI開發系列閱讀
1. 前言
1.1 Android SDK介紹
Android是基于Linux內核的一個手機操作系統,谷歌提供了開發包(Android SDK),程序員可以通過開發包開發Android App(應用程序)。Android SDK提供Java語言接口,因此Android應用是使用Java開發的。
1.2 使用純Java開發App的缺點
在某些場合下,使用純Java開發Android應用程序不完美,比如:
- 有高性能算法,Java語言無法滿足
- 有跨平臺需求,希望將App移植到iOS
- 已有代碼的重用
1.3 引入NDK
早在Android 1.6(2009年)時,google就提供了NDK(native development kit),NDK包括了一套Android的交叉編譯環境和開發庫,利用它可以編寫C/C++程序,并編譯成Android環境下使用的動態庫,Java代碼通過Jni規范,調用C/C++寫的動態庫。
目前最新的Android Studio 2.2中,集成了C/C++開發環境,開發人員在使用C/C++更加簡單了。
2. 課程內容
NDK中文官方開發技術文檔地址
下載配置NDK
配置NDK
如果不配置NDK路徑,會報NDK沒有配置錯誤
JNI開發HelloWorld
把 Include C++ support的勾打上
選擇C++11和Toolchain Default均可,C++11有更多的新特性和功能
點擊Finish后,進入工程目錄,如圖所示,除了java文件夾外多了一個cpp文件夾,cpp就是存放c和c++代碼的文件夾
配置NDK開發環境中遇到的坑
Failed to find CMake
什么,CMake是什么鬼,原來,在Android Studio 2.2 后,NDK開發更加人性化了,使用了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在中國開了發布會后,Google的鏈接可以使用了,Android開發官網也可以上了,而且翻譯了大量的技術文檔,方便了英語不太好的同學
打開 SDK Manager,安裝上CMake和LLDB
更多更詳細的NDK開發文檔,請看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");
// 把參數用到的字符串轉化成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函數的簽名,比如
javap -s Jni