Android 系统(210)----Android的.so文件你需要知道那些知识

Android的.so文件你需要知道那些知识

.so文件的前世今生

早期的Android系统几乎只支持ARMv5的CPU架构,而现在它可以支持7种,几乎涵盖了市面上大部分的CPU架构。

Android系统目前支持的CPU架构主要包含以下7种:ARMv5,ARMv7 (从2010年起),x86 (从2011年起),MIPS (从2012年起),ARMv8,MIPS64和x86_64 (从2014年起),每一种都关联着一个相应的ABI。

二进制接口(ABI)

应用程序二进制接口(Application Binary Interface)定义了二进制文件(尤其是.so文件)如何运行在相应的系统平台上,从使用的指令集,内存对齐到可用的系统函数库。在Android中调用动态库文件(*.so)都是通过jni的方式。形如我们常见的:

void System.load(String pathName); 

在Android系统上,每一个CPU架构对应一个ABI:armeabi,armeabi-v7a,x86,mips,arm64-v8a,mips64,x86_64。

比较常见的百度地图等sdk一般都会提供好几套的架构库。 
这里写图片描述

Android平台生成.so文件

说了这么多,那么如何在Android平台上生成.so文件呢?

NDK环境搭建

关于ndk详细的理论请查看之前的讲解ndk详解,这里只做一个简单的环境搭建。

  1. 下载NDK

在Android Studio上下载即可。下载完后可以在structs目录查看。 
这里写图片描述

  1. 打开项目根目录的local.properties文件

这里写图片描述

  1. 打开项目根目录的gradle.properties文件,添加
android.useDeprecatedNdk=true
  •  

这里写图片描述

这样ndk环境就搭建好了。

编写代码

1 . 创建一个Java类,以实现jni调用。

 
  1. public class JniUtil {

  2. static {

  3. //括号的参数可以任意修改

  4. System.loadLibrary("jniutil");

  5. }

  6.  
  7. //java调C/C++中的方法都需要用native声明且方法名必须和C/C++的方法名一样

  8. public native String test();

  9. }

然后Make Project,完成后便生成字节码文件。 
这里写图片描述

这里写图片描述

2 . 根据JniUtil.class生成.h文件 
打开Android Studio的Terminal,执行以下命令:

javah -d jni -classpath 编译后的class文件的绝对路径

这里写图片描述

执行上述命令后便会在app/src/main目录下自动创建一个包含.h文件的jni文件夹。 
这里写图片描述

我们直接打开文件:

 
  1. #include <jni.h>

  2.  
  3. #ifndef _Included_com_othershe_jnitest_JniUtil

  4. #define _Included_com_othershe_jnitest_JniUtil

  5. #ifdef __cplusplus

  6. extern "C" {

  7. #endif

  8. JNIEXPORT jstring JNICALL Java_com_othershe_jnitest_JniUtil_test

  9. (JNIEnv *, jobject);

  10.  
  11. #ifdef __cplusplus

  12. }

  13. #endif

  14. #endif

3 . 编写.c文件(jniutil.c)

这里的jniutil文件名需要和JniUtil类中System.loadLibrary(“jniutil”);的参数一致。 
jniutil.c具体的编写可根据自己的业务实现,这里仅做测试:

 
  1. #include "com_othershe_jnitest_JniUtil.h"

  2.  
  3. JNIEXPORT jstring JNICALL Java_com_othershe_jnitest_JniUtil_test

  4. (JNIEnv *env, jobject obj) {

  5. return (*env)->NewStringUTF(env, "jni调用成功");

  6. }

注:这里需要注意包名的统一。

4 . 配置项目app目录下的build.gradle文件

 
  1. defaultConfig {

  2. applicationId "com.othershe.jnitest"

  3. minSdkVersion 14

  4. targetSdkVersion 25

  5. versionCode 1

  6. versionName "1.0"

  7.  
  8. ndk {

  9. moduleName "jniutil"

  10. abiFilters 'armeabi', 'x86', 'armeabi-v7a' //输出指定三种abi体系结构下的so库。

  11. }

  12. }

其中ndk标签是新添加的,moduleName 的值同样为System.loadLibrary(“jniutil”);的参数。由于配置了abiFilters,则只会得到armeabi、x86、armeabi-v7a三种ABI对应的.so文件。

最后还需要在生成的jni文件夹下创建一个空的util.c文件,否则会有如下异常:

这里写图片描述

完成以上操作后,jni文件的目录如下:

这里写图片描述

生成.so文件

其实到这一步就已经完成了,那么我们怎么验证我们是否成功的创建了.so文件呢?现在我们来测试一下,写个TextView显示一下调用的C:

 
  1. public class MainActivity extends AppCompatActivity {

  2.  
  3. @Override

  4. protected void onCreate(Bundle savedInstanceState) {

  5. super.onCreate(savedInstanceState);

  6. setContentView(R.layout.activity_main);

  7. TextView tv = (TextView)findViewById(R.id.tv);

  8.  
  9. tv.setText(new JniUtil().getString());

  10. }

  11. }

这里写图片描述

这里写图片描述

使用.so文件需要注意的地方

当你编译.so文件时,经常会出现一些错误,其中最多的是”UnsatisfiedLinkError”,”dlopen: failed”以及其他类型的crash或者低下的性能:

高版本编译的.so文件运行在低版本手机上

NDK平台不是向后兼容的,而是向前兼容的,推荐使用app的minSdkVersion对应的编译平台。

每个支持的CPU架构都需要一套对应的.so文件

这个就好比32位的软件没办法运行在64位的CPU上,必须为每一个CPU架构提供一套.so文件。

参考:Android中.so文件的Hook

猜你喜欢

转载自blog.csdn.net/zhangbijun1230/article/details/81126060