Android Tinker核心架构剖析(1)

一、Tinker核心架构总览

1.1 架构设计目标与核心价值

Tinker作为Android热修复框架,核心目标是在不重启App的情况下实现代码、资源和So库的动态更新。其架构设计围绕高效差分合成稳定加载机制系统兼容性展开,核心价值体现在:

  • 最小化补丁包体积:通过bsdiff/bspatch算法生成差分补丁
  • 多维度修复支持:涵盖Dex、资源文件、So库的修复
  • 低侵入性:对原有App工程的代码改动极小
  • 高兼容性:适配Android多版本、多厂商系统差异

1.2 整体架构分层模型

Tinker架构可分为基础层核心处理层应用接口层

  1. 基础层:提供底层支撑,包括文件系统操作、进程管理、ClassLoader适配等。
  2. 核心处理层:实现补丁包的核心处理逻辑,如差分合成、加载重定向、资源修复等。
  3. 应用接口层:暴露对外API,供开发者进行初始化配置、补丁加载等操作。

二、基础层:底层能力支撑

2.1 文件系统与存储管理

Tinker通过com.tencent.tinker.lib.util.TinkerIO类实现文件操作:

public class TinkerIO {
    // 从输入流读取数据到文件
    public static boolean saveToFile(InputStream inputStream, File destFile) {
        FileOutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(destFile);
            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream.read(buffer)) > 0) {
                outputStream.write(buffer, 0, length);
            }
            return true;
        } catch (IOException e) {
            // 捕获并记录异常
            TinkerLog.e("TinkerIO", "saveToFile fail: %s", e.getMessage());
            return false;
        } finally {
            // 关闭输入输出流
            closeQuietly(inputStream);
            closeQuietly(outputStream);
        }
    }

    // 静默关闭流,避免抛出异常
    public static void closeQuietly(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (IOException e) {
                TinkerLog.e("TinkerIO", "closeQuietly fail: %s", e.getMessage());
            }
        }
    }
}

补丁文件的存储路径管理由com.tencent.tinker.lib.tinker.TinkerInstaller负责:

public class TinkerInstaller {
    private static final String TINKER_DIR = "tinker";
    private static final String PATCH_DIR = "patch";

    // 获取Tinker根目录
    public static File getTinkerDir(Context context) {
        File cacheDir = context.getCacheDir();
        return new File(cacheDir, TINKER_DIR);
    }

    // 获取补丁文件目录
    public static File getPatchDir(Context context) {
        File tinkerDir = getTinkerDir(context);
        return new File(tinkerDir, PATCH_DIR);
    }
}

2.2 进程管理与生命周期处理

Tinker通过com.tencent.tinker.lib.tinker.Tinker类管理补丁加载流程与进程状态:

public class Tinker {
    private static final String TAG = "Tinker";
    private static Tinker tinker;

    // 单例模式获取Tinker实例
    public static Tinker with(Context context) {
        if (tinker == null) {
            tinker = new Tinker(context);
        }
        return tinker;
    }

    private Tinker(Context context) {
        // 初始化Tinker配置
        TinkerLoadResult loadResult = TinkerLoadResult.load(context);
        if (loadResult.isSuccess()) {
            // 加载成功后的处理逻辑
            TinkerLog.i(TAG, "Tinker load success");
        } else {
            // 加载失败处理
            TinkerLog.e(TAG, "Tinker load fail: %s", loadResult.getError());
        }
    }
}

在进程重启场景下,com.tencent.tinker.lib.tinker.TinkerLoadResult负责记录加载状态:

public class TinkerLoadResult {
    private boolean success;
    private String error;

    // 从配置文件加载加载结果
    public static TinkerLoadResult load(Context context) {
        try {
            // 读取配置文件获取加载状态
            File configFile = new File(TinkerInstaller.getTinkerDir(context), "tinker_config.txt");
            if (!configFile.exists()) {
                return new TinkerLoadResult(false, "config file not exist");
            }
            BufferedReader reader = new BufferedReader(new FileReader(configFile));
            String line = reader.readLine();
            if ("success".equals(line)) {
                return new TinkerLoadResult(true, null);
            } else {
                return new TinkerLoadResult(false, line);
            }
        } catch (IOException e) {
            return new TinkerLoadResult(false, e.getMessage());
        }
    }

    private TinkerLoadResult(boolean success, String error) {
        this.success = success;
        this.error = error;
    }

    public boolean isSuccess() {
        return success;
    }

    public String getError() {
        return error;
    }
}

2.3 ClassLoader适配机制

Tinker通过自定义TinkerClassLoader实现类加载重定向:

public class TinkerClassLoader extends DexClassLoader {
    private final File optimizedDirectory;
    private final String libraryPath;
    private final ClassLoader parent;

    // 构造函数初始化参数
    public TinkerClassLoader(String dexPath, File optimizedDirectory,
                             String libraryPath, ClassLoader parent) {
        super(dexPath, optimizedDirectory, libraryPath, parent);
        this.optimizedDirectory = optimizedDirectory;
        this.libraryPath = libraryPath;
        this.parent = parent;
    }

    // 重写findClass方法实现类加载逻辑
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            // 尝试从补丁Dex中加载类
            return super.findClass(name);
        } catch (ClassNotFoundException e) {
            // 若失败,委托给父ClassLoader
            return parent.loadClass(name);
        }
    }
}

在应用启动时,com.tencent.tinker.lib.tinker.Tinker会替换默认的ClassLoader

public class Tinker {
    private void replaceClassLoader(Context context) {
        File patchDir = TinkerInstaller.getPatchDir(context);
        File optimizedDir = new File(patchDir, "odex");
        if (!optimizedDir.exists()) {
            optimizedDir.mkdirs();
        }
        String dexPath = new File(patchDir, "patch.dex").getAbsolutePath();
        ClassLoader parentClassLoader = context.getClassLoader();
        // 创建TinkerClassLoader实例
        ClassLoader tinkerClassLoader = new TinkerClassLoader(
            dexPath, optimizedDir, null, parentClassLoader);
        // 设置新的ClassLoader
        setClassLoader(context, tinkerClassLoader);
    }

    // 设置应用的ClassLoader
    private void setClassLoader(Context context, ClassLoader classLoader) {
        try {
            Field loaderField = Context.class.getDeclaredField("mClassLoader");
            loaderField.setAccessible(true);
            loaderField.set(context, classLoader);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            TinkerLog.e("Tinker", "setClassLoader fail: %s", e.getMessage());
        }
    }
}

三、核心处理层:补丁处理逻辑

3.1 差分合成算法实现

Tinker采用bsdiff/bspatch算法生成和应用差分补丁:

  • bsdiff:用于生成补丁文件,对比新旧Dex文件生成差异数据。
  • bspatch:用于合成补丁,将差异数据应用到旧Dex文件生成新Dex。

补丁合成核心逻辑在com.tencent.tinker.lib.tinker.TinkerPatch类中:

public class TinkerPatch {
    private static final String TAG = "TinkerPatch";

    // 应用补丁文件
    public static boolean applyPatch(Context context, File oldDexFile, File patchFile, File outputDexFile) {
        try {
            // 执行bspatch命令进行合成
            Process process = new ProcessBuilder("sh", "-c",
                String.format("bspatch %s %s %s", oldDexFile.getAbsolutePath(),
                              outputDexFile.getAbsolutePath(), patchFile.getAbsolutePath()))
                .redirectErrorStream(true)
                .start();
            int exitCode = process.waitFor();
            if (exitCode == 0) {
                TinkerLog.i(TAG, "Patch apply success");
                return true;
            } else {
                TinkerLog.e(TAG, "Patch apply fail, exit code: %d", exitCode);
                return false;
            }
        } catch (IOException | InterruptedException e) {
            TinkerLog.e(TAG, "Patch apply fail: %s", e.getMessage());
            return false;
        }
    }
}

3.2 Dex文件加载与重定向

Tinker通过DexFileFactory类处理Dex文件的加载:

public class DexFileFactory {
    private static final String TAG = "DexFileFactory";

    // 加载Dex文件
    public static DexFile loadDexFile(Context context, File dexFile) {
        try {
            // 创建DexFile实例
            return DexFile.loadDex(dexFile.getAbsolutePath(),
                                   new File(context.getCacheDir(), "tinker_odex").getAbsolutePath(), 0);
        } catch (IOException e) {
            TinkerLog.e(TAG, "loadDexFile fail: %s", e.getMessage());
            return null;
        }
    }
}

在类加载过程中,TinkerClassLoader会优先从补丁Dex中查找类:

public class TinkerClassLoader extends DexClassLoader {
    private final DexFile patchDexFile;

    public TinkerClassLoader(String dexPath, File optimizedDirectory,
                             String libraryPath, ClassLoader parent) {
        super(dexPath, optimizedDirectory, libraryPath, parent);
        this.patchDexFile = DexFileFactory.loadDexFile(null, new File(dexPath));
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            // 从补丁Dex中查找类
            Class<?> clazz = patchDexFile.loadClass(name, this);
            if (clazz != null) {
                return clazz;
            }
        } catch (IOException | ClassNotFoundException e) {
            // 忽略异常,继续委托给父ClassLoader
        }
        return super.findClass(name);
    }
}

3.3 资源修复机制

Tinker通过ResourcePatch类处理资源补丁:

public class ResourcePatch {
    private static final String TAG = "ResourcePatch";

    // 应用资源补丁
    public static boolean applyResourcePatch(Context context, File patchFile) {
        try {
            // 解析资源补丁文件
            // (实际逻辑涉及AssetManager操作)
            AssetManager assetManager = context.getAssets();
            Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
            addAssetPath.invoke(assetManager, patchFile.getAbsolutePath());

            // 更新资源加载上下文
            Context newContext = context.createConfigurationContext(context.getResources().getConfiguration());
            newContext.setAssets(assetManager);
            // 替换应用的资源上下文
            setActivityThreadResources(newContext);

            TinkerLog.i(TAG, "Resource patch apply success");
            return true;
        } catch (Exception e) {
            TinkerLog.e(TAG, "Resource patch apply fail: %s", e.getMessage());
            return false;
        }
    }

    // 替换ActivityThread中的Resources实例
    private static void setActivityThreadResources(Context newContext) {
        try {
            Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
            Field sCurrentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
            sCurrentActivityThreadField.setAccessible(true);
            Object activityThread = sCurrentActivityThreadField.get(null);

            Field mResourcesField = activityThreadClass.getDeclaredField("mResources");
            mResourcesField.setAccessible(true);
            Resources newResources = newContext.getResources();
            mResourcesField.set(activityThread, newResources);
        } catch (Exception e) {
            TinkerLog.e(TAG, "setActivityThreadResources fail: %s", e.getMessage());
        }
    }
}

四、应用接口层:对外API设计

4.1 Tinker初始化配置

开发者通过TinkerApplication类进行初始化:

public class TinkerApplication extends MultiDexApplication {
    private static final String TAG = "TinkerApplication";

    @Override
    public void onCreate() {
        super.onCreate();
        // 初始化Tinker配置
        TinkerInstaller.install(this);
        Tinker tinker = Tinker.with(this);
        if (tinker.isTinkerLoaded()) {
            TinkerLog.i(TAG, "Tinker is already loaded");
        } else {
            // 加载补丁逻辑
            tinker.loadPatch();
        }
    }
}

TinkerInstaller类负责具体的初始化操作:

public class TinkerInstaller {
    public static void install(Context context) {
        // 检查Tinker是否已初始化
        if (isTinkerInstalled(context)) {
            return;
        }
        // 初始化文件目录
        File tinkerDir = getTinkerDir(context);
        if (!tinkerDir.exists()) {
            tinkerDir.mkdirs();
        }

        // 注册Tinker回调
        TinkerManager.setTinkerApplicationLike(new TinkerApplicationLike() {
            // 实现回调方法
            @Override
            public void onCreate() {
                // 初始化完成后的处理
            }

            @Override
            public void onTerminate() {
                // 应用终止时的处理
            }
        });
    }

    private static boolean isTinkerInstalled(Context context) {
        File tinkerDir = getTinkerDir(context);
        return tinkerDir.exists();
    }
}

4.2 补丁加载与管理API

Tinker类提供补丁加载接口:

public class Tinker {
    // 加载补丁文件
    public void loadPatch() {
        File patchDir = TinkerInstaller.getPatchDir(null);
        File patchFile = new File(patchDir, "patch.dex");
        if (!patchFile.exists()) {
            TinkerLog.e("Tinker", "Patch file not exist");
            return;
        }
        // 执行补丁应用逻辑
        if (TinkerPatch.applyPatch(null, null, patchFile, null)) {
            TinkerLog.i("Tinker", "Patch load success");
            // 重启应用使补丁生效
            restartApplication();
        } else {
            TinkerLog.e("Tinker", "Patch load fail");
        }
    }

    // 重启应用
    private void restartApplication() {
        Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName());
        PendingIntent restartIntent = PendingIntent.getActivity(
            null, 0, intent, PendingIntent.FLAG_ONE_SHOT);
        AlarmManager mgr = (AlarmManager) getSystemService(Context.ALARM_MANAGER);
        mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, restartIntent);
        android.os.Process.killProcess(android.os.Process.myPid());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Android 小码蜂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值