公司项目需要将一个Android项目打包成apk文件并加密,然后在另一个Android项目中解密并动态加载。
一般情况下,我们调用System.loadLibrary("libraryName");
就能加载so库,但是当我的子项目加载so库时报错了:
java.lang.UnsatisfiedLinkError: dalvik.system.DexClassLoader[
DexPathList[[zip file "/data/app/packageName/xxx/xxx.apk"],
nativeLibraryDirectories=[/vendor/lib, /system/lib]]]
couldn't find "libraryName.so"
也就是找不到so库文件,项目是使用DexClassLoader
加载的,一开始尝试了把库文件放到子项目里面,发现行不通,apk文件里面虽然打包了so文件,但是加载的时候so文件还是在apk文件里面,需要用某种方式将so文件从apk文件里面提取出来才行。
后来又尝试了把so库文件放到主项目中,并提前在加载子项目之前执行System.loadLibrary("libraryName");
发现这样也不行,子项目不能直接使用主项目加载的库。
后来看了下DexClassLoader
的构造函数是这样的:
public DexClassLoader(String dexPath, String optimizedDirectory,
String librarySearchPath, ClassLoader parent);
其中可以自定义librarySearchPath
,也就是不用在系统目录中去找库文件。
接下来思路就清晰了,就是要找到app安装在手机里后,so文件的路径。
于是通过百度、google,找到了获取库路径的语句:
getApplicationInfo().nativeLibraryDir
但是这种方式依然不行,子项目依然无法加载到so库,也就是说库文件不在这个目录,而且这条语句获取到的目录路径是/data/app/packageName/lib/arm
,而我使用的库的cpu架构是armeabi-v7a,所以这个目录路径不正确。
然后我在主项目中加载一个不存在的库,此时提示
java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[
zip file "/data/app/packageName-1/base.apk"],
nativeLibraryDirectories=[/data/app/packageName-1/lib/arm,
/data/app/packageName-1/base.apk!/lib/armeabi-v7a, /vendor/lib, /system/lib]]]
couldn't find "libraryName.so"
这样就知道了主项目加载库文件的路径,于是我把librarySearchPath
改成了这个路径,编译运行。
结果又报错了:
java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[
zip file "/data/app/packageName-2/base.apk"],
nativeLibraryDirectories=[/data/app/packageName-2/lib/arm,
/data/app/packageName-2/base.apk!/lib/armeabi-v7a, /vendor/lib, /system/lib]]]
couldn't find "libraryName.so"
packageName
后面的1和2还是会变化的,也就是这可能是个临时生成的路径,我还是要动态获取库目录的路径才行。
通过在网上搜索资料,知道了ClassLoader
里有个findLibrary(String libname)
函数可以获取库文件的路径,但是ClassLoader
下的这个函数是protected的,类外包外不可见,不能直接调用,不过BaseDexClassLoader
继承了ClassLoader
,并且它的findLibrary(String name)
是public的,我们可以通过调用它来获取库文件路径。
这个函数获取的是某个具体的库文件的路径,截取出库目录路径即可。
最后的代码如下:
String librarySearchPath = ((BaseDexClassLoader) getClassLoader()).findLibrary("libraryName");
librarySearchPath = librarySearchPath.substring(0, librarySearchPath.lastIndexOf('/'));
DexClassLoader classLoader = new DexClassLoader(dexPath,optimizedDirectory,
librarySearchPath, getClassLoader());
这个问题也许有更好的解决办法,不过目前只想到这种,以后找到更好的再更新。