Android JNI开发一: 如何生成SO库

Posted 长沙火山

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android JNI开发一: 如何生成SO库相关的知识,希望对你有一定的参考价值。

目录

        Android JNI开发一: 如何生成SO库

        Android JNI开发二: SO库的使用​​​​​​​

一、JNI

1.1 创建工程

androidStudio 创建一个新的工程,我的 AndroidStudio 版本为4.2.2。创建工程第一步的时候,需要选择模版,请选择 Native C++ 这个模版。这个模版为我们提供了JNI开发的环境,我们在这个模版里可以更容易的使用JNI 去调用C语言代码。

(1) 创建工程第一步:选择 Native C++ 模版,如下图所示:

(2) 创建工程第二步:填写项目名称、项目存放目录,如下图所示:

(3) 创建工程第三步:选择语言版本,选择默认的即可,如下图所示:

至此,项目创建完毕,项目中CPP目录就是编写C语言代码的地方。整个目录如下图所示:

1.2 下载JNI相关工具

打开 SDK Manager 对话框, AndroidStudio -> Tools -> SDK Manager,如下图所示:

下载 NDK 和CMake 工具,如下图所示:

1.3 编写C语言代码

接下来,我们要做一个简单的计算器,实现最基本的加法、减法、乘法、除法。加减乘除的计算功能用C语言实现,界面控件布局等则用 Java语言实现,然后采用 JNI 的方式去调用C语言里的加减乘除方法。

(1) 在和 MainActivity 同级目录下新建一个 java 类,类名为:JNITools。

package com.example.jnidemo;

public class JNITools {

    // 这个链接C语言文件的代码本来是在MainActivity里面,我挪到了这里来了,照做即可。
    static {
        System.loadLibrary("native-lib");
    }

		//自己添加加减乘除四个方法的声明,具体实现在native-lib.cpp文件里。
    public static native int addNumber(int a, int b);
    public static native int subNumber(int a, int b);
    public static native int mulNumber(int a, int b);
    public static native int divNumber(int a, int b);
}

(2) 在 native-lib.cpp 里面实现加减乘除方法。

添加完上面四个方法之后,会发现四个方法都变红了。因为只是声明了方法,没有实现方法,所以当然会报错。不过不要慌,将这四个方法实现一下即可。将鼠标移动到飙红的方法上面,然后 Alt + Enter,会发现代码正常了。这个时候,我们点开 native-lib.cpp 文件,会发现系统自动帮我们实现了这个方法,只是方法体里面是空的,需要自己去实现你想要的业务逻辑。

敲黑板,重点来了。 到了这里,有的同学可能会感觉奇怪,为什么我在JNITools里面声明方法,方法的具体实现却跑到了native-lib.cpp文件里。因为仔细看上面的代码,是这句代码 System.loadLibrary("native-lib"); 把这两个文件关联到了一起。在ios里面有头文件和源文件之分,JNITools.java 就相当于头文件,native-lib 就相当于源文件。

打开 native-lib.cpp,在方法体里面添加自己的业务逻辑代码:

#include <jni.h>
#include <string>

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_jnidemo_MainActivity_sum(JNIEnv *env, jobject thiz, jint a, jint b) {
    return a+b;
}extern "C"
JNIEXPORT jint JNICALL
Java_com_example_jnidemo_JNITools_addNumber(JNIEnv *env, jclass clazz, jint a, jint b) {
    return a+b;
}extern "C"
JNIEXPORT jint JNICALL
Java_com_example_jnidemo_JNITools_subNumber(JNIEnv *env, jclass clazz, jint a, jint b) {
    return a-b;
}extern "C"
JNIEXPORT jint JNICALL
Java_com_example_jnidemo_JNITools_mulNumber(JNIEnv *env, jclass clazz, jint a, jint b) {
    return a*b;
}extern "C"
JNIEXPORT jint JNICALL
Java_com_example_jnidemo_JNITools_divNumber(JNIEnv *env, jclass clazz, jint a, jint b) {
    return a/b;
}

至此,我们的C语言代码就写好了。JNITools里的方法是提供给安卓代码调用的,native-lib.cpp里面的代码最终会被编译成so库。

1.4 安卓代码调用C语言

下面,就回到了安卓这部分,这部分都是大家比较熟悉的,就加快进度直接上代码。

(1) activity_main.xml 里添加布局代码:

package com.example.jnidemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import com.example.jnidemo.databinding.ActivityMainBinding;

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.addNumber(a,b);
                break;
            case R.id.div:
                result=JNITools.divNumber(a,b);
                break;
            case R.id.mul:
                result=JNITools.mulNumber(a,b);
                break;
            case R.id.sub:
                result=JNITools.subNumber(a,b);
                break;
        }
        tvResult.setText(""+result);
    }
}

(2) string.xml 里添加代码:

<resources>
    <string name="app_name">JniDemo</string>
    <string name="add">相加</string>
    <string name="sub">相减</string>
    <string name="mul">相乘</string>
    <string name="div">相除</string>
</resources>

(3) MainActivity 里添加代码:

package com.example.jnidemo;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

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);
      
        //这里就是通过JNI调用C语言的代码
        switch (v.getId()){
            case R.id.add:
                result=JNITools.addNumber(a,b);
                break;
            case R.id.div:
                result=JNITools.divNumber(a,b);
                break;
            case R.id.mul:
                result=JNITools.mulNumber(a,b);
                break;
            case R.id.sub:
                result=JNITools.subNumber(a,b);
                break;
        }
        tvResult.setText(""+result);
    }
}

(4) 运行效果

1.5 生成SO库

前面就提到过native-lib.cpp最终会被编译成so库,提供给普通的安卓程序使用。那么怎么将native-lib.cpp编译成so库呢?其实so库被存放在apk里面了,解压apk文件,即可得到so库。

以上是关于Android JNI开发一: 如何生成SO库的主要内容,如果未能解决你的问题,请参考以下文章

Android JNI开发二: SO库的使用

Android JNI开发三: SO库的使用

Android JNI开发四: 鸿蒙JNI开发

Android项目中JNI技术生成并调用.so动态库实现详解

Android so库开发——使用Studio调用so库

Android JNI你应该知道的