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

本系列文章如下:

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

樣式大概如下:


image.png

里面有兩個(gè)輸入框,下面有4個(gè)按鈕匀归,代表加減乘除项鬼,最下面有個(gè)TextView蜻直,表示結(jié)果饵逐。

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

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);


}

然后倍权,我們先用來看下對(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í)候掷豺,我們來看下對(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);
    }
}

由于我們不是通過javah來生成.c文件,所以我們要?jiǎng)?chuàng)建一個(gè)jni的文件夾薄声,然后創(chuàng)建一個(gè)jnitools.c文件当船。這時(shí)候jnitools.c文件里面應(yīng)該什么都沒有,我們看到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方法,然后我們寫出這些方法的具體實(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è)方法的步驟蒸绩,我們要重寫JNI_OnLoad(JavaVM* vm, void* reserved)函數(shù)。所以我們?cè)?code>jnitools.c中重寫函數(shù)這個(gè)函數(shù)铃肯,如下:

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

有時(shí)候患亿,我們要打印日志,來幫助我們識(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í)候咙冗,開始注冊(cè)代碼,我們開始編寫注冊(cè)代碼

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

    //打印日志漂彤,說明已經(jīng)進(jìn)來了
    __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");

    //開始注冊(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ǔ)充一下挫望,很多人立润,最后失敗,都是簽名的問題媳板,可以建議使用java -p 命令行來對(duì)比簽名桑腮,我這是這樣操作的。首先打開JNITools.class蛉幸,如下圖:

image.png

然后打開Android Studio里面的Terminal破讨,然后拖拽JNITools.classTerminal丛晦。這樣Terminal就自動(dòng)打開了,然后我們?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)系作者
  • 序言:七十年代末乏冀,一起剝皮案震驚了整個(gè)濱河市蝶糯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辆沦,老刑警劉巖拼余,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赖钞,死亡現(xiàn)場(chǎng)離奇詭異伤溉,居然都是意外死亡仰美,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門蔚晨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乍钻,“玉大人,你說我怎么就攤上這事铭腕∫瘢” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵累舷,是天一觀的道長(zhǎng)浩考。 經(jīng)常有香客問我,道長(zhǎng)被盈,這世上最難降的妖魔是什么析孽? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮只怎,結(jié)果婚禮上袜瞬,老公的妹妹穿的比我還像新娘。我一直安慰自己尝盼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布佑菩。 她就那樣靜靜地躺著盾沫,像睡著了一般裁赠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上赴精,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天佩捞,我揣著相機(jī)與錄音,去河邊找鬼蕾哟。 笑死一忱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的谭确。 我是一名探鬼主播帘营,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼逐哈!你這毒婦竟也來了芬迄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤昂秃,失蹤者是張志新(化名)和其女友劉穎禀梳,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肠骆,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡算途,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蚀腿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘴瓤。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖唯咬,靈堂內(nèi)的尸體忽然破棺而出纱注,到底是詐尸還是另有隱情,我是刑警寧澤胆胰,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布狞贱,位于F島的核電站,受9級(jí)特大地震影響蜀涨,放射性物質(zhì)發(fā)生泄漏瞎嬉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一厚柳、第九天 我趴在偏房一處隱蔽的房頂上張望氧枣。 院中可真熱鬧,春花似錦别垮、人聲如沸便监。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)烧董。三九已至毁靶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逊移,已是汗流浹背预吆。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胳泉,地道東北人拐叉。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像扇商,于是被迫代替她去往敵國(guó)和親凤瘦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容