美文网首页NDK
学习JNI编程第1篇--写一个FactorialDemo APP

学习JNI编程第1篇--写一个FactorialDemo APP

作者: 汶水一方 | 来源:发表于2017-08-10 10:16 被阅读2017次

    作者:汶水一方
    2017.08.09

    本文软硬件环境
    MacBook Pro, OS X El Capitan, 10.11.6
    Android Studio v2.3

    2017.11.1更新:
    本文的ndk方法已经被更好用的cmake方法取代。cmake方法会另外撰文介绍。

    0. 计划

    0-1.png

    00. 准备工作

    如果你还没有安装NDK:

    1. 下载,然后解压。无需安装。
    https://developer.android.com/ndk/downloads/index.html#stable-downloads
    

    解压得到android-ndk-r15c目录,记住路径。主要需要它下面的ndk目录及文件。

    1. 设置PATH
      编辑~/.bash_profile文件,加入这样一行(要用到上面的解压路径):
    PATH=$PATH:/Downloads/android-ndk-r15c/ndk
    

    然后,执行source ~/.bash_profile,使之生效。

    1. 执行:
    ln -s /Downloads/android-ndk-r15c/ndk ndk
    

    这样就设置好了。

    1. 新建一个Android Studio项目

    命名为FactorialDemo,接下来的选项全部默认即可。


    1-1.png 1-2.png 1-3.png 1-4.png

    把视图切换到Project,下图中标记出来的是要修改的几个文件,当然我们还要创建几个文件:


    1-5.png

    2. 新建Factorial.java类

    2-1.png

    内容如下:

    package ai.nixie.aiden.factorialdemo;
    public class Factorial {
        public static long fac(long n){
            return n <=0? 1 : n * fac(n-1);
        }
        public native static long facNTV(long n);
    }
    

    3. 从java类文件生成头文件(ai_nixie_aiden_factorialdemo_Factorial.h)

    点击窗口下方的Terminal,打开命令行窗口,切换到app/src/main/目录,然后执行命令生成头文件。

    cd app/src/main/
    javah -jni -classpath java/ -d jni/ ai.nixie.aiden.factorialdemo.Factorial
    

    注意:ai.nixie.aiden.factorialdemo.Factorial
    前面ai.nixie.aiden.factorialdemo是package包名,全部小写。最后的Factorial是上面创建的Factorial的类名,注意区分大小写哦!否则会报错。

    执行结果如下图。

    3-1.png

    如果没有看到任何提示,说明运行成功啦。
    这时再看左侧的项目树,发现多一个jni文件夹,展开里面就是我们刚生成的头文件啦。

    3-2.png

    4. 生成c文件(ai_nixie_aiden_factorialdemo_Factorial.c)

    现在选中ai_nixie_aiden_factorialdemo_Factorial.h文件,按Command + C复制,接着按Command + V粘贴,弹出如下对话框。

    4-1.png

    把文件名最后的h改为c。点OK
    现在的jni文件夹就有2个文件了。

    4-2.png

    接下来修改C文件(ai_nixie_aiden_factorialdemo_Factorial.c)的内容为:

    #include <ai_nixie_aiden_factorialdemo_Factorial.h>
    
    static jlong fac(long n) {
        return n<=0 ? 1 : n * fac(n - 1);
    }
    
    JNIEXPORT jlong JNICALL Java_ai_nixie_aiden_factorialdemo_Factorial_facNTV
      (JNIEnv *env, jclass clazz, jlong n){
    
        return fac(n);
    
      };
    
    

    注意!!!fac函数必须放在前面!顺序不能反!否则会提示找不到!

    5. 编写Android.mk文件

    jni文件夹下新建一个文件,名字为Android.mk,内容如下:

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_SRC_FILES :=ai_nixie_aiden_factorialdemo_Factorial.c
    LOCAL_MODULE :=Factorial
    include $(BUILD_SHARED_LIBRARY)
    

    6. 编写Application.mk文件

    jni文件夹下新建一个文件,命名为Application.mk,内容如下:

    APP_PLATFORM := android-14
    APP_ABI :=arm64-v8a armeabi-v7a
    

    如果不加的话,会提示Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-14.

    点击右上角出现的Sync Now

    6-1.png

    7. 编译so库文件

    此时,在Terminal窗口中执行ndk-build,就可以得到编译的so文件。

    $ ndk-build
    Android NDK: WARNING: APP_PLATFORM android-14 is higher than android:minSdkVersion 1 in ./AndroidManifest.xml. NDK binaries will *not* be comptible with devices older than android-14. See https://android.googlesource.com/platform/ndk/+/master/docs/user/common_problems.md for more information.    
    [arm64-v8a] Compile        : Factorial <= ai_nixie_aiden_factorialdemo_Factorial.c
    [arm64-v8a] SharedLibrary  : libFactorial.so
    [arm64-v8a] Install        : libFactorial.so => libs/arm64-v8a/libFactorial.so
    [x86_64] Compile        : Factorial <= ai_nixie_aiden_factorialdemo_Factorial.c
    [x86_64] SharedLibrary  : libFactorial.so
    [x86_64] Install        : libFactorial.so => libs/x86_64/libFactorial.so
    [mips64] Compile        : Factorial <= ai_nixie_aiden_factorialdemo_Factorial.c
    [mips64] SharedLibrary  : libFactorial.so
    [mips64] Install        : libFactorial.so => libs/mips64/libFactorial.so
    [armeabi-v7a] Compile thumb  : Factorial <= ai_nixie_aiden_factorialdemo_Factorial.c
    [armeabi-v7a] SharedLibrary  : libFactorial.so
    [armeabi-v7a] Install        : libFactorial.so => libs/armeabi-v7a/libFactorial.so
    [armeabi] Compile thumb  : Factorial <= ai_nixie_aiden_factorialdemo_Factorial.c
    [armeabi] SharedLibrary  : libFactorial.so
    [armeabi] Install        : libFactorial.so => libs/armeabi/libFactorial.so
    [x86] Compile        : Factorial <= ai_nixie_aiden_factorialdemo_Factorial.c
    [x86] SharedLibrary  : libFactorial.so
    [x86] Install        : libFactorial.so => libs/x86/libFactorial.so
    [mips] Compile        : Factorial <= ai_nixie_aiden_factorialdemo_Factorial.c
    [mips] SharedLibrary  : libFactorial.so
    [mips] Install        : libFactorial.so => libs/mips/libFactorial.so
    
    

    8. 链接C++代码和Gradle

    如果现在就编译整个项目,会得到下面的错误。

    8-1.png 8-2.png

    jni文件夹下的任意文件上右击,选择Link C++ Project with Gradle

    8-3.png

    选择ndk-build,并找到并选择它的Android.mk文件,然后OK。

    8-4.png

    执行完这一步后,在build.gradle文件中android下面多了几行:

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

    其实直接在这个文件中加入这几行应该就可以了。

    9. 导入库文件

    在Factorial.java文件中,加入导入库文件的代码,

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

    完成后如下:

    package ai.nixie.aiden.factorialdemo;
    public class Factorial {
        public static long fac(long n){
            return n <=0? 1 : n * fac(n-1);
        }
        public native static long facNTV(long n);
        static {
            System.loadLibrary("Factorial");
        }
    }
    

    好了,现在先测试一下,应该可以正常编译了。不过我们现在还没有在我们的APP中用上so库的功能。

    9-1.png 9-2.png

    10. 完善APP,验证我们的so库

    10.1 修改布局文件

    修改res/layout/activity_main.xml文件,改为LinearLayout布局,加入3个控件,一个输入框用于输入数字,一个文本框用于显示结果,一个按钮。

    完成后如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        >
    
        <EditText
            android:id="@+id/input"
            android:text="5"
            android:textSize="32dp"
            android:textAlignment="center"
            android:selectAllOnFocus="true"
            android:inputType="text"
            android:maxLines="1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <requestFocus />
        </EditText>
    
        <TextView
            android:id="@+id/result"
            android:textSize="32dp"
            android:textAlignment="center"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="result"
            />
    
        <Button
            android:id="@+id/calculate"
            android:text="Calculate"
            android:textSize="32dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
    </LinearLayout>
    

    10.2 修改主java文件MainActivity.java

    主要的几个修改点:

    • 加入implements监听Click事件。
    • 获得3个控件,并为按钮加入Click事件监听。
    • 在onClick函数中,实现点击时计算阶乘的功能。其中用到了:
      • 判断字符串是否为空
      • String转换成Long
    • 创建了Factorial的一个实例,并调用它的方法来实现阶乘的功能。
    • 将阶乘的计算结果,进行字符串格式化后,显示在文本框中。

    修改完成的MainActivity.java文件为:

    package ai.nixie.aiden.factorialdemo;
    
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.text.TextUtils;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.TextView;
    
    public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    
        private EditText inputBox;
        private TextView tvResult;
        private Button calButton;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            inputBox = (EditText) findViewById(R.id.input);
            tvResult = (TextView) findViewById(R.id.result);
            calButton = (Button) findViewById(R.id.calculate);
    
            calButton.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View view) {
    
            String in = inputBox.getText().toString();
    
            if (TextUtils.isEmpty(in)) {
                return;
            }
            long input = Long.parseLong(in);
    
            /*  These two lines are the most important! */
            Factorial myFactorial = new Factorial();
            long result = myFactorial.facNTV(input);
            /*  These two lines are the most important! */
    
            tvResult.setText(String.format("fac(%d)=%d", input, result));
    
        }
    }
    

    其中最主要的是这2句:

    Factorial myFactorial = new Factorial(); //生成一个Factorial类的实例,
    long result = myFactorial.facNTV(input); //然后调用它的facNTV或fac方法,来计算阶乘。
    

    11. 编译测试

    到此项目完成。实际的运行结果图:


    11-1.png

    完成后的项目树:


    11-2.png

    相关文章

      网友评论

        本文标题:学习JNI编程第1篇--写一个FactorialDemo APP

        本文链接:https://www.haomeiwen.com/subject/lwncrxtx.html