靠譜Android Studio JNI Demo

前言

作爲工作了近四年時間的Android開發,搞通這個Demo用了一個工作日,覺得自己動手能力有些差,所以記錄下。這裏我們不介紹JNI的基礎理論知識。只講Demo流程。

Demo

第一步

創建Demo工程,從SDK Manager中下載NDK相關組件,同時將Support Repository也下載下來

第二步

創建Java文件,如下:

package com.example.jnidemo;

public class JNITest {

    public native String getStrFromJNI();
    
}

第三步

使用javah命令生成JNITest的頭文件。我就是這步卡主的,在使用javah命令過程中遇到了很多問題。例如 javah不是命令、無法識別的文件名、找不到類文件。這裏我說下我這裏如何跑通的。注意以下幾點。

  • 1、在Android Studio的終端中使用Javah如果報不是內部或外部命令,也不是可運行的程序。則在cmd命令行中執行,只要環境變量配置無誤,一定會識別javah命令

  • 2、在生成的classes文件夾目錄下使用命令,在源文件目錄下執行會報找不到xxx的類文件

  • 3、之前class文件目錄是在build/intermediates/classes/debug下。我一度懷疑沒有生成class文件。後來發現目錄是:build/intermediates/javac/debug/complieDebugJavaWithJavac/classes下。那麼我們就在如下目錄下執行如下命令:

javah -jni com.example.jnidemo.JNITest

如下:
命令執行截圖
生成的.h文件就在同目錄下,如下:

生成.h文件截圖

第四步

在src/main/下建立jni文件下,jni文件夾與java文件是平行的。建立方法如下:

創建jni文件

第五步

將剛剛生成的.h文件複製到jni目錄下,並編寫其實現類,文件名自取,我們這裏就叫demo.c。如下:

//
// Created by xxx on 2019/6/17.
//
#include<jni.h>
//導入頭文件
#include "com_example_jnidemo_JNITest.h"  
//實現.h文件中定義的方法
jstring Java_com_example_jnidemo_JNITest_getStrFromJNI(JNIEnv *env,jobject thiz){
    return (*env)->NewStringUTF(env,"I am Str from jni libs!");
}

第六步

在jni文件夾下建立Android.mk文件用於編譯,如下:

#my-dir:返回當前 Android.mk 所在的目錄的路徑
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

# 定義生成so文件的名字
LOCAL_MODULE := hello-jni
# 需要編譯的文件,由於在一個文件夾下,不用寫路徑
LOCAL_SRC_FILES := demo.c

include $(BUILD_SHARED_LIBRARY)
# 指定生成全部的ABI的so文件
APP_ABI := all

第七步

在app.gradle中android{}內添加ndk相關配置,如下:

externalNativeBuild {
        ndkBuild {
            path 'src/main/jni/Android.mk'
        }
    }

第八步

make project即可看到生成的so文件,如下:

生成so截圖

可以看到每個ABI均生成了so文件,但是so文件的名字多了個前綴lib,這是因爲Android強制要求所有的so文件必須是lib前綴,我們可以看到例如高德地圖、融雲聊天等第三方的so庫也是如此

第九步

加載so文件並使用,如下:

package com.example.jnidemo;

public class JNITest {
    static {
        System.loadLibrary("jni-demo");//與Android.mk文件中設置的一致,不需要手動添加前綴lib
    }
    public native String getStrFromJNI();
}
package com.example.jnidemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new JNITest();
        ((TextView)findViewById(R.id.tv_info)).setText(new JNITest().getStrFromJNI());
    }
}

文件目錄

文件目錄截圖

寫在後邊

這裏只介紹了最基礎最簡單的jni使用方法,還有如CMake等方式沒有調研。後邊有時間會陸續更新。如照着這個Demo跑不通可留言共同探討。網上還有另一種方式,不寫Android.mk文件,而是在app.gradle的defaultConfig{}下添加ndk設置,如下:

 ndk {
            moduleName "NdkJniDemo"          //生成的so名字
            abiFilters "armeabi-v7a", "x86","arm64-v8a","x86_64" //輸出指定三種abi體系結構下的so庫,目前可有可無。
        }

我並沒有跑通。沒有繼續糾結。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章