android jni开发中遇到的几点问题

:从应用层向下编译so库 html

00. 准备工做

若是你尚未安装NDK:java

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

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

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

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

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

这样就设置好了。app

1. 新建一个Android Studio项目

命名为FactorialDemo,接下来的选项所有默认便可。ide


1-1.png

1-1.png函数

1-2.png

1-2.png布局

1-3.png

1-3.png测试

1-4.png

1-4.pnggradle

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


1-5.png

1-5.png

2. 新建Factorial.java类

2-1.png

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

3-1.png

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

3-2.png

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

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

  };

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 :=ai_nixie_aiden_factorialdemo_Factorial
include $(BUILD_SHARED_LIBRARY)

6. 编写Application.mk文件

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

APP_PLATFORM := android-14
APP_ABI :=all

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

点击右上角出现的Sync Now

6-1.png

6-1.png

7. 编译so库文件

此时,在Terminal窗口中执行ndk-build,就能够获得编译的so文件。

8. 连接C++代码和Gradle

若是如今就编译整个项目,会获得下面的错误。

8-1.png

8-1.png

8-2.png

8-2.png

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

8-3.png

8-3.png

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

8-4.png

8-4.png

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

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

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

9. 导入库文件

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

static {
    System.loadLibrary("ai_nixie_aiden_factorialdemo_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("ai_nixie_aiden_factorialdemo_Factorial");
    }
}

好了,如今先测试一下,应该能够正常编译了。不过咱们如今尚未在咱们的APP中用上so库的功能。

9-1.png

9-1.png

9-2.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-1.png

完成后的项目树:


11-2.png

11-2.png


二:直接用ndk-build编译so库

      当咱们有C文件须要编译成so库时,在工程新建一jni目录,将C文件复制到这个目录下,而后在添加一个Android.mk文件,其中须要这几行

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    :=  led  //生成so库的名字
LOCAL_SRC_FILES := ledjni.c  //C文件的名字

include $(BUILD_SHARED_LIBRARY)

而后cmd进入jni目录下,ndk-build编译便可。编译完成后会在当前目录下生成一个 libs文件夹,里面存放的是编译好的so库。

相关文章
相关标签/搜索