Android JNI學(xué)習(xí)(五)——Demo演示

本系列文章如下:

我們這里做一個(gè)簡(jiǎn)單的計(jì)算器demo,其中運(yùn)算的邏輯由Native實(shí)現(xiàn),而且我們采用動(dòng)態(tài)注冊(cè)的方式來(lái)實(shí)現(xiàn)

樣式大概如下:


image.png

里面有兩個(gè)輸入框,下面有4個(gè)按鈕,代表加減乘除,最下面有個(gè)TextView,表示結(jié)果。

我們把運(yùn)算的邏輯抽象出來(lái),用一個(gè)JNITools來(lái)表示。代碼如下:

public class JNITools {

    static {
        System.loadLibrary("jnidemo3");
    }

    //加法
    public static native int  add(int a,int b);

    //減法
    public static native int sub(int a,int b);

    //乘法
    public static native int mul(int a,int b);

    //除法
    public static native int div(int a,int b);


}

然后,我們先用來(lái)看下對(duì)應(yīng)的布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.gebilaolitou.jni.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <EditText
            android:id="@+id/inputa"
            android:hint="請(qǐng)輸入a"
            android:inputType="number"
            android:layout_weight="1.0"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />


        <EditText
            android:inputType="number"
            android:id="@+id/inputb"
            android:hint="請(qǐng)輸入b"
            android:layout_weight="1.0"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />



    </LinearLayout>

    <TextView
        android:text="請(qǐng)選擇符號(hào)"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">


    <Button
        android:gravity="center"
        android:layout_weight="1.0"
        android:text="@string/add"
        android:id="@+id/add"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:gravity="center"
        android:layout_weight="1.0"
        android:text="@string/sub"
        android:id="@+id/sub"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:gravity="center"
        android:layout_weight="1.0"
        android:text="@string/mul"
        android:id="@+id/mul"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:gravity="center"
        android:layout_weight="1.0"
        android:text="@string/div"
        android:id="@+id/div"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    </LinearLayout>

    <TextView
        android:id="@+id/result"
        android:text="計(jì)算結(jié)果"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

這時(shí)候,我們來(lái)看下對(duì)應(yīng)的MainActivity

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private Button btnAdd,btnSub,btnMul,btnDiv;
    private EditText inputA,inputB;
    private TextView tvResult;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setupView();
        addListener();
    }

    private void addListener() {
        btnAdd.setOnClickListener(this);
        btnDiv.setOnClickListener(this);
        btnMul.setOnClickListener(this);
        btnSub.setOnClickListener(this);
    }

    private void setupView() {
        btnAdd=this.findViewById(R.id.add);
        btnDiv=this.findViewById(R.id.div);
        btnMul=this.findViewById(R.id.mul);
        btnSub=this.findViewById(R.id.sub);

        inputA=this.findViewById(R.id.inputa);
        inputB=this.findViewById(R.id.inputb);

        tvResult=this.findViewById(R.id.result);
    }


    @Override
    public void onClick(View v) {
        double result=0;
        String strA=inputA.getText().toString();
        String strB=inputB.getText().toString();
        int a=Integer.parseInt(strA);
        int b=Integer.parseInt(strB);
        switch (v.getId()){
            case R.id.add:
                result=JNITools.add(a,b);
                break;
            case R.id.div:
                result=JNITools.div(a,b);
                break;
            case R.id.mul:
                result=JNITools.mul(a,b);
                break;
            case R.id.sub:
                result=JNITools.sub(a,b);
                break;
        }
        tvResult.setText(""+result);
    }
}

由于我們不是通過(guò)javah來(lái)生成.c文件,所以我們要?jiǎng)?chuàng)建一個(gè)jni的文件夾,然后創(chuàng)建一個(gè)jnitools.c文件。這時(shí)候jnitools.c文件里面應(yīng)該什么都沒(méi)有,我們看到JNITools這個(gè)文件有4個(gè)native方法,所以我們要也要在jnitools.c里面聲明這4個(gè)方法。如下:

jint addNumber(JNIEnv *env,jclass clazz,jint a,jint b);

jint subNumber(JNIEnv *env,jclass clazz,jint a,jint b);

jint mulNumber(JNIEnv *env,jclass clazz,jint a,jint b);

jint divNumber(JNIEnv *env,jclass clazz,jint a,jint b);

依次對(duì)應(yīng)Java層的native方法,然后我們寫(xiě)出這些方法的具體實(shí)現(xiàn),如下;

jint addNumber(JNIEnv *env,jclass clazz,jint a,jint b){
     return a+b;
}

jint subNumber(JNIEnv *env,jclass clazz,jint a,jint b){
     return a-b;
}

jint mulNumber(JNIEnv *env,jclass clazz,jint a,jint b){
     return a*b;
}

jint divNumber(JNIEnv *env,jclass clazz,jint a,jint b){
     return a/b;
}

最后,然后加入引用,如下:

#include <jni.h>
#include <stdio.h>
#include <stdlib.h>

依照前面動(dòng)態(tài)注冊(cè)方法的步驟,我們要重寫(xiě)JNI_OnLoad(JavaVM* vm, void* reserved)函數(shù)。所以我們?cè)?code>jnitools.c中重寫(xiě)函數(shù)這個(gè)函數(shù),如下:

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){
    return JNI_VERSION_1_6;
}

有時(shí)候,我們要打印日志,來(lái)幫助我們識(shí)別是否進(jìn)入這個(gè)方法,所以我們要配置一個(gè)log,這時(shí)候,我們創(chuàng)建一個(gè)Android.mk文件,然后進(jìn)行如下的編輯:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := jnidemo3

LOCAL_SRC_FILES := jnitools.c

LOCAL_LDLIBS :=  -L$(SYSROOT)/usr/lib -llog

include $(BUILD_SHARED_LIBRARY)

然后在jnitools.c添加#include <android/log.h>代碼

這時(shí)候,開(kāi)始注冊(cè)代碼,我們開(kāi)始編寫(xiě)注冊(cè)代碼

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){

    //打印日志,說(shuō)明已經(jīng)進(jìn)來(lái)了
    __android_log_print(ANDROID_LOG_DEBUG,"JNITag","enter jni_onload");

    JNIEnv* env = NULL;
    jint result = -1;

    // 判斷是否正確
    if((*vm)->GetEnv(vm,(void**)&env,JNI_VERSION_1_6)!= JNI_OK){
        return result;
    }

    //注冊(cè)四個(gè)方法,注意簽名
    const JNINativeMethod method[]={
            {"add","(II)I",(void*)addNumber},
            {"sub","(II)I",(void*)subNumber},
            {"mul","(II)I",(void*)mulNumber},
            {"div","(II)I",(void*)divNumber}
    };
    
    //找到對(duì)應(yīng)的JNITools類
    jclass jClassName=(*env)->FindClass(env,"com/gebilaolitou/jni/JNITools");

    //開(kāi)始注冊(cè)
    jint ret = (*env)->RegisterNatives(env,jClassName,method, 4);

     //如果注冊(cè)失敗,打印日志
    if (ret != JNI_OK) {
        __android_log_print(ANDROID_LOG_DEBUG, "JNITag", "jni_register Error");
        return -1;
    }

    return JNI_VERSION_1_6;
}

這里補(bǔ)充一下,很多人,最后失敗,都是簽名的問(wèn)題,可以建議使用java -p 命令行來(lái)對(duì)比簽名,我這是這樣操作的。首先打開(kāi)JNITools.class,如下圖:

image.png

然后打開(kāi)Android Studio里面的Terminal,然后拖拽JNITools.classTerminal。這樣Terminal就自動(dòng)打開(kāi)了,然后我們?cè)?code>Terminal里面輸入pwd,就已經(jīng)在這個(gè)目錄里面了。然后我們?cè)?code>Terminal輸入如下代碼

javap -s JNITools.class 

Terminal就會(huì)輸出這個(gè)類的所有簽名,如下:

Compiled from "JNITools.java"
public class com.gebilaolitou.jni.JNITools {
  public com.gebilaolitou.jni.JNITools();
    descriptor: ()V

  public static native int add(int, int);
    descriptor: (II)I

  public static native int sub(int, int);
    descriptor: (II)I

  public static native int mul(int, int);
    descriptor: (II)I

  public static native int div(int, int);
    descriptor: (II)I

  static {};
    descriptor: ()V
}

我們?cè)谶@里核對(duì)下我們注冊(cè)方法的簽名。
最后,我們?cè)趯?duì)應(yīng)的build.gradle里面defaultConfig里面添加如下代碼:

        ndk{
            moduleName "jnidemo3"
            abiFilters 'x86','armeabi-v7a','arm64-v8a'
            ldLibs "log"
        }

這時(shí)候我們目錄結(jié)構(gòu)如下:

image.png

jnitools.c的內(nèi)容如下:

#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <stdlib.h>


jint addNumber(JNIEnv *env,jclass clazz,jint a,jint b);

jint subNumber(JNIEnv *env,jclass clazz,jint a,jint b);

jint mulNumber(JNIEnv *env,jclass clazz,jint a,jint b);

jint divNumber(JNIEnv *env,jclass clazz,jint a,jint b);





JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){

    __android_log_print(ANDROID_LOG_DEBUG,"JNITag","enter jni_onload");

    JNIEnv* env = NULL;
    jint result = -1;

    if((*vm)->GetEnv(vm,(void**)&env,JNI_VERSION_1_6)!= JNI_OK){
        return result;
    }

    const JNINativeMethod method[]={
            {"add","(II)I",(void*)addNumber},
            {"sub","(II)I",(void*)subNumber},
            {"mul","(II)I",(void*)mulNumber},
            {"div","(II)I",(void*)divNumber}
    };
    jclass jClassName=(*env)->FindClass(env,"com/gebilaolitou/jni/JNITools");

    jint ret = (*env)->RegisterNatives(env,jClassName,method, 4);

    if (ret != JNI_OK) {
        __android_log_print(ANDROID_LOG_DEBUG, "JNITag", "jni_register Error");
        return -1;
    }

    return JNI_VERSION_1_6;
}


jint addNumber(JNIEnv *env,jclass clazz,jint a,jint b){
     return a+b;
}

jint subNumber(JNIEnv *env,jclass clazz,jint a,jint b){
     return a-b;
}

jint mulNumber(JNIEnv *env,jclass clazz,jint a,jint b){
     return a*b;
}

jint divNumber(JNIEnv *env,jclass clazz,jint a,jint b){
     return a/b;
}

至此,整個(gè)代碼已經(jīng)全部完成。我們可以運(yùn)行下試試。其實(shí)挺簡(jiǎn)單的。

github地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容