NDK交叉编译学习

目录

什么是交叉编译

为什么需要交叉编译

动态库与静态库的区别

动态库

静态库

编译动态库

环境准备

linux环境

下载ndk

编译代码

 编写android代码

添加动态库

build.gradle修改

java代码

jni代码

cmake代码

 遇到的问题

记录一下自己的学习过程

什么是交叉编译

交叉编译是在一个平台上生成另一个平台上的可执行代码。同一个体系结构可以运行不同的操作系统;同样,同一个操作系统也可以在不同的体系结构上运行。

举例来说,我们常说的x86 Linux平台实际上是Intel x86体系结构和Linux for x86操作系统的统称;而x86 WinNT平台实际上是Intel x86体系结构和Windows NT for x86操作系统的简称

为什么需要交叉编译

在android中如果想要使用类似于FFmpeg、opencv、openGL等开源的库,就需要用到交叉编译。同时一些比较大的公司,也会将一些核心模块打成动态库,给其多个应用去使用,也需要交叉编译成对应平台的可执行文件。

动态库与静态库的区别

动态库

通常以lib开头,以.so作为后缀名,在程序运行的时候,才会去加载动态库。

静态库

静态库命名方式区别于动态库,通常在编译的时候,会把静态库打到主包里面,所以解压安装包查看的时候,看不到静态库。

编译动态库

环境准备

linux环境

编译动态库需要一个linux环境,我是用VM ware安装了一个ubuntu16的虚拟机。

下载ndk

下载linux使用的ndk: NDK 下载  |  Android NDK  |  Android Developers

我下载的是最新的 android-ndk-r25c

编译代码

我这边写了一个简单的例子,两数相加。

add.h

#include<stdio.h>

int add(int a,int b);

add.c

#include "add.h"
int add(int a,int b){
 return a+b;
}

现在ndk的交叉编译是使用的clang,而不是gcc,需要先配置一下编译器的路径

添加到 /etc/profile里面

export NDK="/home/user/ndk/android-ndk-r25c"
export NDK_CLANG="$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi28-clang"

 使用命令  $NDK_CLANG -fPIC -shared add.c -o libaddndk.so  编译生成动态库

 编写android代码

添加动态库

在android代码中的main目录下面,新建jniLibs文件夹,在jniLibs下面需要根据手机的平台,添加对应的文件夹

对应平台的获取,可以通过下面命令获取

adb shell getprop ro.product.cpu.abi

build.gradle修改

需要在build.gradle里面添加对应的cmake和ndk的平台配置

在android的defaultCongfig中添加


        externalNativeBuild {
            cmake {
                // cppFlags "" 默认包含四大平台
                abiFilters "armeabi-v7a"
            }
        }
        ndk {
            abiFilters "armeabi-v7a"
        }

java代码

添加一个native方法


public class MainActivity extends AppCompatActivity {

    // Used to load the 'ndktest' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        // Example of a call to a native method
        TextView tv = binding.sampleText;
        tv.setText(" 3 + 5 = " + add(3,5));
    }

    /**
     * A native method that is implemented by the 'ndktest' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public native int add(int a,int b);
}

jni代码

#include <jni.h>
#include <string>
extern "C" { // 由于库,是c编写的,所有采用c的编译方式
extern int add(int ,int); //
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_ndktest_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_ndktest_MainActivity_add(JNIEnv *env, jobject thiz, jint a, jint b) {
    return add(a,b);
}

cmake代码

我是用的是set方式设置动态库的目录,再通过target_link_libraries连接到对应的动态库。

cmake_minimum_required(VERSION 3.22.1)

# Declares and names the project.

project("ndktest")

# 批量导入
file(GLOB appCPP *.cpp)

add_library(
        native-lib
        SHARED # 动态库
        ${appCPP})

#include_directories("${CMAKE_SOURCE_DIR}/cpp/")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/armeabi-v7a")

find_library(
        log-lib
        log)

target_link_libraries(
        native-lib
        ${log-lib}
        addndk
)

运行结果

 遇到的问题

cmake还有一种添加动态库的方式,通过add_library指定添加方式,再通过set设置动态库的路径,但是我是用这种方式,在android 12的小米手机上面就会直接crash,提示找不到动态库,我在低版本的android 9 模拟器上面就是可以运行的。 

家人们谁懂啊,搞了一天也没走通。最后是用上面setflag的方式设置才能在小米手机上运行。有懂的家人们可以帮忙指导一下。

add_library(addndk SHARED  IMPORTED)
set_target_properties(addndk PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/armeabi-v7a/libaddndk.so)
 Process: com.example.ndktest, PID: 28839
 java.lang.UnsatisfiedLinkError: dlopen failed: library "D:/Code/NdkTest/app/src/main/cpp/../jniLibs/armeabi-v7a/libaddndk.so" not found: needed by /data/app/~~prrC2yw6f71milydnH_J3Q==/com.example.ndktest-6CZEdK9q_P6Zrwfh-gzZNQ==/base.apk!/lib/armeabi-v7a/libnative-lib.so in namespace classloader-namespace
 	at java.lang.Runtime.loadLibrary0(Runtime.java:1077)
 	at java.lang.Runtime.loadLibrary0(Runtime.java:998)
 	at java.lang.System.loadLibrary(System.java:1661)
 	at com.example.ndktest.MainActivity.<clinit>(MainActivity.java:14)
 	at java.lang.Class.newInstance(Native Method)
 	at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:95)
 	at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:45)
 	at android.app.Instrumentation.newActivity(Instrumentation.java:1340)
 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3707)
 	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3963)
 	at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:101)
 	at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
 	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2381)
 	at android.os.Handler.dispatchMessage(Handler.java:106)
 	at android.os.Looper.loopOnce(Looper.java:210)
 	at android.os.Looper.loop(Looper.java:299)
 	at android.app.ActivityThread.main(ActivityThread.java:8237)
 	at java.lang.reflect.Method.invoke(Native Method)
 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:559)
 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:954)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值