Android中的类加载器

来源:互联网 发布:并发编程 多线程 区别 编辑:程序博客网 时间:2024/06/17 21:56

概述

在讲解类加载器之前,我们先看一张从Android项目打包成apk的一个过程
这里写图片描述

分类

Android 中有三个 ClassLoader, 分别为URLClassLoader、PathClassLoader、DexClassLoader。其中

  • URLClassLoader 只能用于加载jar文件,但是由于 dalvik 不能直接识别jar,所以在 Android 中无法使用这个加载器。
  • 它只能加载已经安装的apk。因为 PathClassLoader 只会去读取 /data/dalvik-cache 目录下的 dex 文件。例如我们安装一个包名为com.hujiang.xxx的 apk,那么当 apk 安装过程中,就会在/data/dalvik-cache目录下生产一个名为data@app@com.hujiang.xxx-1.apk@classes.dex的 ODEX 文件。在使用 PathClassLoader 加载 apk 时,它就会去这个文件夹中找相应的 ODEX 文件,如果 apk 没有安装,自然会报ClassNotFoundException
  • DexClassLoader 是最理想的加载器。它的构造函数包含四个参数,分别为:
    1.dexPath,指目标类所在的APK或jar文件的路径.类装载器将从该路径中寻找指定的目标类,该类必须是APK或jar的全路径.如果要包含多个路径,路径之间必须使用特定的分割符分隔,特定的分割符可以使用System.getProperty(“path.separtor”)获得.
    2.dexOutputDir,由于dex文件被包含在APK或者Jar文件中,因此在装载目标类之前需要先从APK或Jar文件中解压出dex文件,该参数就是制定解压出的dex 文件存放的路径.在Android系统中,一个应用程序一般对应一个Linux用户id,应用程序仅对属于自己的数据目录路径有写的权限,因此,该参数可以使用该程序的数据路径.
    3.libPath,指目标类中所使用的C/C++库存放的路径
    4.classload,是指该装载器的父装载器,一般为当前执行类的装载器

为了便于大家理解,我截了一张图,展示了dexPath和libPath在Android中的路径

这里写图片描述

补充知识

由于Java的Runtime环境在初始化时内部会创建一个ClassLoader对象用于加载Runtime所需的各种Java类,所以一般不需要创建一个新的ClassLoader对象,而是使用当前环境已经存在的ClassLoader对象。

每个ClassLoader必须有一个父ClassLoader,在装载class文件时,子ClassLoader会先请求其父ClassLoader加载class文件,只有当父ClassLoader找不到class文件时,子ClassLoader才会继续装载该类

案例

下面我们通过一个例子来说明一下DexClassLoader的用法

假设有两个APK,一个叫app,一个叫Plugin,其中Plugin定义了一个PlugClass,该类中定义了一个函数function(),然后在app中我们去动态加载Plugin,调用它的function函数

这里写图片描述

public class PluginClass {    private static final String TAG = "PluginClass";    public PluginClass(){        Log.d(TAG, "PluginClass: initialized");    }    public int function(int a,int b){        return a+b;    }}

同时我们给Plugin中的MainActivity定义一个Action

   <activity android:name=".MainActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN"/>                <action android:name="com.intent.action.classloader"/>                <category android:name="android.intent.category.LAUNCHER"/>            </intent-filter>        </activity>

然后我们编写app中的MainActivity如下

public class MainActivity extends AppCompatActivity {    private TextView mTextView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mTextView = (TextView) findViewById(R.id.textview);        testDexClassLoader();    }   public void testDexClassLoader(){        //定义intent,指定的action要与plugin中的一致        Intent intent = new Intent("com.intent.action.classloader");        PackageManager packageManager = getPackageManager();        List<ResolveInfo> infos = packageManager.queryIntentActivities(intent, 0);        ResolveInfo info = infos.get(0);        ActivityInfo activityInfo = info.activityInfo;        String packageName = activityInfo.packageName;        Log.d(TAG, "testDexClassLoader: "+packageName);//com.example.plugin        String sourceDir = activityInfo.applicationInfo.sourceDir;        Log.d(TAG, "testDexClassLoader: "+sourceDir);// /data/app/com.example.plugin-1.apk        String dataDir = getApplicationInfo().dataDir;        Log.d(TAG, "testDexClassLoader: "+dataDir);// /data/data/com.example.host        String nativeLibraryDir = activityInfo.applicationInfo.nativeLibraryDir;        Log.d(TAG, "testDexClassLoader: "+nativeLibraryDir);// /data/app-lib/com.example.plugin-1        DexClassLoader classLoader = new DexClassLoader(sourceDir,dataDir,nativeLibraryDir,getClass().getClassLoader());        try {            Class<?> aClass = classLoader.loadClass(packageName + ".PluginClass");            Object object = aClass.newInstance();            Method method = aClass.getMethod("function", new Class[]{Integer.TYPE, Integer.TYPE});            Integer result = (Integer) method.invoke(object, 3, 4);            mTextView.setText(result+"");        } catch (Exception e) {            e.printStackTrace();        }}

我们看看运行的结果
这里写图片描述

接下来我们再看看PathClassLoader的用法

同样,我们在plugin中定义一个Animal类

public class Animal {    public String sayHello(){        return "Hello";    }}

然后我们在app中编写如下代码

public class MainActivity extends AppCompatActivity {    private static final String TAG = "MainActivity";    private TextView mTextView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mTextView = (TextView) findViewById(R.id.textview);        testPathClassLoader();    }    public void testPathClassLoader(){        String packageName = "com.example.plugin";        String classPath = "com.example.plugin.Animal";        try {            ApplicationInfo applicationInfo = getPackageManager().getApplicationInfo(packageName, 0);            String sourceDir = applicationInfo.sourceDir;            Log.d(TAG, "testPathClassLoader: "+sourceDir);// /data/app/com.example.plugin-2.apk            PathClassLoader pathClassLoader = new PathClassLoader(sourceDir,ClassLoader.getSystemClassLoader());            Class<?> aClass = Class.forName(classPath, true, pathClassLoader);            Object object = aClass.newInstance();            Method method = aClass.getMethod("sayHello", null);            String result= (String) method.invoke(object, null);            mTextView.setText(result);        } catch (Exception e) {            e.printStackTrace();        }    }}

这里写图片描述

总结

学习类加载器有什么用?

  • 对Android工程的编译和打包进行自动化,比如建立每日构建系统,自动生成发布文件等
  • 通过下载插件加载的方式实现功能扩展
  • 有些功能性模块,或是第三方开发,或是分阶段开发的,为了方便程序的扩展,我们同样考虑将功能代码封装成插件的形式
0 0
原创粉丝点击