热更新

热更新技术
我这里使用没有使用阿里巴巴的黑科技,腾讯的之类的。

首先看效果图

  • 出现错误界面

  • 正常界面 弹出toast。

  • 详细看demo中的代码

    1. 首先是Test类。很简单。就是个测试,故意出错报个错误

      public class Test {
      public static void show(Context context) {
      int i = 10;
      //这里过一会 会把j的值改成1。这样子就正确
      int j = 0;

          Toast.makeText(context, "shit:" + i / j, Toast.LENGTH_SHORT).show();
          }
      

      }

    2. Activity的代码,具体作用会在下面写出来

      public class FixDesActivity extends AppCompatActivity {

      @Override
      protected void onCreate(@Nullable Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_fix);
      }
      
      
      public void inject(View view) {
          // 无bug的classes2.dex文件存放地址
          String sourceFile = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator
                  + "classes2.dex";
          // 系统的私有目录
          String targetFile = this.getDir("odex", Context.MODE_PRIVATE).getAbsolutePath() + File.separator
                  + "classes2.dex";
          try {
              // 复制文件到私有目录
              FileUtils.copyFile(sourceFile, targetFile);
      
              // 加载.dex文件
              FixDexUtils.loadFixDex(this.getApplication());
      
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
      
      public void test(View view) {
          Test.show(this);
      }
      

      }

    3. FixDexUtils类的代码

      public class FixDexUtils {
      
          private static HashSet<File> loadedDex = new HashSet<File>();
      
          static {
              loadedDex.clear();
          }
      
          public static void loadFixDex(Context context) {
              // 获取到系统的odex 目录
              File fileDir = context.getDir("odex", Context.MODE_PRIVATE);
              File[] listFiles = fileDir.listFiles();
      
              for (File file : listFiles) {
                  if (file.getName().endsWith(".dex")) {
                      //存储该目录下的.dex结尾的文件(补丁)
                      loadedDex.add(file);
                  }
              }
              doDexInject(context, fileDir);
          }
      
          /**
           * 合并之前的apk
           *
           * @param context
           * @param fileDir
           */
          private static void doDexInject(Context context, File fileDir) {
              // .dex 的加载需要一个临时目录
              String optimizeDir = fileDir.getAbsolutePath() + File.separator + "opt_dex";
              File fopt = new File(optimizeDir);
              if (!fopt.exists()) {
                  fopt.mkdirs();
              }
              //加载应用程序的dex
              // 根据.dex 文件创建对应的DexClassLoader 类
              for (File file : loadedDex) {
                  DexClassLoader classLoader = new DexClassLoader(file.getAbsolutePath()
                          , fopt.getAbsolutePath(), null, context.getClassLoader());
                  inject(classLoader, context);
              }
          }
      
          private static void inject(DexClassLoader classLoader, Context context) {
              // 获取到系统的DexClassLoader 类
              PathClassLoader pathLoader = (PathClassLoader) context.getClassLoader();
              try {
                  // 分别获取到补丁的dexElements和系统的dexElements
                  Object dexElements = combineArray(getDexElements(getPathList(classLoader)),
                          getDexElements(getPathList(pathLoader)));
                  // 获取到系统的pathList 对象
                  Object pathList = getPathList(pathLoader);
                  // 设置系统的dexElements 的值
                  setField(pathList, pathList.getClass(), "dexElements", dexElements);
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      
          /**
           * 通过反射设置字段值
           */
          private static void setField(Object obj, Class<?> cl, String field, Object value)
                  throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
      
              Field localField = cl.getDeclaredField(field);
              localField.setAccessible(true);
              localField.set(obj, value);
          }
      
          /**
           * 通过反射获取 BaseDexClassLoader中的PathList对象
           */
          private static Object getPathList(Object baseDexClassLoader)
                  throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
              return getField(baseDexClassLoader, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList");
          }
      
          /**
           * 通过反射获取指定字段的值
           */
          private static Object getField(Object obj, Class<?> cl, String field)
                  throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
              Field localField = cl.getDeclaredField(field);
              localField.setAccessible(true);
              return localField.get(obj);
          }
      
          /**
           * 通过反射获取DexPathList中dexElements
           */
          private static Object getDexElements(Object paramObject)
                  throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException {
              return getField(paramObject, paramObject.getClass(), "dexElements");
          }
      
      
          /**
           * 合并两个数组
           *
           * @param arrayLhs
           * @param arrayRhs
           * @return
           */
          private static Object combineArray(Object arrayLhs, Object arrayRhs) {
              Class<?> localClass = arrayLhs.getClass().getComponentType();
              int i = Array.getLength(arrayLhs);
              int j = i + Array.getLength(arrayRhs);
              Object result = Array.newInstance(localClass, j);
              for (int k = 0; k < j; ++k) {
                  if (k < i) {
                      Array.set(result, k, Array.get(arrayLhs, k));
                  } else {
                      Array.set(result, k, Array.get(arrayRhs, k - i));
                  }
              }
              return result;
          }
      }   
      

原理

这里先说下 热更新吧

通常的技术有

  • 阿里巴巴的黑科技

    底层c入手。杀到进程加载的类二进制数据,把内存里面的二进制数据修改。Dexposed、andfix这些。

  • 腾讯的开源tinker

    基于android和java的类加载机制入手
    ClassLoader—-> .class文件

这里就得知道android是如果加载classes.dex文件的

dalvik虚拟机:

PathClassLoader

用来加载应用程序的dex

DexClassLoader可以用来加载指定的某些dex文件

android studio中使用的 instant run 使用的就是。

接下来就明白了。当用户手机上的版本出现问题时。程序员在修改好之后。拿到没问题的版本的 class打包成class.dex文件,发送到用户客户端。把classes2.dex加载dexElements(DexPathList中)。就达到目的了
  • 上面的也就是使用步骤了.

    1. 错误客户端在客户手机上面。

    2. 修改成功后,把class2.dex放到服务器,客户端下载该文件。

    3. 客户端下载完成后,执行方法实现热更新。

对应到我的demo中就是把
1. test方法中的j=0, 出现除0异常
2. 下载这一步直接手动放到sd卡的根目录了。这里其实应该放到包目录下。也无所谓了。然后进入app,点击inject。实现替换。
3. 再点击test。正常谈toast。

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值