Android NDK基礎編程入門

對於還不瞭解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
 *
 *      https://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");
    }
}

我們先知道這行代碼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
 *
 *      https://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
 */
jstring
Java_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交互的一種結構,結構的細節暫時不去深究,先知道通過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的基礎編程入門就說到這裡。

發佈留言