NDK40_Tinker热修复

NDK开发汇总

一 原理

要点:替换整个类

  1. 网络下载dex
  2. 将下载在sd卡的dex拷贝到私有路径下,获取对应的File[]
  3. 反射获取dalvik.system.BaseDexClassLoader的成员pathList,再次反射获取pathList的成员dexElements
  4. 获取系统的dexElements
  5. 将自己的dexElements和系统的dexElements拼成一个新的数组,保证自己的再签名,这样类在加载的时候找到自己修复的对象就会返回

二 实现

FixManager修复

package com.cn.ray.tinker_ray;


import android.content.Context;

import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.HashSet;

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

public class FixManager {

    private static final FixManager ourInstance = new FixManager();

    public static FixManager getInstance() {
        return ourInstance;
    }

    private HashSet<File> loaded = new HashSet<>();

    private FixManager() {
    }

    public void loadDex(Context context) {
        if (context == null) {
            return;
        }
        File filesDir = context.getDir("odex", Context.MODE_PRIVATE);
        File[] listFiles = filesDir.listFiles();
        for (File file : listFiles) {
            //            如果是classes开到   。dex
            if (file.getName().endsWith(".dex")) {
                loaded.add(file);
            }
        }

        //修复
        String optimizeDir = filesDir.getAbsolutePath() + File.separator + "opt_dex";
        File fopt = new File(optimizeDir);
        if(!fopt.exists()){
            fopt.mkdirs();
        }

        for(File dex:loaded){
            //              private final Element[] dexElements;

//            //        对象成员变量      对象 还原     ----数组   反射调用
//    ------------------------------我们的--------------------------------------------------------
            DexClassLoader dexClassloader = new DexClassLoader(dex.getAbsolutePath(),optimizeDir,null,context.getClassLoader());
            //         BaseClassLoader  ----dexPathList   ---->dexElements;
            try {
                Class myDexClazzLoader = Class.forName("dalvik.system.BaseDexClassLoader");
                Field myPathListFiled = myDexClazzLoader.getDeclaredField("pathList");
                myPathListFiled.setAccessible(true);
                //dexPathList
                Object myPathListObject = myPathListFiled.get(dexClassloader);

                Class myPathClazz = myPathListObject.getClass();
                //dexElements
                Field myElementsField = myPathClazz.getDeclaredField("dexElements");

                myElementsField.setAccessible(true);
                Object myElements = myElementsField.get(myPathListObject);
                //                myElements  -----》  private final Element[] dexElements;


                //----------------------------------系统的--------------------------------------------------------
//              PathClassLoader   PathList Element
                PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader();
                Class baseDexClazzLoader = Class.forName("dalvik.system.BaseDexClassLoader");
                Field pathListFiled = baseDexClazzLoader.getDeclaredField("pathList");
                pathListFiled.setAccessible(true);
                Object pathListObject = pathListFiled.get(pathClassLoader);

                Class systemPathClazz = pathListObject.getClass();
                Field systemElementsField = systemPathClazz.getDeclaredField("dexElements");
                systemElementsField.setAccessible(true);
                Object systemElements = systemElementsField.get(pathListObject);

                // 其他地方  新建留个房间
                //                dalvik.system.Element[] dexElements;
                Class elemnt = systemElements.getClass();
                int systemLength = Array.getLength(systemElements);
                int myLength =  Array.getLength(myElements);

                //                生成一个新的 数组   类型为Element类型
                Object newElementArray = Array.newInstance(elemnt, systemLength + myLength);

                for(int i=0;i<myLength+systemLength;i++) {
                    if (i < myLength) {
//                      先插入修复包的dex yes  1
                        Array.set(newElementArray, i, Array.get(myElements,i));
                    }else {
//                      系统的            no  2 索引错了  systemElements  5
                        Array.set(newElementArray, i, Array.get(systemElements,i-myLength));
                    }

                }

                Field  elementsField=pathListObject.getClass().getDeclaredField("dexElements");;
                elementsField.setAccessible(true);
                elementsField.set(pathListObject, newElementArray);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }
}

手动修复和再次启动加载
MainActivity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    //外置卡  - dex      指定目录   copy
    public void fix(View view) {
        File filesDir = getDir("odex", Context.MODE_PRIVATE);
        String name = "fix.dex";
        String filePath = new File(filesDir, name).getAbsolutePath();
        File file = new File(filePath);
        if (file.exists()) {
            file.delete();
        }

        InputStream is = null;
        FileOutputStream os = null;
        try {
            Log.i(TAG, "fixBug: " + new File(Environment.getExternalStorageDirectory(), name).getAbsolutePath());
            is = new FileInputStream(new File(Environment.getExternalStorageDirectory(), name));
            os = new FileOutputStream(filePath);
            int len = 0;
            byte[] buffer = new byte[1024];
            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
            File f = new File(filePath);
            FixManager.getInstance().loadDex(this);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                os.close();
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    public void test(View view) {
        Test test = new Test();
        test.testFix(this);
    }
}

MyAplication

public class MyAplication extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        FixManager.getInstance().loadDex(base);
        super.attachBaseContext(base);
    }
}

三 注意

  1. SD卡的读写权限
  2. 因为应用第一次进入,修复的dex还没下载,而类已经被加载使用了,因此需要再次启动的时候,才能完全修复

四 Demo

Tinker_Ray

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值