androidso加载,android动态加载so文件

安卓JVM加载so库流程

好久没有写点东西发了,工作中的事情有点杂,也找不到整块东西可以写的。

创新互联是一家专注于做网站、成都网站制作与策划设计,扎兰屯网站建设哪家好?创新互联做网站,专注于网站建设十余年,网设计领域的专业建站公司;建站业务涵盖:扎兰屯等地区。扎兰屯做网站价格咨询:18982081108

最近调查了一个问题,稍微追了一下流程,这里记录一下。

由于我们支持的设备相对比竞品,zygote进程多占用了好几倍的内存空间。通过dump meminfo后发现,我们的设备在so库,ttf,和unkonwn mmap的内存空间相比竞品一共大了20多M,其中so库多了15M左右。

通过查看zygote进程的smaps,确定了占用空间最大的几个so库确实是我们自己的。虽然确定了内存占用大的原因,还是得把这些so库是加载在zygote进程中的时机确定了才行。

我的第一反应就是在zygote启动时,加载sharedLibrary()时,把这些库加载了,于是去看了这部分源码,并没有。

通过反复调试以及追踪源码,最后发现是在JVM启动的过程中加载了这些so库,这些so库的配置在“system/etc/public.libraries.txt”下。

这个文件里配置的都是public的so库,能够被普通app访问的。类似的配置文件还有“vendor/etc/”下面的,还有一些其他的配置地方,我没有深入去看,想要看的盆友可以自己去看源码或者注释。

下面我就带大家一起看看虚拟机加载这些so库的流程。

调用栈:

frameworks/base/cmds/app_process/app_main.cpp

----runtime.start("com.android.internal.os.ZygoteInit", args, zygote);

frameworks/base/core/jni/AndroidRuntime.cpp

----AndroidRuntime::startVm

---JNI_CreateJavaVM(pJavaVM, pEnv, initArgs)

art/runtime/jni/java_vm_ext.cc

---android::InitializeNativeLoader();

system/core/libnativeloader/native_loader.cpp

----Initialize()

----ReadConfig(public_native_libraries_system_config, sonames, always_true, error_msg)

其中,public_native_libraries_system_config 为 system/etc/public.libraries.txt

这部分流程是在安卓设备开机过程中的,在执行ZygoteInit.main()之前会先启动java虚拟机的,这样fork其他java进程的时候,java环境就已经有了,不用再创建虚拟机了。

最后贴一下Initialize()函数:

android项目中如何加载已有so库

1,在项目根目录下建立文件夹libs/armeabi文件夹

2,将so库放入libs/armeabi文件夹注意事项:

1,如果采用静态注册的方式请注意C文件中严格按照命名规则Java_packageName_className_method()的方式命名

2,在Android项目中建立同上述命名规则中packageName中相同的包名,在此包名下建立同上述命名规则中className相同的类名

3,在className声明native方法

4,程序中加载so库System.loadLibrary(data/data/xxx.xxx.xxx/lib/xx.so)或者System.loadLibrary(xx),例如:System.loadLibrary(data/data/com.dtBank.app.service/lib/libjnixcld.so);

Android 应用push ,so无法加载

作者君主要做SDK开发,对接一些厂商或运行商的普通应用或系统应用。

当对接系统应用时,由于系统应用是由于覆盖机型比较广,会碰到Android多个版本机型,有的可能出现so找不到的问题。

普通install安装apk的方式,apk会被安装在 /data/app 目录下,那么So则会被映射到/data/app/项目目录下/lib。

首次安装只能通过直接push到/system/app/下的方式来安装,而不是如普通应用般采取install的方式。

android在开机扫描应用的时候会对 相应目录进行扫描,如果发现 data/app目录下 存在和系统应用同包名的应用,并且版本号比系统应用的版本号更高则构成升级关系,校验签名等安全验证通过。此时data/app下的这个应用就是系统应用

而push到系统目录下如system/app/....,则会优先寻找system/lib(lib64)目录下的so,由于so

不会自动释放到该目录下,所以需要手动push到该路径下。

作者君还遇到过这样的问题,有时候为了减少包的大小或者其他,不会把所有ABI类型的so都放进目录,另外so的提供者团队没有提供64的so(哈哈,如果提供了,下面的问题直接就没有了,那为什么还拿出来说呢,主要是简单地了解一下系统底层的一些基本原理,感兴趣的可以看下一下~~)

机型类型是64位的,其他apk仅提供了64位的so。而某个apk由于只集成了32位so, install安装是正常的,但是把apk通过push到/system/app下,so放到/system/lib下则报如下错误:

32位机器上当然是正常的。

那么为什么会出错呢?首先是系统级应用,需要理解Android系统的原理(当然啦,也许厂商定制了一番,那则另一回事。):

系统有几个属性,其中app.info.primaryCpuAbi这个值用来决定apk关联ABI类型。而PackageManager会对这个值有所影响。比如:通过apk包里包含的so库的架构来决定app的primaryCpuAbi的值。

另外:

如果机器里有64位的apk,且PackageManager扫描到第一正好是这个apk,PackageManager调整所有apk要加载的都是64位的so。不再去加载32位的so,那么只含32位so的apk就会跑出异常。反之,则64位的apk正常运行,32位的则出错。

作者君能力有限,如有错处,请书友们指导,作者君会第一时间修改。

一起学习 一起进步 ღ( ´・ᴗ・` )比心

Android studio 怎么加载.so文件

1、在src/main中添加

jniLibs文件夹

,把.so复制进去

2、在build.gradle中就添加这么几行

,

看图

复制内容到剪贴板

sourceSets

{

main

{

jniLibs.srcDirs

=

['libs']

}

}

3、然后make

project

4、切换到android结构下,你会看到

jniLibs

中.so已经变成了.jar文件,证明已经成功

如何加载so文件 android

android中加载so文件:

在Android中调用动态库文件(*.so)都是通过jni的方式,而且往往在apk或jar包中调用so文件时,都要将对应so文件打包进apk或jar包,工程目录下图:

Android中加载so文件的提供的API:

void System.load(String pathName);

说明:

1、pathName:文件名+文件路劲;

2、该方法调用成功后so文件中的导出函数都将插入的系统提供的一个映射表(类型Map);

3、具体代码如下:

try { 

String localPath = Environment.getExternalStorageDirectory() + path; 

Log.v(TAG, "LazyBandingLib localPath:" + localPath);

String[] tokens = mPatterns.split(path); 

if (null == tokens || tokens.length = 0 

|| tokens[tokens.length - 1] == "") { 

Log.v(TAG, "非法的文件路径!"); 

return -3; 

// 开辟一个输入流 

File inFile = new File(localPath); 

// 判断需加载的文件是否存在 

if (!inFile.exists()) { 

// 下载远程驱动文件 

Log.v(TAG, inFile.getAbsolutePath() + " is not fond!"); 

return 1; 

FileInputStream fis = new FileInputStream(inFile);

File dir = context.getDir("libs", Context.MODE_PRIVATE); 

// 获取驱动文件输出流 

File soFile = new File(dir, tokens[tokens.length - 1]); 

if (!soFile.exists()) { 

Log.v(TAG, "### " + soFile.getAbsolutePath() + " is not exists"); 

FileOutputStream fos = new FileOutputStream(soFile); 

Log.v(TAG, "FileOutputStream:" + fos.toString() + ",tokens:" 

+ tokens[tokens.length - 1]);

// 字节数组输出流,写入到内存中(ram) 

ByteArrayOutputStream baos = new ByteArrayOutputStream(); 

byte[] buffer = new byte[1024]; 

int len = -1; 

while ((len = fis.read(buffer)) != -1) { 

baos.write(buffer, 0, len); 

// 从内存到写入到具体文件 

fos.write(baos.toByteArray()); 

// 关闭文件流 

baos.close(); 

fos.close(); 

fis.close(); 

Log.v(TAG, "### System.load start"); 

// 加载外设驱动 

System.load(soFile.getAbsolutePath()); 

Log.v(TAG, "### System.load End");

return 0;

} catch (Exception e) { 

Log.v(TAG, "Exception   " + e.getMessage()); 

e.printStackTrace(); 

return -1;

}

Android So加载的路径选择

我们在Android应用程序会常常的加载一些So文件来完成我们的目标,那么我们的APK加载So是有哪些平时我们没有注意到的事情呢?

1. 首先我们一般开发会遇见两种APK(其实一般大部分只会遇到一种),一种为系统级APK,另外一种为普通APK。那么这个两种APK跟So加载有什么关系呢?别急,让我们先聊聊我们那些操作会产生这些类型的APK。

普通级AKP: 

pm install + 包名将会把APK安装到 /data/app 目录下,同时会把So映射到/data/app-lib/包命/ 目录下。这个就是普通的APK(pm Install -r 会替换原有的APK,当然必须是一样的签名)。

系统级APK:

push  + 绝对路径 + 包名 /system/app 目录下(必须把原有的包名删除哦!),这时APK就会在System/app下面了,这时你需要把你的APK的So 同时push到system/lib里面。因为apk里面的So并不会自动映射到system/lib下面。

一般我们在使用加载So的方法时候,会使用到System.load(pathName)和 System.loadLibrary(libName)这两种方法。这篇文章主要讲讲System.load(pathName)这个绝对路径加载的注意点。

我们通常会直接使用

context.getApplicationInfo().nativeLibraryDir +/具体名字.so  来让系统帮我寻找加载So所需要的路径。那么这里问题就来了。

如果是系统级APK

context.getApplicationInfo().nativeLibraryDir = /system/lib/

如果是普通级APK

context.getApplicationInfo().nativeLibraryDir  =/data/data-lib/PackageName/ 对!就是那个映射的So系统会根据这个去data/app/包名下面寻找真正的So文件。

这个需要注意的细节,主要用于在中间件,系统预置程序的研发人员与测试上面。我们在拿到芯片厂商给予调试模式的开发硬件上进行Demo和So的更换测试的时候,需要自己和测试都需要知道,自己安装的APK是什么类型,会加载什么路径,以免我们的底层老司机在帮忙测试问题的时候造成不必要的麻烦。


本文名称:androidso加载,android动态加载so文件
链接URL:http://scyanting.com/article/dsgdeoj.html