赞
踩
so文件不打包进apk,在安装完应用打开app的时候通过后台下载so库,将下载下来的so文件再写入到app里面。
Android加载so文件的方式有两种:
System.loadLibrary();
System.load();
它们都可以用来装载库文件,但是System.load参数必须为库文件的绝对路径,可以是任意路径;System.loadLibrary参数为库文件名,不包含库文件的扩展名,必须是在JVM属性java.library.path所指向的路径中,路径可以通过System.getProperty(‘java.library.path’) 获得。所有动态加载的时候我们不能用System.loadLibrary,只能用System.load来加载。
最终的结果如下图:我们把我们自定义的路径加入到系统能认识的dexPathList里面
Field pathListField = ShareReflectUtil.findField(classLoader, "pathList"); Object dexPathList = pathListField.get(classLoader); Field nativeLibraryDirectories = ShareReflectUtil.findField(dexPathList, "nativeLibraryDirectories"); List<File> libDirs = (List<File>) nativeLibraryDirectories.get(dexPathList); libDirs.add(0, folder); Field systemNativeLibraryDirectories = // 就是这个路径,把我们下载的路径添加到这里 ShareReflectUtil.findField(dexPathList, "systemNativeLibraryDirectories"); List<File> systemLibDirs = (List<File>) systemNativeLibraryDirectories.get(dexPathList); Method makePathElements = ShareReflectUtil.findMethod(dexPathList, "makePathElements", List.class); libDirs.addAll(systemLibDirs); Object[] elements = (Object[]) makePathElements. invoke(dexPathList, libDirs); Field nativeLibraryPathElements = ShareReflectUtil.findField(dexPathList, "nativeLibraryPathElements"); nativeLibraryPathElements.setAccessible(true); nativeLibraryPathElements.set(dexPathList, elements);
packagingOptions {
exclude 'lib/armeabi/libindoor.so'
exclude 'lib/armeabi/libBaiduMapSDK_base_v5_2_0.so'
exclude 'lib/armeabi/liblocSDK7b.so'
}
private void dynamicSo() {
String dir = FileUtils.getAppRecordDir(sApplication).toString();
SoFile.loadSoFile(sApplication, dir);
}
public class TinkerLoadLibrary { private static final String TAG = "Tinker.LoadLibrary"; public static synchronized void installNativeLibraryPath(ClassLoader classLoader, File folder) throws Throwable { if (folder == null || !folder.exists()) { Logger.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 && getPreviousSdkInt() != 0) || Build.VERSION.SDK_INT > 25) { try { V25.install(classLoader, folder); return; } catch (Throwable throwable) { // install fail, try to treat it as v23 // some preview N version may go here Logger.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 Logger.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); } } /** * fuck部分机型删了该成员属性,兼容 * * @return 被厂家删了返回1,否则正常读取 */ @TargetApi(Build.VERSION_CODES.M) private static int getPreviousSdkInt() { try { return Build.VERSION.PREVIEW_SDK_INT; } catch (Throwable ignore) { } return 1; } private static final class V14 { private static void install(ClassLoader classLoader, File folder) throws Throwable { Field pathListField = ShareReflectUtil.findField(classLoader, "pathList"); Object dexPathList = pathListField.get(classLoader); ShareReflectUtil.expandFieldArray(dexPathList, "nativeLibraryDirectories", new File[]{folder}); } } private static final class V23 { private static void install(ClassLoader classLoader, File folder) throws Throwable { Field pathListField = ShareReflectUtil.findField(classLoader, "pathList"); Object dexPathList = pathListField.get(classLoader); Field nativeLibraryDirectories = ShareReflectUtil.findField(dexPathList, "nativeLibraryDirectories"); List<File> libDirs = (List<File>) nativeLibraryDirectories.get(dexPathList); libDirs.add(0, folder); Field systemNativeLibraryDirectories = ShareReflectUtil.findField(dexPathList, "systemNativeLibraryDirectories"); List<File> systemLibDirs = (List<File>) systemNativeLibraryDirectories.get(dexPathList); Method makePathElements = ShareReflectUtil.findMethod(dexPathList, "makePathElements", List.class, File.class, List.class); ArrayList<IOException> suppressedExceptions = new ArrayList<>(); libDirs.addAll(systemLibDirs); Object[] elements = (Object[]) makePathElements. invoke(dexPathList, libDirs, null, suppressedExceptions); Field nativeLibraryPathElements = ShareReflectUtil.findField(dexPathList, "nativeLibraryPathElements"); nativeLibraryPathElements.setAccessible(true); nativeLibraryPathElements.set(dexPathList, elements); } } private static final class V25 { private static void install(ClassLoader classLoader, File folder) throws Throwable { Field pathListField = ShareReflectUtil.findField(classLoader, "pathList"); Object dexPathList = pathListField.get(classLoader); Field nativeLibraryDirectories = ShareReflectUtil.findField(dexPathList, "nativeLibraryDirectories"); List<File> libDirs = (List<File>) nativeLibraryDirectories.get(dexPathList); libDirs.add(0, folder); Field systemNativeLibraryDirectories = ShareReflectUtil.findField(dexPathList, "systemNativeLibraryDirectories"); List<File> systemLibDirs = (List<File>) systemNativeLibraryDirectories.get(dexPathList); Method makePathElements = ShareReflectUtil.findMethod(dexPathList, "makePathElements", List.class); libDirs.addAll(systemLibDirs); Object[] elements = (Object[]) makePathElements. invoke(dexPathList, libDirs); Field nativeLibraryPathElements = ShareReflectUtil.findField(dexPathList, "nativeLibraryPathElements"); nativeLibraryPathElements.setAccessible(true); nativeLibraryPathElements.set(dexPathList, elements); } } }
// 获取路径的类 public class ShareReflectUtil { /** * Locates a given field anywhere in the class inheritance hierarchy. * * @param instance an object to search the field into. * @param name field name * @return a field object * @throws NoSuchFieldException if the field cannot be located */ public static Field findField(Object instance, String name) throws NoSuchFieldException { for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) { try { Field field = clazz.getDeclaredField(name); if (!field.isAccessible()) { field.setAccessible(true); } return field; } catch (NoSuchFieldException e) { // ignore and search next } } throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass()); } public static Field findField(Class<?> originClazz, String name) throws NoSuchFieldException { for (Class<?> clazz = originClazz; clazz != null; clazz = clazz.getSuperclass()) { try { Field field = clazz.getDeclaredField(name); if (!field.isAccessible()) { field.setAccessible(true); } return field; } catch (NoSuchFieldException e) { // ignore and search next } } throw new NoSuchFieldException("Field " + name + " not found in " + originClazz); } /** * Locates a given method anywhere in the class inheritance hierarchy. * * @param instance an object to search the method into. * @param name method name * @param parameterTypes method parameter types * @return a method object * @throws NoSuchMethodException if the method cannot be located */ public static Method findMethod(Object instance, String name, Class<?>... parameterTypes) throws NoSuchMethodException { for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) { try { Method method = clazz.getDeclaredMethod(name, parameterTypes); if (!method.isAccessible()) { method.setAccessible(true); } return method; } catch (NoSuchMethodException e) { // ignore and search next } } throw new NoSuchMethodException("Method " + name + " with parameters " + Arrays.asList(parameterTypes) + " not found in " + instance.getClass()); } /** * Locates a given method anywhere in the class inheritance hierarchy. * * @param clazz a class to search the method into. * @param name method name * @param parameterTypes method parameter types * @return a method object * @throws NoSuchMethodException if the method cannot be located */ public static Method findMethod(Class<?> clazz, String name, Class<?>... parameterTypes) throws NoSuchMethodException { for (; clazz != null; clazz = clazz.getSuperclass()) { try { Method method = clazz.getDeclaredMethod(name, parameterTypes); if (!method.isAccessible()) { method.setAccessible(true); } return method; } catch (NoSuchMethodException e) { // ignore and search next } } throw new NoSuchMethodException("Method " + name + " with parameters " + Arrays.asList(parameterTypes) + " not found in " + clazz); } /** * Locates a given constructor anywhere in the class inheritance hierarchy. * * @param instance an object to search the constructor into. * @param parameterTypes constructor parameter types * @return a constructor object * @throws NoSuchMethodException if the constructor cannot be located */ public static Constructor<?> findConstructor(Object instance, Class<?>... parameterTypes) throws NoSuchMethodException { for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) { try { Constructor<?> ctor = clazz.getDeclaredConstructor(parameterTypes); if (!ctor.isAccessible()) { ctor.setAccessible(true); } return ctor; } catch (NoSuchMethodException e) { // ignore and search next } } throw new NoSuchMethodException("Constructor" + " with parameters " + Arrays.asList(parameterTypes) + " not found in " + instance.getClass()); } /** * Replace the value of a field containing a non null array, by a new array containing the * elements of the original array plus the elements of extraElements. * * @param instance the instance whose field is to be modified. * @param fieldName the field to modify. * @param extraElements elements to append at the end of the array. */ public static void expandFieldArray(Object instance, String fieldName, Object[] extraElements) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException { Field jlrField = findField(instance, fieldName); Object[] original = (Object[]) jlrField.get(instance); Object[] combined = (Object[]) Array.newInstance(original.getClass().getComponentType(), original.length + extraElements.length); // NOTE: changed to copy extraElements first, for patch load first System.arraycopy(extraElements, 0, combined, 0, extraElements.length); System.arraycopy(original, 0, combined, extraElements.length, original.length); jlrField.set(instance, combined); } /** * Replace the value of a field containing a non null array, by a new array containing the * elements of the original array plus the elements of extraElements. * * @param instance the instance whose field is to be modified. * @param fieldName the field to modify. */ public static void reduceFieldArray(Object instance, String fieldName, int reduceSize) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException { if (reduceSize <= 0) { return; } Field jlrField = findField(instance, fieldName); Object[] original = (Object[]) jlrField.get(instance); int finalLength = original.length - reduceSize; if (finalLength <= 0) { return; } Object[] combined = (Object[]) Array.newInstance(original.getClass().getComponentType(), finalLength); System.arraycopy(original, reduceSize, combined, 0, finalLength); jlrField.set(instance, combined); } public static Object getActivityThread(Context context, Class<?> activityThread) { try { if (activityThread == null) { activityThread = Class.forName("android.app.ActivityThread"); } Method m = activityThread.getMethod("currentActivityThread"); m.setAccessible(true); Object currentActivityThread = m.invoke(null); if (currentActivityThread == null && context != null) { // In older versions of Android (prior to frameworks/base 66a017b63461a22842) // the currentActivityThread was built on thread locals, so we'll need to try // even harder Field mLoadedApk = context.getClass().getField("mLoadedApk"); mLoadedApk.setAccessible(true); Object apk = mLoadedApk.get(context); Field mActivityThreadField = apk.getClass().getDeclaredField("mActivityThread"); mActivityThreadField.setAccessible(true); currentActivityThread = mActivityThreadField.get(apk); } return currentActivityThread; } catch (Throwable ignore) { return null; } } /** * Handy method for fetching hidden integer constant value in system classes. * * @param clazz * @param fieldName * @return */ public static int getValueOfStaticIntField(Class<?> clazz, String fieldName, int defVal) { try { final Field field = findField(clazz, fieldName); return field.getInt(null); } catch (Throwable thr) { return defVal; } } }
/** * 文件描述:提供一个文件读写和加载指定路径下so的类 * * @author :feilong on 2018/11/6 */ public class SoFile { /** * 加载 so 文件(直接指定你so下载的路径即可) * * @param context * @param fromPath 下载到得sdcard目录 */ public static void loadSoFile(Context context, String fromPath) { File dir = context.getDir("libs", Context.MODE_PRIVATE); if (!isLoadSoFile(dir)) { copy(fromPath, dir.getAbsolutePath()); } try { TinkerLoadLibrary.installNativeLibraryPath(VchatApplication.getMyApplication().getBaseContext().getClassLoader(), dir); } catch (Throwable throwable) { Logger.e(throwable.getMessage()); } } /** * 判断 so 文件是否存在 * * @param dir * @return */ public static boolean isLoadSoFile(File dir) { File[] currentFiles; currentFiles = dir.listFiles(); boolean hasSoLib = false; if (currentFiles == null) { return false; } for (int i = 0; i < currentFiles.length; i++) { if (currentFiles[i].getName().contains("libwedsa23")) { hasSoLib = true; } } return hasSoLib; } /** * @param fromFile 指定的下载目录 * @param toFile 应用的包路径 * @return */ public static int copy(String fromFile, String toFile) { //要复制的文件目录 File root = new File(fromFile); //如同判断SD卡是否存在或者文件是否存在,如果不存在则 return出去 if (!root.exists()) { return -1; } //如果存在则获取当前目录下的全部文件 填充数组 File[] currentFiles = root.listFiles(); //目标目录 File targetDir = new File(toFile); //创建目录 if (!targetDir.exists()) { targetDir.mkdirs(); } if (currentFiles != null && currentFiles.length > 0) { //遍历要复制该目录下的全部文件 for (File currentFile : currentFiles) { if (currentFile.isDirectory()) { //如果当前项为子目录 进行递归 copy(currentFile.getPath() + "/", toFile + currentFile.getName() + "/"); } else { //如果当前项为文件则进行文件拷贝 if (currentFile.getName().contains(".so")) { int id = copySdcardFile(currentFile.getPath(), toFile + File.separator + currentFile.getName()); } } } } return 0; } //文件拷贝 //要复制的目录下的所有非子目录(文件夹)文件拷贝 public static int copySdcardFile(String fromFile, String toFile) { try { FileInputStream fosfrom = new FileInputStream(fromFile); FileOutputStream fosto = new FileOutputStream(toFile); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = -1; while ((len = fosfrom.read(buffer)) != -1) { baos.write(buffer, 0, len); } // 从内存到写入到具体文件 fosto.write(baos.toByteArray()); // 关闭文件流 baos.close(); fosto.close(); fosfrom.close(); return 0; } catch (Exception ex) { return -1; } } }
(因为我也不会写原理,再不上代码,还TM写个毛啊…)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。