• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >Android > Android热补丁动态修复技术(二):实战!CLASS_ISPREVERIFIED问题!

Android热补丁动态修复技术(二):实战!CLASS_ISPREVERIFIED问题!

作者:网友 字体:[增加 减小] 来源:互联网 时间:2017-05-26

网友通过本文主要向大家介绍了class ispreverified,ispreverified,android class,android class t,android studio class等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

Android热补丁动态修复技术(二):实战!CLASS_ISPREVERIFIED问题!


一、前言

上一篇博客中,我们通过介绍dex分包原理引出了Android的热补丁技术,而现在我们将解决两个问题。
1. 怎么将修复后的Bug类打包成dex
2. 怎么将外部的dex插入到ClassLoader中

二、建立测试Demo

2.1 目录结构

这里写图片描述

2.2 源码

activity_main.xml




    

MainActivity.class

package com.aitsuki.bugfix;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
import com.aitsuki.bugfix.animal.Cat;

public class MainActivity extends AppCompatActivity {

    private Cat mCat;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mCat = new Cat();
    }

    public void click(View view) {
        Toast.makeText(this, mCat.say(),Toast.LENGTH_SHORT).show();
    }
}

Cat.class

package com.aitsuki.bugfix.animal;

/**
 * Created by AItsuki on 2016/3/14.
 */
public class Cat {
    public String say() {
        return "汪汪汪!";
    }
}

2.3 运行结果

这里写图片描述

假设这是我们公司的开发项目,刚刚上线就发现了严重bug,猫会狗叫。
想修复bug,让用户再立刻更新一次显然很不友好,此时热补丁修复技术就有用了。

三、制作补丁

在加载dex的代码之前,我们先来制作补丁。
1. 首先我们将Cat类修复,汪汪汪改成喵喵喵,然后重新编译项目。(Rebuild一下就行了)
2. 去保存项目的地方,将Cat.class文件拷贝出来,在这里
这里写图片描述
3. 新建文件夹,要和该Cat.class文件的包名一致,然后将Cat.class复制到这里,如图
这里写图片描述vc281tC1xHRlc3TEv8K8o6zUy9DQ0rvPwsP8we6jrLTysPyyubahoaPI5828o7o8YnIgLz4NCjxpbWcgYWx0PQ=="这里写图片描述" src="http://www.bkjia.com/uploads/allimg/160408/0414554559-4.png" title="\" />
然后test目录是这样的
这里写图片描述
patch_dex.jar就是我们打包好的补丁了,我们将它放到sdCard中,待会从这里加载补丁。

关于什么用这么复杂的方法打包补丁的说明:
你也可以直接将java文件拷出来,通过javac -d带包编译再转成jar。
但我这么麻烦是有原因的,因为用这种方法你可能会遇到ParseException,原因是jar包版本和dx工具版本不一致。
而从项目中直接将编译好的class直接转成jar就没问题,因为java会向下兼容,打出来的jar包和class版本是一致的。
总而言之,dx版本要和class编译版本对应。

四、加载补丁

4.1 思路

通过上一篇博文,我们知道dex保存在这个位置
BaseDexClassLoader–>pathList–>dexElements

apk的classes.dex可以从应用本身的DexClassLoader中获取。 path_dex的dex需要new一个DexClassLoader加载后再获取。 分别通过反射取出dex文件,重新合并成一个数组,然后赋值给盈通本身的ClassLoader的dexElements

4.2 代码实现

加载外部dex,我们可以在Application中操作。
首先新建一个HotPatchApplication,然后在清单文件中配置,顺便加上读取sdcard的权限,因为补丁就保存在那里。

HotPatchApplication代码如下:

package com.aitsuki.hotpatchdemo;

import android.app.Application;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import dalvik.system.DexClassLoader;

/**
 * Created by hp on 2016/4/6.
 */
public class HotPatchApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        // 获取补丁,如果存在就执行注入操作
        String dexPath = Environment.getExternalStorageDirectory().getAbsolutePath().concat("/patch_dex.jar");
        File file = new File(dexPath);
        if (file.exists()) {
            inject(dexPath);
        } else {
            Log.e("BugFixApplication", dexPath + "不存在");
        }
    }

    /**
     * 要注入的dex的路径
     *
     * @param path
     */
    private void inject(String path) {
        try {
            // 获取classes的dexElements
            Class cl = Class.forName("dalvik.system.BaseDexClassLoader");
            Object pathList = getField(cl, "pathList", getClassLoader());
            Object baseElements = getField(pathList.getClass(), "dexElements", pathList);

            // 获取patch_dex的dexElements(需要先加载dex)
            String dexopt = getDir("dexopt", 0).getAbsolutePath();
            DexClassLoader dexClassLoader = new DexClassLoader(path, dexopt, dexopt, getClassLoader());
            Object obj = getField(cl, "pathList", dexClassLoader);
            Object dexElements = getField(obj.getClass(), "dexElements", obj);

            // 合并两个Elements
            Object combineElements = combineArray(dexElements, baseElements);

            // 将合并后的Element数组重新赋值给app的classLoader
            setField(pathList.getClass(), "dexElements", pathList, combineElements);

            //======== 以下是测试是否成功注入 =================
            Object object = getField(pathList.getClass(), "dexElements", pathList);
            int length = Array.getLength(object);
            Log.e("BugFixApplication", "length = " + length);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过反射获取对象的属性值
     */
    private Object getField(Class cl, String fieldName, Object object) throws NoSuchFieldException, IllegalAccessException {
        Field field = cl.getDeclaredField(fieldName);
        field.setAccessible(true);
        return field.get(object);
    }

    /**
     * 通过反射设置对象的属性值
     */
    private void setField(Class cl, String fieldName, Object object, Object value) throws NoSuchFieldException, IllegalAccessException {
        Field field = cl.getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(object, value);
    }

    /**
     * 通过反射合并两个数组
     */
    private Object combineArray(Object firstArr, Object secondArr) {
        int firstLength = Array.getLength(firstArr);
        int secondLength = Array.getLength(secondArr);
        int length = firstLength + secondLength;

        Class componentType = firstArr.getClass().getComponentType();
        Object newArr = Array.newInstance(componentType, length);
        for (int i = 



 
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • Android热补丁动态修复技术(二):实战!CLASS_ISPREVERIFIED问题!

相关文章

  • 2017-05-26Android事件分发机制总结
  • 2017-05-26Android 博客园客户端 (五) 查看评论、搜索博主,android查看评论
  • 2017-05-26Android安全攻防战,反编译与混淆技术完全解析(下)
  • 2017-05-26Kotlin操作符重载:把标准操作加入到任何类中(KAD 17),kotlinkad
  • 2017-05-26android:Activity数据传递之对象(Serializable)
  • 2017-05-26Android 涂鸦最佳实践
  • 2017-05-26Android 一个改善的okHttp封装库,androidokhttp
  • 2017-05-26记一次Android系统下解决音频UnderRun问题的过程
  • 2017-05-26玩转Android---事件监听篇---第2篇,android---第2篇
  • 2017-05-26我的android学习经历7,android学习经历7

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • 新闻客户端应用项目源码,客户端项目源码
    • iOS,Android网络抓包教程之tcpdump
    • 抽取的BaseFragment和LoadingPage,basefragment
    • Android studio Error occurred during initialization of VM 问题解决,initializationofvm
    • 集成websocket即时通讯 java聊天源码 代码下载 java后台框架源码 websocket源码 IM,websocket即时通讯
    • 在Kotlin编写RecyclerView适配器(KAD 16),kotlinrecyclerview
    • 策略模式,java策略模式
    • PendingIntent 显示通知,pendingintent通知
    • Glide中request的调用和管理
    • Android中Window添加View的底层原理

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有