上一篇看了java的ClassLoader,我们知道,Android虽然是用java开发,但是Android虚拟机可不认识什么.class文件,Android会将所有的.class文件,打包成一个.dex文件后,进行加载。而这被封装在BaseDexClassLoader类里,但是Android通常用到的,都是它的两个子类PathClassLoader和DexClassLoader。

上源码:

package dalvik.system;import java.io.File;public class DexClassLoader extends BaseDexClassLoader {    public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {        super((String)null, (File)null, (String)null, (ClassLoader)null);        throw new RuntimeException("Stub!");    }}public class PathClassLoader extends BaseDexClassLoader {    public PathClassLoader(String dexPath, ClassLoader parent) {        super((String)null, (File)null, (String)null, (ClassLoader)null);        throw new RuntimeException("Stub!");    }    public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {        super((String)null, (File)null, (String)null, (ClassLoader)null);        throw new RuntimeException("Stub!");    }}

我们看到DexClassLoader和PathClassLoader都继承了BaseDexClassLoader。再进一步看看

package dalvik.system;import java.io.File;import java.net.URL;import java.util.Enumeration;public class BaseDexClassLoader extends ClassLoader {    public BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent) {        throw new RuntimeException("Stub!");    }    protected Class<?> findClass(String name) throws ClassNotFoundException {        throw new RuntimeException("Stub!");    }    protected URL findResource(String name) {        throw new RuntimeException("Stub!");    }    protected Enumeration findResources(String name) {        throw new RuntimeException("Stub!");    }    public String findLibrary(String name) {        throw new RuntimeException("Stub!");    }    protected synchronized Package getPackage(String name) {        throw new RuntimeException("Stub!");    }    public String toString() {        throw new RuntimeException("Stub!");    }}

我们看到BaseDexClassLoader父类是ClassLoader。而这个ClassLoader就是上篇Java里的那个。

接下来我们讲讲,Android里常用的PathClassLoader和DexClassLoader。

PathClassLoader这哥们是干啥的呢,它用来加载系统的apk、还有安装到手机的Apk里的.dex。也就是说,当一个apk,被安装到手机后,它的.dex中的class文件都是被PathClassLoader来加载的,不信,上代码:

public class MainActivity extends AppCompatActivity {    private static final String TAG = "MainActivity";    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.d(TAG, "" + MainActivity.class.getClassLoader());    }}

运行一下,看结果:

2020-04-15 14:26:19.325 7782-7782/? D/MainActivity: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/cn.whm.hot-fNwGGljMJdWzbyP2KOGCfg==/base.apk"],nativeLibraryDirectories=[/data/app/cn.whm.hot-fNwGGljMJdWzbyP2KOGCfg==/lib/arm64, /system/lib64, /product/lib64]]]

DexClassLoader:

相比于PathClassLoader的局限性,DexClassLoader比较强大,它可以从SD卡上加载包含class.dex的.jar和.apk文件,这也是插件化和热修复的基础。在不需要更新apk的情况下,完成需要使用.dex的加载。

使用DexClassLoader来实现热修复

第1步:新建一个Android项目,写一个接口IAction:

package cn.whm.hot.impl;/** * Created by juwuguo on 2020-04-14. */public interface IAction {    String doAction();}

第2步:建一个ActionException类

package cn.whm.hot.impl;/** * Created by juwuguo on 2020-04-14. */public class ActionException implements IAction {    @Override    public String doAction() {        return "something wrong here!!!";    }}

第3步:MainActivity 类

package cn.whm.hot.activity;import androidx.appcompat.app.AppCompatActivity;import cn.whm.hot.R;import cn.whm.hot.impl.IAction;import cn.whm.hot.impl.ActionException;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.Toast;public class MainActivity extends AppCompatActivity {    private static final String TAG = "MainActivity";    private Button btn_say;    private IAction iAction;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.d(TAG, "" + MainActivity.class.getClassLoader());        setContentView(R.layout.activity_main);        btn_say = findViewById(R.id.btn_say);        setListener();    }    private void setListener() {        btn_say.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                    iAction = new ActionException();                    Log.e(TAG, "say_hotfix.jar not exist");                    Toast.makeText(MainActivity.this, iAction.doAction(), Toast.LENGTH_LONG).show();            }        });    }}

运行后,点击按钮

探究Android之ClassLoader_第1张图片

第4步:新建一个java项目:

记得必须在想同的包名下,新建接口IAction;

package cn.whm.hot.impl;public interface IAction { String doAction();}

第5步:新建一个ActionNormal类,实现接口,模拟新操作

package cn.whm.hot.impl;public class ActionNormal implements IAction {public String doAction() {System.out.println("============"+ActionNormal.class.getClassLoader());return "Everything is right!!!";}}

第六步:切换到java项目src下,执行javac -d . cn/whm/hot/impl/IAction.java cn/whm/hot/impl/ActionNormal.java

在这一步,吃大亏了,墨迹半天,必须在这个src目录下执行javac命令,否则编译出来的class文件,在下一步使用dex命令的时候,就会报错。切记!!!

执行完后,目录下出现IAction.class和ActionNormal.class文件

第7步:在src下,执行jar cvf /Users/hello/Downloads/input.jar cn/whm/hot/impl/IAction.class cn/whm/hot/impl/ActionNormal.class 命令,将两个class文件打包成input.jar包,并保存在Downloads目录下。

第8步:执行dx --dex --output=/Users/hello/Downloads/output.jar /Users/hello/Downloads/input.jar命令,将input.jar包里的.class优化成dex文件。最终这个output.jar就是我们需要的jar包。

第9步:把这个jar包,复制到手机的SD卡目录里。

第10步:修改MainActivity代码:

package cn.whm.hot.activity;import androidx.appcompat.app.AppCompatActivity;import cn.whm.hot.R;import cn.whm.hot.impl.IAction;import cn.whm.hot.impl.ActionException;import dalvik.system.DexClassLoader;import android.os.Bundle;import android.os.Environment;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.Toast;import java.io.File;public class MainActivity extends AppCompatActivity {    private static final String TAG = "MainActivity";    private Button btn_say;    private IAction iAction;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.d(TAG, "" + MainActivity.class.getClassLoader());        setContentView(R.layout.activity_main);        btn_say = findViewById(R.id.btn_say);        setListener();    }    /**     * dx --dex --output=/Users/hello/Desktop/backProject/HotFixProject/output.jar /Users/hello/Desktop/backProject/HotFixProject/src.jar     */    private void setListener() {        btn_say.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                File jarFile = new File(Environment.getExternalStorageDirectory().getPath() + "/XZLFile/media/output.jar");                if (!jarFile.exists()) {                    iAction = new ActionException();                    Log.e(TAG, "output.jar not exist");                    Toast.makeText(MainActivity.this, iAction.doAction(), Toast.LENGTH_LONG).show();                } else {                    //需要有读写权限                    DexClassLoader classLoader = new DexClassLoader(jarFile.getAbsolutePath(), getExternalCacheDir().getAbsolutePath(), null, getClassLoader());                    try {                        Class clazz = classLoader.loadClass("cn.whm.hot.impl.ActionNormal");                        iAction = (IAction) clazz.newInstance();                        Toast.makeText(MainActivity.this, iAction.doAction(), Toast.LENGTH_LONG).show();                    } catch (Exception e) {                        e.printStackTrace();                    }                }            }        });    }}

判断jar包是否存在,然后使用DexClassLoader加载执行。再次运行,如图:

探究Android之ClassLoader_第2张图片

细心的看客有没有注意到第5步里我打的日志

2020-04-15 15:23:04.162 14279-14279/cn.whm.hot I/System.out: ============dalvik.system.DexClassLoader[DexPathList[[zip file "/storage/emulated/0/XZLFile/media/output

证实它就是通过DexClassLoader加载进来的。

搞定!!!

更多相关文章

  1. Android shell 系统命令大全
  2. 通过xml布局文件实现按钮改变焦点设置背景图片
  3. Android Shell 常用命令
  4. android 模拟器命令 转载
  5. 在deepin系统中adb操作android文件的方法

随机推荐

  1. 浅谈android的selector,背景选择器
  2. Android(安卓)开发中使用 SQLite 数据库
  3. Android(安卓)Camera的接口与架构介绍
  4. Android(安卓)- 文件读写操作 总结
  5. Android(安卓)Studio安装以及Fetching an
  6. android调用第三方软件打开下载的附件
  7. Android进阶(十四)Android(安卓)Adapter
  8. Android(安卓)- 图解向 Android(安卓)Stu
  9. Android流式布局实现
  10. 在Android平台上实现条型码扫描与识别