對於還不瞭解Android NDK作用和搭建的朋友,可以先參考下Android NDK作用與搭建,今天這篇文章是建立在已經搭建好Android NDK開發環境,來看看NDK編程 "Hello Jni"的實現。
首先Android NDK開發需要具備哪些基礎知識?
1.熟悉Android應用開發
2.熟悉C語言(編寫底層.so庫需要用到)
3.瞭解Linux(要知道一些常用的命令)
下面開啟NDK包下Sample目錄下的“hello-jni”例子工程
如可以看到,就是一個普通Android應用程式,在Src目錄裡有個HelloJni.Java程式,不同的是多了一個“jni”目錄,JNI(Java Native Interface,中文為JAVA本地調用),JNI的作用:可以通過Java程式訪問到其他語言(主要是C/C++)寫出來的庫(linux平台.so庫,Windows平台.dll庫),其中NDK會通過“.mk“檔案將“hello-jni.c”C語言檔案編譯成需要的.so庫,預設情況下“.mk“檔案不需要修改,有興趣的朋友可以百度下JNI更多相關資料。
下面將此例子代碼匯入到eclipse中,剛開始研究時只用匯入第一個HelloJni項目就行了
先看看HelloJni.Java
/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.example.hellojni;import android.app.Activity;import android.widget.TextView;import android.os.Bundle;public class HelloJni extends Activity{ /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /* Create a TextView and set its content. * the text is retrieved by calling a native * function. */ TextView tv = new TextView(this); tv.setText( stringFromJNI() ); setContentView(tv); } /* A native method that is implemented by the * 'hello-jni' native library, which is packaged * with this application. */ public native String stringFromJNI(); /* This is another native method declaration that is *not* * implemented by 'hello-jni'. This is simply to show that * you can declare as many native methods in your Java code * as you want, their implementation is searched in the * currently loaded native libraries only the first time * you call them. * * Trying to call this function will result in a * java.lang.UnsatisfiedLinkError exception ! */ public native String unimplementedStringFromJNI(); /* this is used to load the 'hello-jni' library on application * startup. The library has already been unpacked into * /data/data/com.example.hellojni/lib/libhello-jni.so at * installation time by the package manager. */ static { System.loadLibrary("hello-jni"); }}
我們Crowdsourced Security Testing道這行代碼System.loadLibrary("hello-jni");就可以訪問到“hello-jni.c”檔案編譯後的.so庫,然後tv.setText( stringFromJNI() );就可以調用到“hello-jni.c”C檔案中的函數。
這行代碼public native String stringFromJNI();表示介面的申明,返回字串。直接映射到了C檔案中的調用函數。
接著看下hello-jni.c
/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */#include #include /* This is a trivial JNI example where we use a native method * to return a new VM String. See the corresponding Java source * file located at: * * apps/samples/hello-jni/project/src/com/example/hellojni/HelloJni.java */jstringJava_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, jobject thiz ){#if defined(__arm__) #if defined(__ARM_ARCH_7A__) #if defined(__ARM_NEON__) #define ABI "armeabi-v7a/NEON" #else #define ABI "armeabi-v7a" #endif #else #define ABI "armeabi" #endif#elif defined(__i386__) #define ABI "x86"#elif defined(__mips__) #define ABI "mips"#else #define ABI "unknown"#endif return (*env)->NewStringUTF(env, "huahua Hello from JNI ! Compiled with ABI " ABI ".");}
對Java_com_example_hellojni_HelloJni_stringFromJNI函數返回的字串做了一點小小的修改,其中函數第一個參數 JNIEnv是Google所提供的用於Java和C互動的一種結構,結構的細節暫時不去深究,Crowdsourced Security Testing道通過return返回這行代碼的文法就可以把某項內容返回給來自於Java檔案的調用者stringFromJNI()。
這裡可能會有疑問stringFromJNI()和Java_com_example_hellojni_HelloJni_stringFromJNI()兩個方法的名字不一樣,其實C檔案中最後一個"_"後面的名字就是Java中調用的介面的名字,前面則是包名的結構,這種寫法的原因是來自gcc編譯器的,而且是有關聯規則。
下面直接在手機上運行下工程,出現彈錯!,logcat資訊如下
因為現在.so庫還不存在呢,需要我們自己通過mk檔案,C檔案編譯出來。開啟控制台,進入到E:\android-ndk-r9d\samples\hello-jni\jni目錄,輸入ndk-build
ndk-build這個指令碼程式會完成編譯,產生.so庫等等一切的相關工作,執行完後在工程下多了"libs"和"obj"2個子目錄,
在libs中會根據不同CPU硬體產生各自的.so庫,這個暫時不用去深究。
這時重新整理一下eclipse中的工程,libs中的.so庫就加入進去了,運行程式效果如下
說明上層Java程式調用底層.so庫中的函數OK,這樣一個簡單的JNI調用就完成了。
我們再看下在HelloJni.Java中
static {
System.loadLibrary("hello-jni");
}
這個文法表示在類載入時,就會調用System.loadLibrary("hello-jni");,去載入hello-jni這個庫。這個名字在代碼中是這樣寫,在實際中linux平台中左邊會多上lib3個字母,右邊會多上.so。其實就是我們編譯出來的libs目錄中的libhello-jni.so庫。如果在Windows平台下右邊會是.dll。庫個名稱和程式中寫的是相互對應的,這是由作業系統規定必須這樣寫的。
好了,Android NDK的基礎編程入門就說到這裡。