精华内容
下载资源
问答
  • 很早之前就想深入的研究和学习一下修复,由于时间的原因一直拖着,现在才执笔弄起来。微信针对QQ空间超级补丁技术的不足提出了一个提供DEX差量包,整体替换DEX的方案。主要的原理是与QQ空间超级补丁技术基本相同,...

    很早之前就想深入的研究和学习一下热修复,由于时间的原因一直拖着,现在才执笔弄起来。

    微信针对QQ空间超级补丁技术的不足提出了一个提供DEX差量包,整体替换DEX的方案。主要的原理是与QQ空间超级补丁技术基本相同,区别在于不再将patch.dex增加到elements数组中,而是差量的方式给出patch.dex,然后将patch.dex与应用的classes.dex合并,然后整体替换掉旧的DEX文件,以达到修复的目的。

    076afb5cdd55

    我们来逆向微信的APK看一下具体的实现:

    先找到应用入口TinkerApplication,在onBaseContextAttached()调用了loadTinker(),

    076afb5cdd55

    进入TinkerLoader的tryLoad()方法中,

    076afb5cdd55

    从方法名可以预见,在tryLoadPatchFilesInternal()中尝试加载本地的补丁,再经过跳转进入核心修复功能类SystemClassLoaderAdder.class中。

    076afb5cdd55

    代码中可以看出,根据Android版本的不同,分别采取具体的修复操作,不过原理都是一样的。我们以V19为例,

    076afb5cdd55

    从代码中可以看到,通过反射操作得到PathClassLoader的DexPatchList,反射调用patchlist的makeDexElements()方法吧本地的dex文件直接替换到Element[]数组中去,达到修复的目的。

    对于如何进行patch.dex与classes.dex的合并操作,这里微信开启了一个新的进程,开启新进程的服务TinkerPatchService进行合并。

    076afb5cdd55

    整体的流程如下:

    076afb5cdd55

    从流程图来看,同样可以很明显的找到这种方式的特点:

    优势:

    合成整包,不用在构造函数插入代码,防止verify,verify和opt在编译期间就已经完成,不会在运行期间进行。

    性能提高。兼容性和稳定性比较高。

    开发者透明,不需要对包进行额外处理。

    不足:

    与超级补丁技术一样,不支持即时生效,必须通过重启应用的方式才能生效。

    需要给应用开启新的进程才能进行合并,并且很容易因为内存消耗等原因合并失败。

    合并时占用额外磁盘空间,对于多DEX的应用来说,如果修改了多个DEX文件,就需要下发多个patch.dex与对应的classes.dex进行合并操作时这种情况会更严重,因此合并过程的失败率也会更高。

    展开全文
  • Android热更新方案Robust——美团热更新(热修复)使用介绍

    Android热更新方案Robust

    http://tech.meituan.com/android_robust.html

    Android热更新方案Robust开源,新增自动化补丁工具

    http://tech.meituan.com/android_autopatch.html

    美团 Robust 的 github demo 地址

    https://github.com/Meituan-Dianping/Robust

    Robust 的原理

    Robust插件对产品的每个函数在编译打包阶段都插入了一段代码。当我们需要对已上线的app进行bug代码修复时,这时如果存在patch.jar,就会调用patch.jar中作为修复bug的代码而跳过原先的代码片段,由此达到修复的目的;而对产品的每个函数进行插入一段代码的工作是由插件 apply plugin : 'robust' 来完成的;对我们需要修复代码操作而实现修复功能的 patch.jar 是由插件 apply plugin : 'auto-patch-plugin' 生成的。

    大致应用流程

     
    /**
    * 1.集成了 Robust 后,生成 apk。保存期间的混淆
    *   文件 mapping.txt + Robust 生成记录文件 methodMap.robust ;
    * 2.使用注解 @Modify 和 @Add 标注需要修复和用*于修复的方法 ;
    * 3.开启补丁插件(apply plugin:'auto-patch-plugin'),
    *   执行生成 apk 命令,获得补丁包patch.jar ;
    * 4.通过推送或者接口的形式,通知 app 有补丁,需要修复;
    * 5.加载补丁文件不需要重新启动应用(即时生效)。
    */

    制作补丁前准备一

    配置方面有两个地方:1.是在project级别的配置;2.是在module级别的配置;3.robust.xml复制到工程目录下;把如下图所示
     

     

    project级别的配置

     

    module级别的配置

     

    robust.xml配置

    <img data-cke-saved-src="https://img-blog.csdn.net/20170518144934741?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvanVuaHVhaG91c2U=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" src="https://img-blog.csdn.net/20170518144934741?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvanVuaHVhaG91c2U=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
    
    
     
     

    制作补丁前准备二

    首先了解一下这个测试demo的逻辑;打开APP进入到MainActivity,在MainActivity中主要有两个按钮:点击进入HotFixActivity页面和点击执行修复功能;在HotFixActivity页面只有一个TextView显示文字,当可以进行代码修复功能时,在MainActivity点击修复按钮之后再点击进入HotFixActivity页面,你会发现TextView显示的内容变成了我们要修复的内容。
    看一下代码:
    package com.draem.application20170516;
    
    import android.content.Intent;
    import android.os.Bundle;
    import android.os.Environment;
    import android.support.v7.app.AppCompatActivity;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import com.meituan.robust.Patch;
    import com.meituan.robust.PatchExecutor;
    import com.meituan.robust.RobustCallBack;
    
    import java.io.File;
    
    import butterknife.BindView;
    import butterknife.ButterKnife;
    import butterknife.OnClick;
    
    public class MainActivity extends AppCompatActivity {
    
        @BindView(R.id.btn_go)
        Button btnGo;
        @BindView(R.id.btn_hotfix)
        Button btnHotfix;
        @BindView(R.id.tvShow2)
        TextView tvShow2;
        @BindView(R.id.tvShow3)
        TextView tvShow3;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    //        SystemClock.sleep(2*1000);
            setContentView(R.layout.activity_main);
            ButterKnife.bind(this);
            File file = getDir();//创建文件夹  /mnt/sdcard/HotFix
        }
    
    
        @OnClick({R.id.btn_go, R.id.btn_hotfix})
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.btn_go:
                    Intent it = new Intent(MainActivity.this, HotFixActivity.class);
                    startActivity(it);
                    break;
                case R.id.btn_hotfix://执行修复
                    new PatchExecutor(getApplicationContext(),
                            new PatchManipulateImp(),
                            new RobustCallBack() {
                                @Override
                                public void onPatchListFetched(boolean result, boolean isNet) {
                                    Log.e("error-hot", "打印 onPatchListFetched:" + "isNet=" + isNet );
                                }
    
                                @Override
                                public void onPatchFetched(boolean result, boolean isNet, Patch patch) {
                                    Log.e("error-hot", "打印 onPatchFetched:" + "result=" + result+"isNet="+isNet + "--->" + "patch=" + patch);
                                }
    
                                @Override
                                public void onPatchApplied(boolean result, Patch patch) {
                                    Log.e("error-hot", "打印 onPatchApplied:" + "result=" + result + "--->" + "patch=" + patch);
                                }
    
                                @Override
                                public void logNotify(String log, String where) {
                                    Log.e("error-hot", "打印 logNotify:" + "log=" + log + "--->" + "where=" + where);
                                }
    
                                @Override
                                public void exceptionNotify(Throwable throwable, String where) {
                                    Log.e("error-hot", "打印 exceptionNotify:" + "throwable=" + throwable.toString() + "--->" + "where=" + where);
                                }
                            }).start();
                    break;
            }
        }
    
        int count = 0;
    
        private File getDir() {
            StringBuilder path = new StringBuilder();
            if (isSDAvailable()) {
                path.append(Environment.getExternalStorageDirectory()
                        .getPath());
                path.append(File.separator);// '/'
                path.append("HotFix");// /mnt/sdcard/HotFix
              
                Log.e("error-hotfix", "如果SD卡可用就在SD卡创建");
                tvShow3.setText("SD卡可用就在sd创建");
            } else {
                //如果SD卡不可用就在内存创建
                File filesDir = getApplication().getCacheDir();    //  cache  getFileDir file
                path.append(filesDir.getAbsolutePath());
               
                tvShow3.setText("SD卡不可用就在内存创建");
                Log.e("error-hotfix", "SD卡不可用就在内存创建");
            }
            File file = new File(path.toString());
            if (!file.exists() || !file.isDirectory()) {
                file.mkdirs();// 创建文件夹
                count += 10;
            }
            Toast.makeText(this, "file=" + file, Toast.LENGTH_SHORT).show();
            Log.e("error-hotfix", count+" ==>file地址=" + file.toString() + "-->" + file.getAbsolutePath());
            tvShow2.setText(file.toString()+ "\n" + file.getAbsolutePath());
            return file;
    
        }
    
        private boolean isSDAvailable() {
            if (Environment.getExternalStorageState().equals(
                    Environment.MEDIA_MOUNTED)) {
                Toast.makeText(this, "sd 有效", Toast.LENGTH_SHORT).show();
                return true;
            } else {
                return false;
            }
        }
    }
    
     
    package com.draem.application20170516;
    
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.widget.TextView;
    
    import butterknife.BindView;
    import butterknife.ButterKnife;
    
    public class HotFixActivity extends AppCompatActivity {
    
        @BindView(R.id.tvShow)
        TextView tvShow;
    
        @Override
    //    @Modify
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_hot_fix);
            ButterKnife.bind(this);
    
            tvShow.setText(getText());//加载错误的代码
    //        tvShow.setText(getInfo());//加载正确的代码
        }
    
    
        private String getText(){
            return "Hot-Fix, this just an error";
        }
    
    
    //    @Add
    //    public String getInfo(){
    //        return "Hot-Fix, 已经对含有error的代码进行了修改!";
    //    }
    }
    
     
    package com.draem.application20170516;
    
    import android.content.Context;
    import android.os.Environment;
    import android.util.Log;
    
    import com.meituan.robust.Patch;
    import com.meituan.robust.PatchManipulate;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Created by yuanjunhua on 2017/5/17.
     */
    
    public class PatchManipulateImp extends PatchManipulate {
        @Override
        protected List<Patch> fetchPatchList(Context context) {
    
            Patch patch = new Patch();
            patch.setName("test patch");
    
            StringBuilder path = new StringBuilder();
            path.append(Environment.getExternalStorageDirectory()
                    .getPath());
            path.append(File.separator);// '/'
            path.append("HotFix");// /mnt/sdcard/HotFix
            path.append(File.separator);
            path.append("patch");// /mnt/sdcard/HotFix/patch
    
            patch.setLocalPath(path.toString());
            Log.e("error-hotfix", "PatchManipulateImp 地址="+path);
    
            patch.setPatchesInfoImplClassFullName("com.draem.application20170516.PatchesInfoImpl");
            List<Patch> patchList = new ArrayList<>();
            patchList.add(patch);
            return patchList;
        }
    
    
        @Override
        protected boolean verifyPatch(Context context, Patch patch) {
            //do your verification, put the real patch to patch
            //放到app的私有目录
            StringBuilder path = new StringBuilder();
            path.append(context.getCacheDir());
            path.append(File.separator);// '/'
            path.append("HotFix");// /mnt/sdcard/HotFix
            path.append(File.separator);
            path.append("patch");// /mnt/sdcard/HotFix/patch
            patch.setTempPath(path.toString());
            //in the sample we just copy the file
            try {
                Log.e("error-hotfix", "patch.getLocalPath="+patch.getLocalPath()+"--->patch.getTempPath="+patch.getTempPath());
                copy(patch.getLocalPath(), patch.getTempPath());
            }catch (Exception e){
                e.printStackTrace();
                throw new RuntimeException("copy source patch to local patch error, no patch execute in path "+patch.getTempPath());
            }
    
            return true;
        }
    
        @Override
        protected boolean ensurePatchExist(Patch patch) {
            return true;
        }
    
    
    
        public void copy(String srcPath,String dstPath) throws IOException {
            File src = new File(srcPath);
            if(!src.exists()){
                try {
                    Log.e("error-hitfix", "资源不存在哦  srcPath="+srcPath);
                    Log.e("error-hitfix", "资源不存在哦  srcPath="+src.toString());
                    Log.e("error-hitfix", "资源不存在哦  srcPath="+src.length());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                
                throw new RuntimeException("source patch does not exist ");
            }
            File dst=new File(dstPath);
            if(!dst.getParentFile().exists()){
                dst.getParentFile().mkdirs();
            }
            InputStream in = new FileInputStream(src);
            try {
                OutputStream out = new FileOutputStream(dst);
                try {
                    // Transfer bytes from in to out
                    byte[] buf = new byte[1024];
                    int len;
                    while ((len = in.read(buf)) > 0) {
                        out.write(buf, 0, len);
                    }
                } finally {
                    out.close();
                }
            } finally {
                in.close();
            }
        }
    }
    

    然后就保持上面的代码 + module级别gradle中保持设置
    //在生成 apk 的时候使用 apply plugin:’robust’
    apply plugin: 'robust'
    
    
    开启打补丁那个,关闭生成apk那个
    //apply plugin: 'auto-patch-plugin'
    这样准备二阶段就要告一段落了,然后我们执行终端命令生成apk:
     gradlew clean assembleRelease --stacktrace --no-daemon
     
    打包成功之后我们可以看到(并按照图示操作):
     
     
     
     
    操作完之后,将上面的源代码该解注释的解注释该加注解的加上注解:
     
     
     
     
    最后就是执行终端代码生成patch.jar:(打包apk会失败,没关系我们是为了生产jar)
    gradlew clean assembleRelease --stacktrace --no-daemon
     
    到此为止,制作补丁前的准备已经完成;

    开始修复工作

    先要执行终端,把patch.jar文件copy到手机SD目录:/sdcard/HotFix/patch.jar
    adb push C:\studio_workspace\Application20170516\app\build\outputs\robust\patch.jar /sdcard/HotFix/patch.jar
    成功之后,我们只需要点击一下修复按钮然后跳转到HotFixActivity页面就好了,这时候会发现textview内容变化了!
     
     
    还有就是在使用终端命令的时候可能会遇到“不是内部命令的错误”,解决如下:原Android Sdudio 2.2和AndroidSdudio 2.3不一样 ,Android Sdudio 2.3 的 adb.exe是放在android-sdk\platform-tools目录下面的,而2.2是放在tools目录下面的,所以需要把环境配置path的路径指到platform-tools下面。然后终端输入adb,能够显示相关的信息。当然如果你不想要使用终端命令来执行上面的操作完全可以使用“Build/Generate/Signed APK...”来代替上面的终端命令;
    注意:1,测试安装到手机上的apk必须是签过名了的版本;2,配置SD卡的读写权限;
     
     
    展开全文
  • Android热更新

    2020-09-09 14:14:38
    (一)Android热更新简介 热更新 产生的背景 热更新技术的用途 热更新的发版流程以及优势 (二)常见的热修复框架 Andfix---阿里巴巴:是基于C和C++层实现的,使用简单,速度非常快。 Nuwa(来源于女娃...

    (一)热更新

    (一)Android热更新简介

    1. 热更新

    1. 产生的背景

    1. 热更新技术的用途

    展开全文
  • Android而更新系列:Android热更新一:JAVA的类加载机制Android热更新二:理解Java反射Android热更新三:Android类加载机制Android热更新四:热修复机制Android热更新五:四大热修复方案分析Android热更新六:Qzone...

    很早之前就想深入的研究和学习一下热修复,由于时间的原因一直拖着,现在才执笔弄起来。


    Android而更新系列:
    Android热更新一:JAVA的类加载机制
    Android热更新二:理解Java反射
    Android热更新三:Android类加载机制
    Android热更新四:热修复机制
    Android热更新五:四大热修复方案分析
    Android热更新六:Qzone热更新原理
    Android热更新七:Tinker热更新原理
    Android热更新八:AndFix热更新原理
    Android热更新九:Robust热更新原理
    Android热更新十:自己写一个Android热修复


    经过之前分析了各大热修复的实现原理,参考原理,我们来写一个属于自己的Android热修复吧。

    一. 热修复简述。

    所谓热修复,就是已经上线APP发现了Bug,不需要花大精力发布新版本,即可通过在线下载补丁并且修复Bug。

    热修复的基本原理:

    Android框架中存在一个数组,它的作用是维护全部的dex文件(我们写的类的二进制表述方式,用来给安卓虚拟机加载),安卓虚拟机会根据需要从该数组按照自上而下的顺序加载对应的类文件,即使数组中存多个同一个类对应的dex文件,虚拟机一旦找到了对应的dex文件就会停止查找,并加载。根据这个规则,我们只需要把Bug修复涉及到的类文件插入到数组的最前面去,就可以达到修复的目的。

    说白了,热修复是利用Android Application的加载dex的规则,从中干预,从而达到修复的目的。

    二. 根据原理,我们先来写一个热修复的核心类,

    有了上面的原理分析,这个类也肯定不会太复杂,主要用到的是Java的反射以及ClassLoader(DexClassLoader以及PathClassLoader)。

     

    package com.yb.demo.olfix.fixdex;
    
    import java.io.File;
    import java.lang.reflect.Array;
    import java.lang.reflect.Field;
    
    import android.content.Context;
    
    import dalvik.system.DexClassLoader;
    import dalvik.system.PathClassLoader;
    
    /**
     * 作者:created by yufenfen on 2019/3/21:12:13
     * 邮箱: ybyj1314@126.com
     */
    public final class HotFix {
        /**
         * 修复指定的类
         *
         * @param context        上下文对象
         * @param fixDexFilePath   修复的dex文件路径
         */
        public static void fixDexFile(Context context, String fixDexFilePath) {
            if (fixDexFilePath != null && new File(fixDexFilePath).exists()) {
                try {
                    injectDexToClassLoader(context, fixDexFilePath);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * @param context
         * @param fixDexFilePath 修复文件的路径
         * @throws ClassNotFoundException
         * @throws NoSuchFieldException
         * @throws IllegalAccessException
         */
        private static void injectDexToClassLoader(Context context, String fixDexFilePath)
                throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
            //读取 baseElements
            PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader();
            Object basePathList = getPathList(pathClassLoader);
            Object baseElements = getDexElements(basePathList);
    
            //读取 fixElements
            String baseDexAbsolutePath = context.getDir("dex", 0).getAbsolutePath();
            DexClassLoader fixDexClassLoader = new DexClassLoader(
                    fixDexFilePath, baseDexAbsolutePath, fixDexFilePath, context.getClassLoader());
            Object fixPathList = getPathList(fixDexClassLoader);
            Object fixElements = getDexElements(fixPathList);
    
            //合并两份Elements
            Object newElements = combineArray(baseElements, fixElements);
    
            //一定要重新获取,不要用basePathList,会报错
            Object basePathList2 = getPathList(pathClassLoader);
    
            //新的dexElements对象重新设置回去
            setField(basePathList2, basePathList2.getClass(), "dexElements", newElements);
        }
    
        /**
         * 通过反射先获取到pathList对象
         *
         * @param obj
         * @return
         * @throws ClassNotFoundException
         * @throws NoSuchFieldException
         * @throws IllegalAccessException
         */
        private static Object getPathList(Object obj) throws ClassNotFoundException, NoSuchFieldException,
                IllegalAccessException {
            return getField(obj, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList");
        }
    
        /**
         * 从上面获取到的PathList对象中,进一步反射获得dexElements对象
         *
         * @param obj
         * @return
         * @throws NoSuchFieldException
         * @throws IllegalAccessException
         */
        private static Object getDexElements(Object obj) throws NoSuchFieldException, IllegalAccessException {
            return getField(obj, obj.getClass(), "dexElements");
        }
    
        private static Object getField(Object obj, Class cls, String str)
                throws NoSuchFieldException, IllegalAccessException {
            Field declaredField = cls.getDeclaredField(str);
            declaredField.setAccessible(true);//设置为可访问
            return declaredField.get(obj);
        }
    
        private static void setField(Object obj, Class cls, String str, Object obj2)
                throws NoSuchFieldException, IllegalAccessException {
            Field declaredField = cls.getDeclaredField(str);
            declaredField.setAccessible(true);//设置为可访问
            declaredField.set(obj, obj2);
        }
    
        /**
         * 合拼dexElements ,并确保 fixElements 在 baseElements 之前
         *
         * @param baseElements
         * @param fixElements
         * @return
         */
        private static Object combineArray(Object baseElements, Object fixElements) {
            Class componentType = fixElements.getClass().getComponentType();
            int length = Array.getLength(fixElements);
            int length2 = Array.getLength(baseElements) + length;
            Object newInstance = Array.newInstance(componentType, length2);
            for (int i = 0; i < length2; i++) {
                if (i < length) {
                    Array.set(newInstance, i, Array.get(fixElements, i));
                } else {
                    Array.set(newInstance, i, Array.get(baseElements, i - length));
                }
            }
            return newInstance;
        }
    }
    

    三. 写bug修bug

    修复主体类写好了,那么,我们来写个baseAPP,,然后在baseAPP里写一个专门带有bug的类,既然要测试热修复,我们肯定要写一个带有bug的类。

     

    package com.yb.demo.olfix;
    
    import android.app.Activity;
    import android.content.Context;
    import android.util.Log;
    import android.view.View;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    import java.net.URL;
    
    /**
     * 作者:created by yufenfen on 2019/3/27:08:26
     * 邮箱: ybyj1314@126.com
     */
    public class FixMe {
        private final String TAG = "FixMe";
        private ImageView mBelle;
        private TextView mNotGril;
    
        private Context mContext;
        private MyGlide myGlide;
    
        //false: bug, true: fix
        private boolean fix = false;
    
        public FixMe(Activity context) {
            mContext = context;
            mBelle = (ImageView) context.findViewById(R.id.gril);
            mNotGril = (TextView) context.findViewById(R.id.notgril);
            myGlide = MyGlide.getInstance(mContext);
    
        }
    
        public void showWhat() {
            if (fix) {
                fixBug();
                Log.d(TAG, "fix bug!");
            } else {
                mBelle.setVisibility(View.GONE);
                mNotGril.setVisibility(View.VISIBLE);
                Log.d(TAG, "this is a bug!");
            }
        }
    
        private void fixBug() {
            try {
    
                mBelle.setVisibility(View.VISIBLE);
                mNotGril.setVisibility(View.GONE);
    
                URL url = new URL("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1553252483041&di=3c51ed29d8b2efe3c98dac5168f19e6b&imgtype=0&src=http%3A%2F%2Fpic.feizl.com%2Fupload%2Fallimg%2F171016%2F522zpd0y2srfqa.jpg");
                myGlide.loadImageAndAddToTarget(mBelle, url);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    

    然后打包安装到我们的设备上。

     

    接下来,我们把bug修正,也就是在出bug的对应类修复bug,其实就是上面的类的变量赋值为true

     

        //false: bug, true: fix
        private boolean fix = true;
    

    四. 打补丁包打补丁

    修复好bug后,先不要着急编译运行,我们要先在AndroidStudio里面关闭掉Instant_Run。
    由于Android Studio的instan run的原理也是热修复,所以安装的时候不会安装完整的安装包,只会安装新改变的代码。

     

    Jietu20190321-155822.jpg

    重新编译,然后就可以打热修复补丁包了,我们这里了非常原始的打补丁包的方式,步骤如下:

    1. 拷贝出新修改的类

    点击Build->RebuildProject来重新构建,构建完成之后,可以在app/build/interintermediate/debug/包名/找到你刚刚修改的class文件,将他拷贝出来,要连同包名路径一起拷贝出来。

     

    Jietu20190322-095337.jpg

    2. 将class文件打包成dex文件

    我们前面知道热修复的原理是Dalvik/ART加载dex文件,所以接下来我们要将class文件打包成dex文件,首先我们找到AndroidSDK的build-tools 目录下,在控制台下进入该目录下的任意一个版本,执行dx命令,关于dx命令的使用帮助可以使用dx -- help,下面们通过 dx --dex [指定输出路径]/classes.dex [刚才拷贝的修复bug的类及包名的目录]这样我们就得到了.dex文件。

     

    dx --dex --output=/Users/yufenfen/Desktop/outputdex/classes2.dex /Users/yufenfen/Desktop/outputdex
    

    3. 将补丁包放到目的地

    由于实现在线下载补丁文件(classes2.dex)还是比较麻烦一点,我们这里采取简单粗暴的方式,就是手动的把补丁放到以下目录:

     

    Environment.getExternalStorageDirectory()
    

    四. 调用热更新

    放好补丁文件后,就可以在页面点击按钮“修复”后进行检查热修复,方式如下

     

          private void checkFix(){
            try {
                String dexPath = Environment.getExternalStorageDirectory() + "/classes2.dex";
                HotFix.fixDexFile(this, dexPath);
    
                Toast.makeText(this, "修复成功", Toast.LENGTH_SHORT).show();
            } catch (Exception e) {
                Toast.makeText(this, "修复失败" + e.getMessage(), Toast.LENGTH_SHORT).show();
                e.printStackTrace();
            }
        }
    

    OK,造补丁包打补丁就是这么简单粗暴的搞定了,接下来就可以验证是否成功了。
    先验证APP是在bug状态的,然后重新启动有bug的APP,点击修复,我们就可以看到美女了。

     

    存在的问题,是如果先点击“来个妞”,再点修复,貌似没有效果,一定要先点修复,再点击“来个妞”才好,原因待确定。
    初步估计是相关的类已经被加载,因为根据类加载机制,尽管修复bug了,也就是对相关的类进行了提位操作,而该类已经被加载内存,不会再重新加载,故无效。
    有时间找找准确的原因,找到后会更新。



    作者:雨纷纷__
    链接:https://www.jianshu.com/p/b65e5da3dff2
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    展开全文
  • Android热更新简介

    千次阅读 2017-12-19 15:56:42
    如果你交给某人一个程序,你将折磨他一整天;... 热更新是一种各大手游等众多App常用的更新方式。简单来说,就是在用户通过App Store下载App之后,打开App时遇到的即时更新 热更新的原理 说到热更新的原理,不得不
  • 前两篇:与Android 热更新方案Amigo的初次接触原作者已经很久没有更新的,我之前适配了Android8.0 和gradle3.0,最近适配的Android P。我使用的是最新的环境Android Studio 3.4.1。gradle 插件版本5.1.1 gradle版本 ...
  • Android热更新原理记录

    2016-11-30 14:01:50
    Android热更新原理学习笔记
  • android 热更新

    2018-08-21 16:54:52
    热更新的产生 热更新流程 主流热更新框架 android类加载机制 热更新原理 热更新的产生 线上版本出现严重的bug,需要重新发版。热更新能快速的解决线上问题,不需要重新发版。 热更新流程 ...
  • Android 热更新框架

    万次阅读 2017-04-11 19:39:03
    大部分人都称为热更新,其实同修复是一个意思。 本文转载:http://dev.qq.com/topic/57a31921ac3a1fb613dd40f3 Android 不仅系统版本众多,机型众多,而且各个市场都各有各的政策和审核速度,每次发布一个版本...
  • Android 腾讯 Bugly 热更新这个是一位大佬教我的, 我自己照着做写博客热更新虽然看起来很复杂, 但是 Bugly 通过集成, 使得这个过程很简单我这里不涉及多渠道热更新, 只讲述最简单的情况1. 首先我们需要在 Bugly 上有...
  • 安卓原生热更新,具体实现方法,运行APK后,修改里面的Toast文字,打包APK解压出classe.dex文件,把文件导入手机,点击修复后重启软件就能完成热更新
  • 最近热更新热修复的功能在安卓应用上越发火热,热更新的框架很多,比如AndFix,HotFix,Dexposed以及Nuwa,在考虑兼容性和功能需求之后我还是选择了Nuwa,因为这个框架可以新增类和字段,并且兼容到android6.0系统。...
  • Android热更新实现原理

    万次阅读 多人点赞 2015-11-15 01:31:22
    最近Android社区的氛围很不错嘛,连续放出一系列的Android动态加载插件和热更新库,这篇文章就来介绍一下Android中实现热更新的原理。ClassLoader我们知道Java在运行时加载对应的类是通过ClassLoader来实现的,...
  • 介绍  在介绍Bugly之前,需要先向大家简单介绍下一些热更新的相关内容。...Tinker目前已运行在微信的数亿Android设备上,相对于其它热更新方案,Tinker相对比较优秀。什么是Tinker  Tinker是微信官方的Andr
  • Android而更新系列:Android热更新一:JAVA的类加载机制Android热更新二:理解Java反射Android热更新三:Android类加载机制Android热更新四:热修复机制Android热更新五:四大热修复方案分析Android热更新六:Qzone...
  • 1、TinkerTinker 的方案,都是让 Classloader 去加载新的类。如果不重启,原来的类还在虚拟机中,就...从而达到修复的目的。Tinker 开发团队人员自研了 DexDiff 算法。 BsDiff 加载 so 和部分资源文件,DexDiff 加...
  • Android热更新研究与实现

    万次阅读 2019-07-11 17:35:59
    Android热更新技术的研究与实现之研究篇 ———概念讲解——– 热更新 相关概念 这个词出现的时间已经很久了,感觉现在要找工作才来看是晚了不少,但是好东西什么时候学习都不晚的。 今天看到一句话,和大家分享...
  • 这两天看了一下Android修复框架--bugly,亲自按照官网步骤实现了一下,感觉挺好的,除了部分机型会有点问题。 官网地址:https://bugly.qq.com/v2/index 下面是小米note3 Android8.1更新tinker版本后自动合成...
  • Android热更新技术总结

    2019-01-08 22:58:21
    1为什么需要热更新? 正常开发流程: 新版本上线,发现问题或用户反馈bug,紧急修复,上线版本,用户重新安装。 图片1.png 存着如下问题: l 周期长 l 用户下载成本高,app推广成本高昂 l 修复不及时,用户...
  • Android 热更新集成

    2021-04-19 16:32:37
    安卓热更新技术方案 正常开发流程: 新版本上线,发现问题或用户反馈bug,紧急修复,上线版本,用户重新安装,如下图: 修复流程: 新版本上线,发现问题或用户反馈,紧急修复,上线补丁,自动修复,如下图: ...
  • android热更新bugly

    2018-04-24 22:43:50
    百度百科:热更新就是动态下发代码,它可以使开发者在不发布新版本的情况下,修复 bug 和发布功能,避免长时间的审核等待以及多次被拒造成的成本。 修复提出于 2014 年,兴起于 2016 年,尤其是在 Instant run ...
  • android热更新机制

    2016-10-23 09:50:28
    ...Android 不仅系统版本众多,机型众多,而且各个市场都各有各的政策和审核速度,每次发布一个版本对于...iOS 两三天就能达到 80% 的覆盖速度而言,Android 应用版本升级至少需要两周才能达到 80% 的升级率,严
  • Android 热更新 的使用

    千次阅读 2017-03-18 16:37:21
    如今市场上有很多热更新的第三方,我使用的是阿里的阿里百川hotfix2.0,对于各种热更新的优劣我就不介绍了,网上很多,例如:http://www.jianshu.com/p/2d0f70e30111直接进入正题: 阿里百川hotfix入口: ...
  • 之前写过一篇文章,记得应该是七月份的时候,那时仅仅介绍了Andfix的如何使用,当时在文章的末尾还留了自己的QQ号,以至于后来添加的人还挺多,可见对于Android热更新而言,还是有很多开发者进行追捧。 为什么热...
  • 安卓热更新技术探讨

    2019-10-03 13:38:03
    针对热更新技术,目前已经相对比较成熟,最近对热更新技术比较感兴趣,因此经过查找各种资料,最后总结了一下经常使用的技术。 热更新技术大体可分为阿里系和腾讯系。 其中阿里系在前期是免费的,但是后续的话...
  • 配置完热更新后,直接run模式运行,程序会报以下错误:Tinker does not support instant run mode, please trigger build by assembleDebug or disable instant run in 'File->Settings...'.解决办法就是在File-...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 23,631
精华内容 9,452
关键字:

安卓热更新修改