Android中的类加载器
2016-05-22 21:14
597 查看
概述
在讲解类加载器之前,我们先看一张从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工程的编译和打包进行自动化,比如建立每日构建系统,自动生成发布文件等
通过下载插件加载的方式实现功能扩展
有些功能性模块,或是第三方开发,或是分阶段开发的,为了方便程序的扩展,我们同样考虑将功能代码封装成插件的形式
相关文章推荐
- Android 定时任务的多种实现方式
- Android的4中启动模式
- 一个刚到此吧的IT新人,第一次发博客,望大家多多指教
- <Android 基础(一)> Service
- <Android 基础(一)> Service
- java/android 设计模式学习笔记(4)---抽象工厂模式
- Android Dynamic Action(动态Action)—像访问网页一样地访问Activity
- 如何“优雅”地给你的类命名
- onMeasure简单方法 完美解决ListView与ScollView冲突问题!
- Android 学习资源[转]
- [深入理解Android卷二 全文-第五章]深入理解PowerManagerService
- Android----EdgeEffect类边缘效果的实现
- PorterDuffXfermode实现Android刮刮卡效果
- Android日期时间类,解决其他时间类时间会出现误差的bug
- 【Android】安全退出应用程序
- Android中的线程机制(Handler Looper)(二)
- Android Activity生命周期详讲
- Android中 Matrix类
- Notification的几种用法(API不同)
- 关于Activity与Fragment混用中对于startActivityForResult方法的解析