Android中加载so:
(1)调用load()方法,传递so文件的绝对路径;
(2)调用loadLibrary()方法,传递so文件的名称,而且so文件必须放在apk的lib目录下,而且so的名称必须去掉前面的lib和后边的“.so”。
只能加载两个目录下的so文件:
(1)/system/lib
(2)应用程序安装包的路径:/data/data/packgename/…
对于两种加载so文件的方法,在Android源码System.java中可以看到
————————————————
上边两种方法都调用了Runtime中的getRuntime函数,用来获取Runtime的实例、、
在加载so时也调用了loadLibrary0方法
在loadLibrary0中可以看到根据ClassLoader是否为空,有两种不同的处理形式。
privatesynchronizedvoidloadLibrary0(ClassLoaderloader,Class<?>callerClass,Stringlibname) {
if(libname.indexOf((int)File.separatorChar)!=-1) {
thrownewUnsatisfiedLinkError(
"Directory separator should not appear in library name: "+libname);
}
StringlibraryName=libname;
//ClassLoader不为空时(程序中通过System.loadlibrary()方式,这个loader就不会为空)
if(loader!=null&&!(loaderinstanceofBootClassLoader)) {
//通过ClassLoader的findLibrary方法查找so的文件名称,寻找so文件是否存在
Stringfilename=loader.findLibrary(libraryName);
//判断,如果未找到so文件,并且类加载器存在
if(filename==null&&
(loader.getClass()==PathClassLoader.class||
loader.getClass()==DelegateLastClassLoader.class)) {
filename=System.mapLibraryName(libraryName);//拼接so文件名
}
if(filename==null) {
thrownewUnsatisfiedLinkError(loader+" couldn't find \""+
System.mapLibraryName(libraryName)+"\"");
}
//so文件存在,加载它
Stringerror=nativeLoad(filename,loader);
if(error!=null) {
//加载异常,加载失败
thrownewUnsatisfiedLinkError(error);
}
return;
}
//ClassLoader为空时,传入library name和System.mapLibraryName获得真正的library name。
//具体深入的实现过程,可以到Android源码继续查看getLibPaths()和mapLibraryName的实现
getLibPaths();
Stringfilename=System.mapLibraryName(libraryName);//mapLibraryName用于拼接so文件名的前缀:lib和后缀.so
//例如传入的是wudong,得到的就会是libwudong.so,然后在mLibPaths查找'libwudong.so',最终确定library的path。
Stringerror=nativeLoad(filename,loader,callerClass);
if(error!=null) {//加载异常
thrownewUnsatisfiedLinkError(error);
}
}
在查找so文件的过程中,调用了loader.findLibrary的方法去寻找so文件,如果可以找到,会返回so文件的绝对路径,然后交由nativeLoad()去加载。
二、实现so的动态加载
1.首先把so库放在assets资源目录下,一般会放两个so库,一个32位一个64位的;
2.动态加载,就是在需要使用的时候,从assets资源目录下复制到app/libs目录下;
3.通过 System.load(String filename) 或者 System.loadLibrary(String libname) 方法去加载 so。
·在so原本的加载过程中,是系统通过ClassLoader检索native目录里是否存在so库进行加载的,那就需要把下载存放so的路径添加到ClassLoader的libs路径里,这些libs路径在app启动的时候就已经生成了,那就需要利用反射,在运行时把路径添加进去。
将存放so的路径放到ClassLoader中
利用反射将存放so的路径放到ClassLoader中,开源项目tinker的TinkerLoadLibrary也有实现发方法,我们就不用自己实现了,可以拿过来直接使用。
————————————————
privatestaticfinalclassV25{
privatestaticvoidinstall(ClassLoaderclassLoader,Filefolder)throwsThrowable{
finalFieldpathListField=ShareReflectUtil.findField(classLoader,"pathList");
finalObjectdexPathList=pathListField.get(classLoader);
finalFieldnativeLibraryDirectories=ShareReflectUtil.findField(dexPathList,"nativeLibraryDirectories");
List<File>origLibDirs=(List<File>)nativeLibraryDirectories.get(dexPathList);
if(origLibDirs==null) {
origLibDirs=newArrayList<>(2);
}
finalIterator<File>libDirIt=origLibDirs.iterator();
while(libDirIt.hasNext()) {
finalFilelibDir=libDirIt.next();
if(folder.equals(libDir)) {
libDirIt.remove();
break;
}
}
origLibDirs.add(0,folder);
finalFieldsystemNativeLibraryDirectories=ShareReflectUtil.findField(dexPathList,"systemNativeLibraryDirectories");
List<File>origSystemLibDirs=(List<File>)systemNativeLibraryDirectories.get(dexPathList);
if(origSystemLibDirs==null) {
origSystemLibDirs=newArrayList<>(2);
}
finalList<File>newLibDirs=newArrayList<>(origLibDirs.size()+origSystemLibDirs.size()+1);
newLibDirs.addAll(origLibDirs);
newLibDirs.addAll(origSystemLibDirs);
finalMethodmakeElements=ShareReflectUtil.findMethod(dexPathList,"makePathElements",List.class);
finalObject[]elements=(Object[])makeElements.invoke(dexPathList,newLibDirs);
finalFieldnativeLibraryPathElements=ShareReflectUtil.findField(dexPathList,"nativeLibraryPathElements");
nativeLibraryPathElements.set(dexPathList,elements);
}
}
}
我们在原本的检索路径中,在最前面,即数组为0的位置加入了我们的检索路径,这样一来classloader在查找我们已经动态化的so库的时候,就能够找到!
会有的问题:
出现: 依赖的so 找不到的情况
比如不依赖其他的so的时候,直接这样加载就没问题了,但是如果存在着依赖的so库的话,就不行了!相信大家在看其他的博客的时候就能看到,是因为Namespace的问题。具体是我们动态库加载的过程中,如果需要依赖其他的动态库,那么就需要一个链接的过程对吧!