Native so Library APK size 优化 以及
2016-11-17 本文已影响225人
ahking17
对于使用了C++库的android应用来说, 通过在打包时使用7zip进行压缩, 当应用首次启动时解压so库后再进行加载, 可以达到50%上下的so文件大小优化.
这篇文章具体讲讲在实际项目中是如何实现的.
使用NativeLibCompression开源库
https://github.com/liyuming1978/NativeLibCompression
这个开源库还算比较简洁, 使用DecRawso提供的2个static方法就可以实现解压so库的工作.
DecRawso.NewInstance(mContext, null, false);
Log.d(TAG, "Native library uncompression started");
DecRawso.GetInstance().waitdecoding();
Log.d(TAG, "Native library uncompression finished");
DecRawso的实现分析
在构造对象时:
public static boolean NewInstance(Context cont,Handler hdl,boolean showProgress)
{
DecRawsoSingleton = new DecRawso(cont,hdl,showProgress);
return true;
}
--->
private DecRawso(Context cont,Handler hdl,boolean showProgress) {
sAppFilePath = cont.getFilesDir().getAbsolutePath();
sPathName = sAppFilePath+"/../lib/";
AssetFileDescriptor fd=null;
AssetManager am = mAppContext.getAssets();
try {
fd = am.openFd("rawso"); //读取apk assets目录下的"rawso"文件
fd.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
File filex = new File(sAppFilePath+"/DecRawsoLib/decdone_"+localVersion+"_"+lasttime);
File filedir = new File(sAppFilePath+"/DecRawsoLib/");
if(!filex.exists())
{
if(!filedir.exists()){
filedir.mkdir();//empty so create dir
}
sPathName = sAppFilePath+"/DecRawsoLib/";
Dec7zLib(showProgress,bClound,cont);
}
}
--->
private void Dec7zLib(boolean showProgress,boolean bCloudDownload,Context cont)
{
mDec7zLibThread = new Thread(new Dec7zLibThread(bCloudDownload));
mDec7zLibThread.start();
}
--->
class Dec7zLibThread implements Runnable
{
private boolean bCloudDownload;
public Dec7zLibThread(boolean CloudDownload)
{
bCloudDownload = CloudDownload;
}
@Override
public void run() {
int res;
res = Decode(mAppContext.getAssets(),null,sPathName,abi); //调用native方法进行解压缩
}
--->
private native int Decode(AssetManager asset,String inpath, String outpath,String abi);
---->
7zMain.c file
JNIEXPORT int JNICALL Java_com_library_decrawso_DecRawso_Decode
(JNIEnv * env, jclass thiz,jobject assetManager,jstring jinpath,jstring joutpath,jstring jabi)
{
...
//在这里实现的用7zip进行的解压缩工作.
//7zip的底层实现:
./DecRawso/jni/7zMain.c
./DecRawso/jni/7zC/7zFile.c
./DecRawso/jni/7zC/LzmaDec.c
./DecRawso/jni/7zC/Bra86.c
./DecRawso/jni/7zC/Bcj2.c
./DecRawso/jni/7zC/Bra.c
./DecRawso/jni/7zC/7zAlloc.c
./DecRawso/jni/7zC/7zDec.c
./DecRawso/jni/7zC/Ppmd7.c
./DecRawso/jni/7zC/7zCrc.c
./DecRawso/jni/7zC/7zBuf.c
./DecRawso/jni/7zC/CpuArch.c
./DecRawso/jni/7zC/7zStream.c
./DecRawso/jni/7zC/Lzma2Dec.c
./DecRawso/jni/7zC/7zCrcOpt.c
./DecRawso/jni/7zC/Ppmd7Dec.c
./DecRawso/jni/7zC/7zIn.c
...
}
对apk size的试验结果
本身NativeLibCompression的7zip底层库占用46kb.
wangxin@wangxin:~/Downloads/temp/apk/360Browser_Chromium_116$ ll ./lib/armeabi-v7a/libDecRawso.so
-rw-r--r-- 1 wangxin wangxin 46260 Nov 15 17:10 ./lib/armeabi-v7a/libDecRawso.so
chromium内核压缩完占用12Mb.
wangxin@wangxin:~/Downloads/temp/apk/360Browser_Chromium_116$ ll ./assets/rawso
-rw-r--r-- 1 wangxin wangxin 12213535 Nov 15 17:10 ./assets/rawso
解压完的chromium内核占用29Mb.
root@android:/data/data/com.qihoo.browser/files/DecRawsoLib # ls -l
-rw------- u0_a89 u0_a89 0 2016-11-16 15:50 armmode
-rw------- u0_a89 u0_a89 0 2016-11-16 15:50 decdone_712_7.0.0.46_115
-rw------- u0_a89 u0_a89 29023944 2016-11-16 15:50 libchrome_public.so
综合起来, 减少了apk安装包12Mb, 实际效果还是不错的.
除了对so进行压缩, 还能进行扩展
既然能用7zip对so进行压缩, 也就可以对其他运行时才需要使用的文件进行压缩,例如html,js, json文件等.
只是NativeLibCompression的定制化并不好, 比如写死了assets目录下必须是文件名为"rawso"的so文件.
要实现对其他文件进行7zip解压的话, 需要重构它的源码,
还好NativeLibCompression的源码结构还是比较清晰的, 实现起来也不会太复杂.
chromium 加载 libchrome_public.so 的启动分析.
ChromeTabbedActivity extends ChromeActivity
ChromeActivity extends AsyncInitializationActivity
AsyncInitializationActivity onCreate() ---->
ChromeBrowserInitializer.getInstance(this).handlePreNativeStartup(this);
ChromeBrowserInitializer handlePreNativeStartup() ---->
AsyncInitializationActivity setContentViewAndLoadLibrary() --->
mNativeInitializationController.startBackgroundTasks();
NativeInitializationController startBackgroundTasks(), new Thread() --->
2 steps:
1. DecRawso.NewInstance(mContext, null, false); 解压raw_so文件
2. libraryLoader.ensureInitialized()
catch ProcessInitException, if catch it, throw new RuntimeException(e), browser re-start().
LibraryLoader ensureInitialized() throws ProcessInitException--->
LibraryLoader loadAlreadyLocked() ---> System.load("libchrome_public.so");
----------DONE.---------------