Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android之NDK開發一第一個測試

Android之NDK開發一第一個測試

編輯:關於Android編程

前兩天研究了一下NDK開發,然而沒有成功。今天興趣盎然,再試試,不知道會不會成功,我將記錄我在學習過程中遇到的一些困難,以及成功後的效果。我當前的狀態是以及學習了一段時間Android開發,用的環境是Android Studio(2.1.1),所以jdk與Android studio 的配置就不描述了,從NDK開始。

第一步:安裝NDK

這裡寫圖片描述
選擇NDK點擊應用,開始下載。
這裡寫圖片描述
解壓,卡這兒半小時了,怎麼辦?
停止,重新下載,還是卡這兒,好吧,安裝軟件有時候也得看運氣,把安裝的刪除了,然後重啟電腦,再下載,睡覺,第二天醒來,裝好了,繼續出發。

第二步:修改gradle

修改APP的gradle

//ndk編譯生成.so文件
       ndk {
           moduleName "hello_jni"         //生成的so名字
           abiFilters "armeabi", "armeabi-v7a", "x86"  //輸出指定三種abi體系結構下的so庫。
       }

我這樣修改,後面的不加,看會出現什麼情況:

 defaultConfig {
        applicationId "com.example.hj.hellojni"
        minSdkVersion 18
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"

    }
    ndk{
        moduleName="hello_jni"
    }//主要是這句話
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

出錯了,出現了如下錯誤:

Error:(15, 0) Gradle DSL method not found: 'ndk()'
Possible causes:
  • The project 'HelloJNI' may be using a version of Gradle that does not contain the method. Open Gradle wrapper file
  • The build file may be missing a Gradle plugin. Apply Gradle plugin

好吧,網上搜了一下,好像應該這樣寫:

 defaultConfig {
        applicationId "com.example.hj.hellojni"
        minSdkVersion 18
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
        ndk{
            moduleName="hello_jni"
        }//寫在這個地方
    }

這次還是出錯了,不過錯誤不一樣:

Error:(13, 0) NDK integration is deprecated in the current plugin.
Consider trying the new experimental plugin
Set "android.http://blog.csdn.net/qq_25034451/article/details/useDeprecatedNdk=true" in gradle.properties to continue using the current NDK integration

按照意思添加即可,在gradle.properties中添加android.http://blog.csdn.net/qq_25034451/article/details/useDeprecatedNdk=true
好了,不報錯了,開i始編寫代碼:

第三步:添加代碼

在MainActivity這樣寫:

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("hello_jni");
    }
    //加載so庫,這兒的名字要與gradle聲明的相同
    private native String getJNIString();//聲明本地方法
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textview =(TextView)findViewById(R.id.hellowjni);
        textview.setText(getJNIString());
        Toast.makeText(MainActivity.this, getJNIString(), Toast.LENGTH_SHORT).show();
    }
}

好了,activity的編寫就完成了,接下來該編寫C語言代碼了。

第四步:編寫C語言代碼並運行

前面聲明本地方法後,方法名會高亮,如下:
這裡寫圖片描述vcjnz8LQp7n7o6y74dfUtq+0tL2o0ru49s7EvP6jrL7NysfS1MztvNO+ssystPrC67/pyrG1xNfWt/u0rs6qw/ujrM7Sz+vTprjDQVO74daxvdPJ+rPJuq/K/bXEo6y1q8rHsqLDu9PQo6y/ycTcu7nDu7j80MKwyaOsu/LV387StcSw5rG+09C147XNo7o8YnIgLz4NCjxpbWcgYWx0PQ=="這裡寫圖片描述" src="/uploadfile/Collfiles/20160910/20160910092958124.png" title="\" />
先不管,我們自己來寫,再回到MainActivity中,將鼠標放在方法名上:如下
這裡寫圖片描述
這就是我們的C語言函數名,我們需要在前面生成的c文件中寫程序。
具體如下:

#include 
jstring Java_com_example_hj_hellojni_MainActivity_getJNIString( JNIEnv* env,jobject thiz ) {
    return (*env)->NewStringUTF(env, "Hello JNI !");
}

好了,C語言的編寫也完成了,我們開始運行吧,運行結果如下,成功:
這裡寫圖片描述

思考

1、我們的so庫到哪去了?

運行成功,在工程中搜索so,找到了so文件的目錄:
這裡寫圖片描述
前面是這樣配置gradle的:

    ndk{
        moduleName="hello_jni"
    }//主要是這句話

默認生成所有CPU平台的so庫,所以這兒生成的so庫比較多。
現在我們這樣配置:

//ndk編譯生成.so文件
       ndk {
           moduleName "hello_jni"         //生成的so名字
           abiFilters "armeabi", "armeabi-v7a", "x86"  //輸出指定三種abi體系結構下的so庫。
       }

運行後得到的so庫是這樣的:
這裡寫圖片描述
這兒lib與obj下都是so庫,這兒找到了這樣一句話:

As part of the build process, the files in the libs folder have been stripped of symbols and debugging information. So you'll want to keep two copies of each of your .so files: One from the libs folder to install on the Android device, and one from the obj folder to install for GDB to get symbols from.

也就是說,libs目錄下生成的庫是剝離了符號表與調試信息的,而obj下的庫是帶有調試信息的,我們都可以使用。

2、其他人怎麼使用我寫的so庫?

記得使用百度地圖sdk的時候導入過so庫,好像記得是導入到libs下面。
照著它的樣子,導入自己寫的so庫。
這裡寫圖片描述
新建的一個module,取名test,它的MainActivity的代碼和上面一個的相同,其他什麼都不用管,包名是package com.example.test;。
然後運行,程序崩潰,報錯如下:

09-09 09:38:26.361 1950-1950/com.example.test E/art: No implementation found for java.lang.String com.example.test.MainActivity.getJNIString() (tried Java_com_example_test_MainActivity_getJNIString and Java_com_example_test_MainActivity_getJNIString__)

找不到本地方法的實現,看報錯的原因,它在找本地方法時是根據新建的module的包名找方法的。
so庫中的方法名是Java_com_example_hj_hellojni_MainActivity_getJNIString
而在現在的module中找的是
Java_com_example_test_MainActivity_getJNIString
好吧,想想造成這個的原因是,新module的中調用本地方法時,會根據自己的包名來找本地方法,與so庫中的方法名不同,當然就找不到本地方法的實現了,先這樣做一個測試。

so庫測試一

首先,建一個module,取名testLib,這個module不是app,而是Android Library,建成後,
第一步修改gradle,和上面相同,添加:

//ndk編譯生成.so文件
       ndk {
           moduleName "hello_jni"         //生成的so名字
           abiFilters "armeabi", "armeabi-v7a", "x86"  //輸出指定三種abi體系結構下的so庫。
       }

第二步創建類:

package com.example.testlib;
public class InitLib {
    static {
        System.loadLibrary("hello_jni");
    }
    //加載so庫,這兒的名字要與gradle聲明的相同
    public native String getJNIString();//聲明本地方法
}

第三步創建C語言代碼:步驟、代碼和前面的都相同,注意c語言中的函數名即可。
第四步修改前面的test,將其中的MainActivity修改,注意testLib的引包:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textview = (TextView) findViewById(R.id.hellowjni);

        String s = new InitLib().getJNIString();
        textview.setText(s);
        Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();
    }
}

運行,想要的結果又出來了。
這裡寫圖片描述
來看看我們的testLib
這裡寫圖片描述
這兒它又生成了so庫, 調用時,就是調用的這兒的so庫,別人想用so庫,只要把library的module給他,讓他引入,然後就像test的MainActivity那樣調用就行了。
但是,還沒完,記得使用百度地圖時,百度給的是so庫和jar包, 給的不是library,想想也是,不是開源的,就應該給so庫和jar包,而不是library。
現在,我們再做個實驗。

so庫測試二

將testLib生成的so庫拷貝到test的libs下,結果如下:
這裡寫圖片描述
然後,刪除testLib中gradle中添加的代碼:

//ndk編譯生成.so文件
       ndk {
           moduleName "hello_jni"         //生成的so名字
           abiFilters "armeabi", "armeabi-v7a", "x86"  //輸出指定三種abi體系結構下的so庫。
       }

刪除添加的jni以及C語言文件
再運行試試,報錯了…:

09-09 10:25:49.040 15302-15302/com.example.test E/AndroidRuntime: FATAL EXCEPTION: main
                                                                  Process: com.example.test, PID: 15302
                                                                  java.lang.UnsatisfiedLinkError: com.android.tools.fd.runtime.IncrementalClassLoader$DelegateClassLoader[DexPathList[[dex file "/data/data/com.example.test/files/instant-run/dex/slice-support-annotations-23.4.0_0f6553860408c70fcbf9af741b0e3a91ead8e096-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_9-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_8-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_7-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_6-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_5-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_4-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_3-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_2-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_1-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-slice_0-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-internal_impl-23.4.0_d11d62ff0469a851c3144084b7282ebc60ff68c1-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-com.android.support-support-vector-drawable-23.4.0_286fb307ef2e5e61244113450b8053e8fd8b844e-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-com.android.support-support-v4-23.4.0_df88297ba1abd7fa68c5b2511b53ff29b15c41f3-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-com.android.support-appcompat-v7-23.4.0_7cb187c95178878a86f245c4ef9f5bd7bb029aa1-classes.dex", dex file "/data/data/com.example.test/files/instant-run/dex/slice-com.android.support-animated-vector-drawable-23.4.0_90f456ae3bbbade7285eb6faea927e851bb72c6c-classes.dex"],nativeLibraryDirectories=[/vendor/lib, /system/lib, /vendor/lib, /system/lib]]] couldn't find "libhello_jni.so"
                                                                      at java.lang.Runtime.loadLibrary(Runtime.java:366)
                                                                      at java.lang.System.loadLibrary(System.java:988)
                                                                      at com.example.testlib.InitLib.(InitLib.java:10)
                                                                      at com.example.test.MainActivity.onCreate(MainActivity.java:22)

找不到so文件,怎麼辦,百度,找到這麼一句話:

apk安裝時,系統會把apk中libs目錄下armeabi的SO拷貝到應用的私有目錄下。所以libs裡沒有放入SO,運行時肯定找不到SO。

但是我的確放進去了啊,怎麼回事?
回頭看看百度地圖是怎麼使用so庫的。

在src/main/目錄下新建jniLibs目錄,工程會自動加載src目錄下的so動態庫,放入libBaiduMapSDK_vX_X_X_X.so

哦,原來如此,要放到jniLibs下面啊,那我前面那樣做可以嗎?可以的,只是工程並不會自動加載libs下的so,需在gradle編譯時,通過加入代碼: jniLibs.srcDir ‘libs’ 來說明so的路徑為該libs路徑才可以。
先來試試jniLibs的方法吧,移動文件:
這裡寫圖片描述
移動後,再次運行,又出錯了:

09-09 11:41:08.962 7913-7913/com.example.test E/art: No implementation found for java.lang.String com.example.testlib.InitLib.getJNIString() (tried Java_com_example_testlib_InitLib_getJNIString and Java_com_example_testlib_InitLib_getJNIString__)

又出現這個錯誤了,so庫是找到了,並且需要的函數名稱和我們testLib中的相同,但是找不到函數,唉,可能是編寫C語言時直接將jni拷過去,函數名沒改,導致函數名錯誤,不能偷懶啊!重復前面的工作,生成一次so庫,刪除testLib下的gradle配置,jni,運行成功。

接下來的任務就是將我們的testLib打包成jar就OK了,別人使用時,導入so庫和jar文件就可以了。

總結(純屬個人理解,歡迎指教)

NDK開發,就是用C/C++編寫代碼,使得運行效率更高。編寫的代碼將打包成so庫的形式來供我們使用,加載so庫:

static {
System.loadLibrary(“hello_jni”);
}


在Android中,使用之前需要使用native關鍵字修飾方法,而C語言中實現,它的函數名是由當前引用的包名+方法名,”.”將用”_”代替來命名。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved