一、背景
随着第三方sdk的增多,sdk集成的so库越来越多,包体也越来越大,继续开发一个动态加载so库的框架代码:DynamicSoLoader
二、具体使用
提供DynamicSoLoader用于支持此功能,用法如下;
1、打包时,将对应的so库在打包时移除:
packagingOptions {
exclude 'lib/armeabi-v7a/libagora-core.so'
exclude 'lib/armeabi-v7a/libagora-ffmpeg.so'
exclude 'lib/armeabi-v7a/libagora-fdkaac.so'
exclude 'lib/armeabi-v7a/libagora-mpg123.so'
exclude 'lib/armeabi-v7a/libagora-soundtouch.so'
exclude 'lib/armeabi-v7a/libagora-rtc-sdk.so'
exclude 'lib/armeabi-v7a/libagora_ai_denoise_extension.so'
exclude 'lib/armeabi-v7a/libagora_dav1d_extension.so'
exclude 'lib/armeabi-v7a/libagora_jnd_extension.so'
exclude 'lib/armeabi-v7a/libagora-rtc-sdk.so'
}
将对应的so库打包成zip,上传到服务端,配置到后台(这个未做);
2、启动app时调用:
DynamicSoLoader.getInstance().init(getApplicationContext());
3、启动后调用下载接口进行下载;
DynamicSoLoader.getInstance().start(getApplicationContext(), url, matchSoFileName, new CommomCallBack() {
@Override
public void onResult(Object o) {
boolean flag = (boolean)o;
if(flag){
LogUtils.i(TAG,"Dynamic执行成功,开始测试加载SDK");
}else{
LogUtils.i(TAG,"Dynamic执行失败");
}
}
});
参数说明
url:打包后的so库zip 地址;
matchSoFileName:比如:libagora-core.so;此文件 用于精准校验是否已经真正下载并解压完成,取zip包里的任意一个so库即可。
4、注意事项:
由于是动态下载,所有下载失败是有可能的,需要针对下载失败的情况进行处理,比如hook住对应调用的入口,如果没下载完,进行提示(让产品出优化需求),然后重新下载,直到下载成功为止;
5、RN yoga so库 实践:
1、打包配置如下,移除对应的so库:
packagingOptions {
exclude 'lib/armeabi-v7a/libagora-core.so'
exclude 'lib/armeabi-v7a/libagora-ffmpeg.so'
exclude 'lib/armeabi-v7a/libagora-fdkaac.so'
exclude 'lib/armeabi-v7a/libagora-mpg123.so'
exclude 'lib/armeabi-v7a/libagora-soundtouch.so'
exclude 'lib/armeabi-v7a/libagora-rtc-sdk.so'
exclude 'lib/armeabi-v7a/libagora_ai_denoise_extension.so'
exclude 'lib/armeabi-v7a/libagora_dav1d_extension.so'
exclude 'lib/armeabi-v7a/libagora_jnd_extension.so'
exclude 'lib/armeabi-v7a/libagora-rtc-sdk.so'
}
2、application初始化:
DynamicSoLoader.getInstance().init(getApplicationContext());
3、在启动页面后立刻执行:
if(!DynamicSoLoader.getInstance().isSoLoaded(getApplicationContext(),"[libagora-core.so]")){
String url = "https://fe-meiyou.oss-cn-hangzhou.aliyuncs.com/FE/thirdsolibs.zip";
DynamicSoLoader.getInstance().start(getApplicationContext(), url,
new CommomCallBack() {
@Override
public void onResult(Object o) {
boolean flag = (boolean)o;
if(flag) {
LogUtils.i(TAG, "Dynamic执行成功,开始进行预加载逻辑");
ReactFetchManager.getInstance().prefetch();
}else{
LogUtils.e(TAG, "Dynamic执行失败");
}
}
}
);
}
ReactLoader.getInstance().setReactResourcePreparedListener(new ReactLoader.OnReactResourcePreparedListener(){
@Override
public boolean isReactResourcePrepared() {
return DynamicSoLoader.getInstance().isSoLoaded(getApplicationContext(),"[libagora-core.so](http://libagora-core.so)");
}
@Override
public void loadReactResource(Activity activity,CommomCallBack commomCallBack) {
PhoneProgressDialog.showRoundDialog(activity, "正在加载RN资源", new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
}
});
String url = "<https://fe-meiyou.oss-cn-hangzhou.aliyuncs.com/FE/thirdsolibs.zip>";
String matchSoFileName = "[libagora-core.so](http://libagora-core.so)";
DynamicSoLoader.getInstance().start(getApplicationContext(), url, matchSoFileName, new CommomCallBack() {
@Override
public void onResult(Object o) {
PhoneProgressDialog.dismissDialog(activity);
boolean flag = (boolean)o;
if(flag){
LogUtils.i(TAG,"Dynamic执行成功,开始测试加载SDK");
if(commomCallBack!=null){
commomCallBack.onResult(true);
}
//重新开启预加载
ReactFetchManager.getInstance().prefetch();
}else{
LogUtils.i(TAG,"Dynamic执行失败");
if(commomCallBack!=null){
commomCallBack.onResult(false);
}
}
}
});
}
});
6、DynamicSoLoader详细实现:
public class DynamicSoLoader {
/**
* 存储第三方so库的文件夹名称
* DON'T MODIFY ,IMPORTANT!!
*/
private static final String THIRD_SO_FOLDER_NAME = "dynamicsolibs";
private static final String TAG = "DynamicSoLoader";
private static DynamicSoLoader instance;
public synchronized static DynamicSoLoader getInstance(){
if(instance==null){
instance = new DynamicSoLoader();
}
return instance;
}
private List<String> listUrl = Collections.synchronizedList(new ArrayList<>());
private Map<String,List<CommomCallBack>> listMapCallBack = Collections.synchronizedMap(new HashMap<>());
public DynamicSoLoader(){
}
public String getSoLoaderPath(Context context){
String soloaderPath = context.getApplicationInfo().dataDir + "/lib-main";
LogUtils.i(TAG,"getSoLoaderPath:"+soloaderPath);
return soloaderPath;
}
/**
*
* 是否命中64适配规则
*
* @param context
* @return
*/
public boolean isHit64(Context context){
try {
if(CPUUtils.isSupport64Bit()){
if(isAdapter64()){
/*if(ApkBitsUtils.hasApkBits(context)){
if(ApkBitsUtils.isApk64(context) || ApkBitsUtils.isApk3264(context)){
//64设备+64位包;
LogUtils.e(TAG,"isHit64:64设备+64位包,return true");
return true;
}else if(ApkBitsUtils.isApk32(context)){
//64设备+32位包;
LogUtils.e(TAG,"isHit64:64设备+32位包,return false");
return false;
}else{
LogUtils.e(TAG,"isHit64:未知位数,return true");
return true;
}
}else{
//64设备+未知包;
LogUtils.e(TAG,"isHit64:64设备+未知包,return true");
return true;
}*/
LogUtils.e(TAG,"isHit64:64设备+isAdapter64=true,return true");
return true;
}else{
//64位设备+isAdapter64=false;
LogUtils.e(TAG,"isHit64:64设备+isAdapter64=false,return false");
return false;
}
}else if(CPUUtils.isSupport32Bit()){
LogUtils.i(TAG,"此设备CPU类型是:armeabi-v7a");
// 因为32位设备装不了64位包,所以肯定是32位的包;
LogUtils.e(TAG,"isHit64:32设备 return false");
return false;
}else{
//LogUtils.i(TAG,"此设备CPU类型是未知:"+Build.SUPPORTED_ABIS.toString());
LogUtils.e(TAG,"isHit64:未知设备 return true:"+Build.SUPPORTED_ABIS.toString());
return true;
}
}catch (Exception exception){
exception.printStackTrace();
LogUtils.e(TAG,"isHit64:发生异常!!!!"+exception.getMessage());
}
return true;
}
public String getDynamicSoPath(Context context){
try {
if(isHit64(context)){
return getDynamicV8aSoPath(context);
}else{
return getDynamicV7aSoPath(context);
}
}catch (Exception e){
e.printStackTrace();
}
return getDynamicV7aSoPath(context);
}
private String getDynamicV7aSoPath(Context context){
return context.getFilesDir().getAbsolutePath()+"/"+THIRD_SO_FOLDER_NAME+"/armeabi-v7a";
}
private String getDynamicV8aSoPath(Context context){
return context.getFilesDir().getAbsolutePath()+"/"+THIRD_SO_FOLDER_NAME+"/arm64-v8a";
}
//是否适配64位;
private boolean isAdapter64 = false;
public boolean isAdapter64() {
return isAdapter64;
}
public void setAdapter64(boolean adapter64) {
isAdapter64 = adapter64;
}
private boolean isInited = false;
/**
* 初始化:往系统中设置 第三方 so 文件夹
* @param context
*/
public void init(Context context){
try {
if(isInited){
return;
}
//创建data目录下文件夹;业务启动下载后,必须将so拷贝到此文件夹下;
File thirdsoFile = new File(getDynamicSoPath(context));
if(!thirdsoFile.exists()){
thirdsoFile.mkdirs();
}
try {
installNativeLibraryPath(context.getClassLoader(),thirdsoFile);
} catch (Throwable throwable) {
LogUtils.e(TAG,throwable.getMessage());
HashMap<String,String> map = new HashMap<>();
map.put("error",throwable.getMessage()+"");
GaController.getInstance(MeetyouFramework.getContext()).onEvent("/dynamicsofail",map);
}
LogUtils.i(TAG,"DynamicSoLoader 初始化完成");
}catch (Exception ex){
ex.printStackTrace();
HashMap<String,String> map = new HashMap<>();
map.put("error1",ex.getMessage()+"");
GaController.getInstance(MeetyouFramework.getContext()).onEvent("/dynamicsofail",map);
LogUtils.e(TAG,"DynamicSoLoader 初始化失败!!");
}finally {
isInited = true;
}
}
@Deprecated
public boolean isSoLoaded(Context context,String soList,boolean isCheckSoLoaderDirectory){
try {
String dynamicSoPath = getDynamicSoPath(context);
String soloaderPath = getSoLoaderPath(context);
boolean isSoloaded1 = isSoLoadedImp(dynamicSoPath,soList,null);
if(isSoloaded1){
if(isCheckSoLoaderDirectory) {
isSoloaded1 = isSoLoadedImp(soloaderPath, soList, null);
}
}
return isSoloaded1;
/*String dynamicSoPath = getDynamicSoPath(context);
String soloaderPath = getSoLoaderPath(context);
if(matchSoFileName.contains(",")){
String []matchSoFileNameArray = matchSoFileName.split(",");
for(String fileSoName:matchSoFileNameArray){
File file = new File(dynamicSoPath+"/"+fileSoName);
if(!file.exists() || (file.exists() && file.length()==0)){
LogUtils.e(TAG,dynamicSoPath+"文件夹下不存在此so库:"+fileSoName);
return false;
}
}
if(isCheckSoLoaderDirectory){
for(String fileSoName:matchSoFileNameArray){
File file = new File(soloaderPath+"/"+fileSoName);
if(!file.exists() || (file.exists() && file.length()==0)){
LogUtils.e(TAG,"Soloader lib-main文件夹下不存在此so库:"+fileSoName);
return false;
}
}
}
return true;
}else{
File file = new File(dynamicSoPath+"/"+matchSoFileName);
if(file.exists()){
if(isCheckSoLoaderDirectory){
file = new File(soloaderPath+"/"+matchSoFileName);
if(file.exists() && file.length()>0){
return true;
}else{
LogUtils.e(TAG,"1 Soloader lib-main文件夹下不存在此so库:"+matchSoFileName);
}
}else{
return true;
}
}else{
LogUtils.e(TAG,"1" +dynamicSoPath+"文件夹下不存在此so库:"+matchSoFileName);
}
}*/
}catch (Exception ex){
ex.printStackTrace();
}
return false;
}
/**
* 检测so是否完整加载,校验Md5会耗时
* @param context
* @param soList so列表,以逗号隔开
* @param soMd5List so对应的md5列表,以逗号隔开
* @param isCheckSoLoaderDirectory
* @return
*/
public boolean isSoLoadedComplete(Context context,String soList,String soMd5List,boolean isCheckSoLoaderDirectory){
try {
String dynamicSoPath = getDynamicSoPath(context);
String soloaderPath = getSoLoaderPath(context);
boolean isSoloaded1 = isSoLoadedImp(dynamicSoPath,soList,soMd5List);
if(isSoloaded1){
if(isCheckSoLoaderDirectory) {
isSoloaded1 = isSoLoadedImp(soloaderPath, soList, soMd5List);
}
}
return isSoloaded1;
}catch (Exception ex){
ex.printStackTrace();
}
return false;
}
private boolean isSoLoadedImp(String dynamicSoPath,String soList,String soMd5List){
// so文件列表
String []matchSoFileNameArray = soList.split(",");
// md5列表
String []md5Array = null;
if(soMd5List!=null){
md5Array = soMd5List.split(",");
if(md5Array.length!=md5Array.length){
LogUtils.e(TAG,"md5列表和文件列表数量不匹配");
md5Array = null;
}
}
int length = matchSoFileNameArray.length;
for(int i=0;i<length;i++){
String fileSoName = matchSoFileNameArray[i];
//文件大小校验
File file = new File(dynamicSoPath+"/"+fileSoName);
if(!file.exists() || (file.exists() && file.length()==0)){
LogUtils.e(TAG,dynamicSoPath+"文件夹下不存在此so库:"+fileSoName);
return false;
}
//md5校验
if(md5Array!=null){
String fileMd5 = MD5Utils.getFileMD5(file);
String fileMd5Match = md5Array[i];
if(!StringUtils.isNull(fileMd5) && !StringUtils.isNull(fileMd5Match) && fileMd5.equalsIgnoreCase(fileMd5Match)){
LogUtils.i(TAG,"md5校验通过:"+fileSoName);
}else{
LogUtils.e(TAG,"md5校验失败:"+fileSoName+" fileMd5:"+fileMd5+" fileMd5Match:"+fileMd5Match);
return false;
}
}
}
return true;
}
@Deprecated
public boolean isSoLoaded(Context context,String matchSoFileName){
return isSoLoaded(context,matchSoFileName,true);
}
/*public boolean deleteSoLoaded(Context context,String matchSoFileName){
try {
String dynamicSoPath = getDynamicSoPath(context);
if(matchSoFileName.contains(",")){
String []matchSoFileNameArray = matchSoFileName.split(",");
for(String fileSoName:matchSoFileNameArray){
File file = new File(dynamicSoPath+"/"+fileSoName);
if(file.exists()){
file.delete();
}
}
return true;
}else{
File file = new File(dynamicSoPath+"/"+matchSoFileName);
if(file.exists()){
file.delete();
}
return true;
}
}catch (Exception ex){
ex.printStackTrace();
}
return false;
}*/
private boolean isSoDownLoading(String url){
try {
if(StringUtils.isNull(url)){
return false;
}
if(listUrl.contains(url)){
return true;
}
return false;
}catch (Exception ex){
ex.printStackTrace();
}
return false;
}
private void setSoDownLoading(String url,boolean flag){
try {
LogUtils.e(TAG,"setSoDownLoading flag :"+flag+" url:"+url);
if(StringUtils.isNull(url)){
return;
}
if(flag){
if(!listUrl.contains(url)){
listUrl.add(url);
LogUtils.e(TAG,"setSoDownLoading add url:"+url);
}
}else{
if(listUrl.contains(url)){
listUrl.remove(url);
LogUtils.e(TAG,"setSoDownLoading remove url:"+url);
}
}
}catch (Exception ex){
ex.printStackTrace();
}
}
private void addSoDownLoadCallBack(String url,CommomCallBack callBack){
try {
if(StringUtils.isNull(url) || callBack==null){
return;
}
if(listMapCallBack.containsKey(url)){
List<CommomCallBack> list = listMapCallBack.get(url);
if(!list.contains(callBack)){
list.add(callBack);
}
LogUtils.i(TAG,"addSoDownLoadCallBack contains, size is:"+list.size()+" url:"+url+" listMapCallBack.size:"+listMapCallBack.size());
}else{
List<CommomCallBack> list = new ArrayList<>();
list.add(callBack);
listMapCallBack.put(url,list);
LogUtils.i(TAG,"addSoDownLoadCallBack no contains, size is:"+list.size()+" url:"+url+" listMapCallBack.size:"+listMapCallBack.size());
}
}catch (Exception ex){
ex.printStackTrace();
}
}
private void notifySoDownloadCallBackAndRemove(String url,boolean flag){
try {
if(StringUtils.isNull(url)){
return;
}
if(!listMapCallBack.containsKey(url)){
return;
}
List<CommomCallBack> list = listMapCallBack.get(url);
if(list!=null){
for(CommomCallBack commomCallBack:list){
try {
LogUtils.i(TAG,"notifySoDownloadCallBackAndRemove url:"+url);
commomCallBack.onResult(flag);
}catch (Exception ex){
ex.printStackTrace();
}
}
}
//最后移除
listMapCallBack.remove(url);
LogUtils.i(TAG,"notifySoDownloadCallBackAndRemove remove url:"+url+" listMapCallBack size:"+listMapCallBack.size());
}catch (Exception ex){
ex.printStackTrace();
}
}
private void removeDownloadCallBackAndRemove(String url){
try {
if(StringUtils.isNull(url)){
return;
}
if(!listMapCallBack.containsKey(url)){
return;
}
//最后移除
listMapCallBack.remove(url);
LogUtils.i(TAG,"removeDownloadCallBackAndRemove remove url:"+url+" listMapCallBack size:"+listMapCallBack.size());
}catch (Exception ex){
ex.printStackTrace();
}
}
/**
* 启动下载第三方SO,并拷贝进入 so loader
* @param context
*/
public void start(Context context,DynamicSoLoaderParams params, CommomCallBack commomCallBack){
start(context,params,false,commomCallBack);
}
private void start(Context context,DynamicSoLoaderParams params, boolean useBackupUrl,CommomCallBack commomCallBack){
try {
if(context==null || params==null){
return;
}
String matchSoFileName = params.getMatchSoFileName();
String url = useBackupUrl?params.getBackupUrl():params.getUrl();
boolean copyToSoLoaderDirectory = params.isCopyToSoLoaderDiretory();
if(StringUtils.isNull(url)){
LogUtils.e(TAG,"url为空,useBackupUrl:"+useBackupUrl);
if(commomCallBack!=null){
commomCallBack.onResult(false);
}
return;
}
if(!params.isIgnoreCache()){
if(isSoLoaded(context,matchSoFileName)){
LogUtils.i(TAG,"文件之前已拷贝完毕,无需进行下载和拷贝:"+matchSoFileName);
setSoDownLoading(url,false);
if(commomCallBack!=null){
commomCallBack.onResult(true);
}
return;
}
}
if(isSoDownLoading(url)){
LogUtils.e(TAG,"已经在下载中,无需重复执行下载,等待即可");
addSoDownLoadCallBack(url,commomCallBack);
return;
}
//回调监听
addSoDownLoadCallBack(url,commomCallBack);
//设置加载中
setSoDownLoading(url,true);
//开始下载
DynamicSoDiskProducer abstractProducer = new DynamicSoDiskProducer(context,url,matchSoFileName,new AbstractProducer.ProducerListener() {
@Override
public void onException(String cache, Exception e) {
try {
LogUtils.e(TAG,"onException url:"+url +" useBackupUrl:"+useBackupUrl);
if(!useBackupUrl){
String backupUrl = params.getBackupUrl();
if(StringUtils.isNull(backupUrl)){
setSoDownLoading(url,false);
notifySoDownloadCallBackAndRemove(url,false);
return;
}
if(!url.equals(backupUrl)){
LogUtils.e(TAG,"原地址下载失败,现在采用备用地址下载:"+backupUrl);
setSoDownLoading(url,false);
removeDownloadCallBackAndRemove(url);
start(context,params,true,commomCallBack);
}else{
setSoDownLoading(url,false);
notifySoDownloadCallBackAndRemove(url,false);
return;
}
}else{
setSoDownLoading(url,false);
notifySoDownloadCallBackAndRemove(url,false);
return;
}
}catch (Exception ex){
ex.printStackTrace();
}
}
@Override
public void onFinish(String cache) {
ThreadUtil.addTask(context, new ThreadUtil.ITasker() {
@Override
public Object onExcute() {
try {
String sourcePath = cache;
String dynamicSoPath = getDynamicSoPath(context);
LogUtils.i(TAG,"回调成功,开始拷贝 source:"+sourcePath+" targetPath:"+dynamicSoPath+" copyToSoLoaderDirectory:"+copyToSoLoaderDirectory);
copyFolder(sourcePath,dynamicSoPath);
if(copyToSoLoaderDirectory){
String soLoaderDirectory = getSoLoaderPath(context);
LogUtils.i(TAG, "开始拷贝到SoLoader加载路径 source:" + dynamicSoPath + " targetPath:" + soLoaderDirectory, new Object[0]);
copyFolder(sourcePath,soLoaderDirectory);
}
return true;
}catch (Exception ex){
ex.printStackTrace();
LogUtils.e(TAG,"拷贝异常:"+ex.getMessage());
}
return false;
}
@Override
public void onFinish(Object o) {
setSoDownLoading(url,false);
boolean success = (boolean)o;
if(success){
LogUtils.i(TAG,"拷贝成功");
}else{
LogUtils.e(TAG,"拷贝失败!!!");
}
notifySoDownloadCallBackAndRemove(url,success);
}
});
}
});
//开始预加载
abstractProducer.produce(null);
}catch (Exception ex){
ex.printStackTrace();
}
}
/**
* 启动下载第三方SO,并拷贝进入 so loader
* @param context
* @param url
*/
@Deprecated
public void start(Context context, String url, String matchSoFileName, CommomCallBack commomCallBack){
try {
DynamicSoLoaderParams params = new DynamicSoLoaderParams();
params.setUrl(url);
params.setMatchSoFileName(matchSoFileName);
start(context,params,commomCallBack);
}catch (Exception ex){
ex.printStackTrace();
}
}
private void installNativeLibraryPath(ClassLoader classLoader, File folder)
throws Throwable {
if (folder == null || !folder.exists()) {
LogUtils.e(TAG, "installNativeLibraryPath, folder %s is illegal", folder);
return;
}
// android o sdk_int 26
// for android o preview sdk_int 25
if ((Build.VERSION.SDK_INT == 25 && Build.VERSION.PREVIEW_SDK_INT != 0)
|| Build.VERSION.SDK_INT > 25) {
try {
V25.install(classLoader, folder);
} catch (Throwable throwable) {
// install fail, try to treat it as v23
// some preview N version may go here
LogUtils.e(TAG, "installNativeLibraryPath, v25 fail, sdk: %d, error: %s, try to fallback to V23",
Build.VERSION.SDK_INT, throwable.getMessage());
V23.install(classLoader, folder);
}
} else if (Build.VERSION.SDK_INT >= 23) {
try {
V23.install(classLoader, folder);
} catch (Throwable throwable) {
// install fail, try to treat it as v14
LogUtils.e(TAG, "installNativeLibraryPath, v23 fail, sdk: %d, error: %s, try to fallback to V14",
Build.VERSION.SDK_INT, throwable.getMessage());
V14.install(classLoader, folder);
}
} else if (Build.VERSION.SDK_INT >= 14) {
V14.install(classLoader, folder);
} else {
V4.install(classLoader, folder);
}
}
private static final class V4 {
private static void install(ClassLoader classLoader, File folder) throws Throwable {
String addPath = folder.getPath();
Field pathField = ShareReflectUtil.findField(classLoader, "libPath");
final String origLibPaths = (String) pathField.get(classLoader);
final String[] origLibPathSplit = origLibPaths.split(":");
final StringBuilder newLibPaths = new StringBuilder(addPath);
for (String origLibPath : origLibPathSplit) {
if (origLibPath == null || addPath.equals(origLibPath)) {
continue;
}
newLibPaths.append(':').append(origLibPath);
}
pathField.set(classLoader, newLibPaths.toString());
final Field libraryPathElementsFiled = ShareReflectUtil.findField(classLoader, "libraryPathElements");
final List<String> libraryPathElements = (List<String>) libraryPathElementsFiled.get(classLoader);
final Iterator<String> libPathElementIt = libraryPathElements.iterator();
while (libPathElementIt.hasNext()) {
final String libPath = libPathElementIt.next();
if (addPath.equals(libPath)) {
libPathElementIt.remove();
break;
}
}
libraryPathElements.add(0, addPath);
libraryPathElementsFiled.set(classLoader, libraryPathElements);
}
}
private static final class V14 {
private static void install(ClassLoader classLoader, File folder) throws Throwable {
final Field pathListField = ShareReflectUtil.findField(classLoader, "pathList");
final Object dexPathList = pathListField.get(classLoader);
final Field nativeLibDirField = ShareReflectUtil.findField(dexPathList, "nativeLibraryDirectories");
final File[] origNativeLibDirs = (File[]) nativeLibDirField.get(dexPathList);
final List<File> newNativeLibDirList = new ArrayList<>(origNativeLibDirs.length + 1);
newNativeLibDirList.add(folder);
for (File origNativeLibDir : origNativeLibDirs) {
if (!folder.equals(origNativeLibDir)) {
newNativeLibDirList.add(origNativeLibDir);
}
}
nativeLibDirField.set(dexPathList, newNativeLibDirList.toArray(new File[0]));
}
}
private static final class V23 {
private static void install(ClassLoader classLoader, File folder) throws Throwable {
final Field pathListField = ShareReflectUtil.findField(classLoader, "pathList");
final Object dexPathList = pathListField.get(classLoader);
final Field nativeLibraryDirectories = ShareReflectUtil.findField(dexPathList, "nativeLibraryDirectories");
List<File> origLibDirs = (List<File>) nativeLibraryDirectories.get(dexPathList);
if (origLibDirs == null) {
origLibDirs = new ArrayList<>(2);
}
final Iterator<File> libDirIt = origLibDirs.iterator();
while (libDirIt.hasNext()) {
final File libDir = libDirIt.next();
if (folder.equals(libDir)) {
libDirIt.remove();
break;
}
}
origLibDirs.add(0, folder);
final Field systemNativeLibraryDirectories = ShareReflectUtil.findField(dexPathList, "systemNativeLibraryDirectories");
List<File> origSystemLibDirs = (List<File>) systemNativeLibraryDirectories.get(dexPathList);
if (origSystemLibDirs == null) {
origSystemLibDirs = new ArrayList<>(2);
}
final List<File> newLibDirs = new ArrayList<>(origLibDirs.size() + origSystemLibDirs.size() + 1);
newLibDirs.addAll(origLibDirs);
newLibDirs.addAll(origSystemLibDirs);
final Method makeElements = ShareReflectUtil.findMethod(dexPathList,
"makePathElements", List.class, File.class, List.class);
final ArrayList<IOException> suppressedExceptions = new ArrayList<>();
final Object[] elements = (Object[]) makeElements.invoke(dexPathList, newLibDirs, null, suppressedExceptions);
final Field nativeLibraryPathElements = ShareReflectUtil.findField(dexPathList, "nativeLibraryPathElements");
nativeLibraryPathElements.set(dexPathList, elements);
}
}
private static final class V25 {
private static void install(ClassLoader classLoader, File folder) throws Throwable {
final Field pathListField = ShareReflectUtil.findField(classLoader, "pathList");
final Object dexPathList = pathListField.get(classLoader);
final Field nativeLibraryDirectories = ShareReflectUtil.findField(dexPathList, "nativeLibraryDirectories");
List<File> origLibDirs = (List<File>) nativeLibraryDirectories.get(dexPathList);
if (origLibDirs == null) {
origLibDirs = new ArrayList<>(2);
}
final Iterator<File> libDirIt = origLibDirs.iterator();
while (libDirIt.hasNext()) {
final File libDir = libDirIt.next();
if (folder.equals(libDir)) {
libDirIt.remove();
break;
}
}
origLibDirs.add(0, folder);
final Field systemNativeLibraryDirectories = ShareReflectUtil.findField(dexPathList, "systemNativeLibraryDirectories");
List<File> origSystemLibDirs = (List<File>) systemNativeLibraryDirectories.get(dexPathList);
if (origSystemLibDirs == null) {
origSystemLibDirs = new ArrayList<>(2);
}
final List<File> newLibDirs = new ArrayList<>(origLibDirs.size() + origSystemLibDirs.size() + 1);
newLibDirs.addAll(origLibDirs);
newLibDirs.addAll(origSystemLibDirs);
final Method makeElements = ShareReflectUtil.findMethod(dexPathList, "makePathElements", List.class);
final Object[] elements = (Object[]) makeElements.invoke(dexPathList, newLibDirs);
final Field nativeLibraryPathElements = ShareReflectUtil.findField(dexPathList, "nativeLibraryPathElements");
nativeLibraryPathElements.set(dexPathList, elements);
}
}
/**
* 复制文件夹(使用缓冲字节流)
* @param sourcePath 源文件夹路径
* @param targetPath 目标文件夹路径
*/
private void copyFolder(String sourcePath,String targetPath) throws Exception{
//源文件夹路径
File sourceFile = new File(sourcePath);
//目标文件夹路径
File targetFile = new File(targetPath);
if(!sourceFile.exists()){
throw new Exception("文件夹不存在");
}
if(!sourceFile.isDirectory()){
throw new Exception("源文件夹不是目录");
}
if(!targetFile.exists()){
targetFile.mkdirs();
}
if(!targetFile.isDirectory()){
throw new Exception("目标文件夹不是目录");
}
File[] files = sourceFile.listFiles();
if(files == null || files.length == 0){
throw new Exception("文件夹为空");
}
for(File file : files){
if(file.getName().endsWith(".zip")){
LogUtils.e(TAG,"zip文件不拷贝:"+file.getAbsolutePath());
continue;
}
//文件要移动的路径
String movePath = targetFile+File.separator+file.getName();
if(file.isDirectory()){
//如果是目录则递归调用
copyFolder(file.getAbsolutePath(),targetPath);
}else {
File fileMove = new File(movePath);
if(fileMove.exists()){
LogUtils.i(TAG,"文件已存在,删除,重新拷贝:"+movePath);
fileMove.delete();
}
//如果是文件则复制文件
FileInputStream fileInputStream = null;
BufferedInputStream in = null;
FileOutputStream fileOutputStream = null;
BufferedOutputStream out = null;
try {
fileInputStream = new FileInputStream(file);
in = new BufferedInputStream(fileInputStream);
fileOutputStream = new FileOutputStream(movePath);
out = new BufferedOutputStream(fileOutputStream);
byte[] b = new byte[1024];
int temp = 0;
while((temp = in.read(b)) != -1){
out.write(b,0,temp);
}
}catch (Exception ex){
ex.printStackTrace();
}finally {
try {
if(out!=null){
out.close();
}
if(in!=null){
in.close();
}
if(fileOutputStream!=null){
fileOutputStream.close();
}
if(fileInputStream!=null){
fileInputStream.close();
}
}catch (Exception ex){
ex.printStackTrace();
}
}
}
}
}
}