熱修復 - Tinker

關於Tinker 熱修復

熱修復的優點

① 無需從新發布版本,省時省力java

② 用戶無感修復,也無需下載最新應用,代價小android

③ 修復成功率搞,把損失降到最低git

Tinker能夠完成哪些修復

① 代碼修復github

② 資源修復數組

③ SO庫修復bash

image

  • 熱修復流程圖

image

  • 類加載器BaseDexClassLoader的子類是DexClassLoader,這個類主要的方法是一個數據pathList,DexElements的數組中有class.dex是主包,還有不少**.dex文件,緣由是由於javaC會把咱們的java文件編譯成class文件,再由class文件經過sdk/build-tools/版本號/dx.bat工具轉換成dex。
  • 建立咱們本身的類加載器加載修復包例如classes2.dex(這個文件是從服務器進行下載)而後保存到私有目錄中(data/app/....)
  • 將咱們本身的dex和系統的dex進行合併,生成一個新的dexElements數組,並把咱們本身的dex放在數組最前面,這樣優先級最高,這樣就不會加載到錯誤的類了
  • 經過反射技術Reflect將咱們新的dexElements賦值給系統的pathList

配置Gradle

① 在build.gradle defaultConfig裏添加服務器

//開啓分包
         multiDexEnabled true
         //設置分包配置
         multiDexKeepFile file('multidex-config.txt')
複製代碼

② 在build.gradle android裏添加網絡

dexOptions {
                     javaMaxHeapSize "4g"
                     preDexLibraries = false
                     additionalParameters = [//配置multiDex參數
                                   '--multi-dex',
                                   //每一個包內方法數上限
                                   '--set-max-idx-number=50000',
                                   //打包到主 classes.dex的文件列表
                                   '--minimal-main-dex'
                                     ]
                     }
複製代碼

③ 添加multidex 支持依賴app

implementation 'com.android.support:multidex:1.0.3'
複製代碼

④ 引入 moduleide

implementation project(':HaoLinHotFixLibrary')
複製代碼

⑤ 建立 multidex-config.txt 文件 app目錄下 作一些主要類的引用

com/haolin/hotfix/MainActivity.class
         com/haolin/hotfix/base/BaseActivity.class
         com/haolin/hotfix/base/BaseApplication.class
複製代碼

用法

初始化

  • 基礎用法 Application須要繼承MultiDexApplication

    BaseApplication extends MultiDexApplication
    複製代碼

建立私有目錄

//修復包  現不作網絡下載 從手機裏拿
       File sourceFile = new File
       (Environment.getExternalStorageDirectory(), Constants.DEX_NAME);

       //目標路徑 私有目錄
       File targetFile = new File(getDir(Constants.DEX_DIR, Context.MODE_PRIVATE)
       .getAbsolutePath() + File.separator + Constants.DEX_NAME);

       if (targetFile.exists()){
           targetFile.delete();
       }
       try {
           FileUtils.copyFile(sourceFile, targetFile);
           FixDexUtils.loadFixedDex(this);
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
複製代碼

加載熱修復文件並經過反射技術插樁

package com.haolin.hotfix.library;

import android.content.Context;

import com.haolin.hotfix.library.utils.ArrayUtils;
import com.haolin.hotfix.library.utils.Constants;
import com.haolin.hotfix.library.utils.ReflectUtils;

import java.io.File;
import java.util.HashSet;

import dalvik.system.DexClassLoader;
import dalvik.system.PathClassLoader;

/**
* 做者:haoLin_Lee on 2019/04/19 11:43
* 郵箱:Lhaolin0304@sina.com
* class: 加載熱修復文件
*/
public class FixDexUtils {

   private static HashSet<File> loadeDex = new HashSet<>();

   static {
       //修復以前清空集合
       loadeDex.clear();
   }

   public static void loadFixedDex(Context context) {

       File fileDir = context.getDir(Constants.DEX_DIR, Context.MODE_PRIVATE);
       //循環私有目錄的全部文件
       File[] listFiles = fileDir.listFiles();
       for (File file : listFiles) {
           if (file.getName().endsWith(Constants.DEX_SUFFIX) &&
           !"class.dex".equals(file.getName())) {

               loadeDex.add(file);
           }
       }
       //模擬類加載器
       createDexClassLoader(context, fileDir);
   }

   //建立加載補丁的DexClassLoad 類加載器
   private static void createDexClassLoader(Context context, File fileDir) {
       //建立解壓目錄
       String optimizedDir = fileDir.getAbsolutePath() + 
        File.separator + "opt_dex";
       //建立目錄
       File fopt = new File(optimizedDir);
       if (!fopt.exists()) {
           //建立多級目錄
           fopt.mkdirs();
       }
       for (File dex : loadeDex) {
           //自有的類加載器
           DexClassLoader classLoader = new DexClassLoader
           (dex.getAbsolutePath(), optimizedDir, null, context.getClassLoader());
           //每循環一次 修復一次(插裝)
           hotFix(classLoader, context);
       }
   }

   private static void hotFix(DexClassLoader classLoader, Context context) {
       //獲取系統的pathClassLoader
       PathClassLoader pathLoader = (PathClassLoader) context.getClassLoader();
       try {
           //獲取自有的dexElement數組
           Object myElements = ReflectUtils.getDexElements(ReflectUtils.
           getPathList(classLoader));
           //獲取系統的dexElement數組
           Object systemElements = ReflectUtils.getDexElements(ReflectUtils.
           getPathList(pathLoader));
           //合併而且生成新的dexElements數組
           Object dexElements = ArrayUtils.combineArray(myElements, systemElements);
           //獲取系統的pathList
           Object systemPathList = ReflectUtils.getPathList(pathLoader);
           //經過反射技術,將新的dexElements 數組賦值給系統的pathList
           ReflectUtils.setField(systemPathList, systemPathList.getClass(), dexElements);
       } catch (Exception e) {
           e.printStackTrace();
       }
   }

}
複製代碼

修復

將修復好的apk包利用解壓工具打開,裏面會有classes2.dex文件,與舊版本apk和該classes2.dex文件同時複製到手機裏在實際開發項目中應該將classes2.dex文件放在服務器,進行下載修復,我這只是方面demo主要了解下核心原理,主要就是插樁技術。

Demo地址

總結

Tinker熱修復主要運用framework層技術,瞭解插樁原理,核心代碼就是反射技術實現

//獲取系統的pathClassLoader
      PathClassLoader pathLoader = (PathClassLoader) context.getClassLoader();
      try {
          //獲取自有的dexElement數組
          Object myElements = ReflectUtils.getDexElements(ReflectUtils.getPathList
          (classLoader));
          //獲取系統的dexElement數組
          Object systemElements = ReflectUtils.getDexElements(ReflectUtils.
          getPathList(pathLoader));
          //合併而且生成新的dexElements數組
          Object dexElements = ArrayUtils.combineArray(myElements, systemElements);
          //獲取系統的pathList
          Object systemPathList = ReflectUtils.getPathList(pathLoader);
          //經過反射技術,將新的dexElements 數組賦值給系統的pathList
          ReflectUtils.setField(systemPathList, systemPathList.getClass(), dexElements);
      } catch (Exception e) {
          e.printStackTrace();
      }
複製代碼

感謝

謝謝你們的閱讀,想要了解更多,請關注我

GitHub

相關文章
相關標籤/搜索