Java 中的 ClassLoader:

Java 中 ClassLoader 用于动态加载 Class 到 JVM,包含 BootstrapClassLoader(C++ 编写,用于加载系统核心类)、 ExtClassLoader(用于加载 lib/ext/ 目录的扩展 API)、 AppClassLoader(加载 CLASSPATH 目录下的类)。

双亲委托机制:

  • 任何自定义 ClassLoader 都必须继承 ClassLoader 抽象类,并指定其 parent 加载器,默认为 BootstrapClassLoader;

  • 任何自定义 ClassLoader 在加载一个类之前都会先委托其 parent 去加载,只有 parent 加载失败才会自己加载;

    • 这样既可以防止重复加载,又可以排除安全隐患(防止用户替换系统核心类);

    • 所以一般只需要重写 findClass()方法即可(在 parent 加载失败时调用);

  • 双亲委托机制是在 loadClass() 方法实现的,要想避开(自己验证安全性,比如 Tomcat 的 WebAppClassLoader),必须重写 loadClass() 方法;

自定义 ClassLoader 用途:

  • 在执行非置信代码前先做签名认证等;

  • 从网络、数据库等动态加载类;

类的卸载:

  • 只有当类的实例被回收,才会被 unload,但被 BootstrapClassLoader 加载的系统类除外;

  • 重复加载类会报异常,只能重新定义新的 ClassLoader 再次加载;

Dalvik 的 ClassLoader:

  • Android 里 ClassLoaderdefineClass() 方法直接抛出 UnsupportedOperationException 异常,必须借助 DexClassLoaderPathClassLoader

  • DexClassLoaderPathClassLoader 都遵循双亲委托机制,因为只重写了 findClass() 方法,没有重写 loadClass() 方法;

  • Dalvik 虚拟机识别的是 DexFile 而不是 JarFile;且 DexFile.loadClass() 方法必须通过类加载器调用,否则无效;

利用 DexClassLoader 实现 Android 插件加载:

比如我们在主应用 HostApp 中需要调用 视频插件 VideoPlayerPlugin 中的 playVideo() 方法。

给插件加入 Intent 标识:

HostApp 要查询插件信息,只能通过 PackageManager。这里我首先想到的是直接通过其 getPackageInfo() 方法。但是试想,可能插件有很多个,而且包名不同。所以最好还是通过在插件中定义空的 Activity 并加入 Intent 标识,然后调用 queryIntentActivities() 方法去查询插件信息:

1     
2
3
4
5
<activity android:name=".plugin">     
<intent-filter>
<action android:name="com.rincliu.videoplayerplugin"/>
</intent-filter>
</activity>

查询插件信息:

首先使用 PackageMananer 查询到插件的 packageNameApplicationInfo:

1     
2
3
4
5
Intent intent = new Intent("com.rincliu.videoplayerplugin");     
List<ResolveInfo> plugins = getPackageManager().queryIntentActivities(intent, 0);
ActivityInfo act = plugins.get(0).activityInfo;
String packageName = act.packageName;
ApplicationInfo app = act.applicationInfo;

上面是直接读取的第一条信息( plugins 要先判空),如果有很多种插件,或者有好几个版本,这样就需要继续读取插件的版本号等配置信息作进一步区分:

1     
2
3
Resources res = pm.getResourcesForApplication(packageName);     
int resId = res.getIdentifier("version", "string", packageName);
String version = res.getString(resId);

使用 DexClassLoader 调用插件

创建 DexClassLoader 对象:

1     
2
3
4
5
String dexSourceDir = app.sourceDir;     
String dexOutputDir = getApplicationInfo().dataDir;
String dexLibDir = app.nativeLibraryDir;
ClassLoader parentLoader = this.getClass().getClassLoader();
DexClassLoader loader = new DexClassLoader(dexSourceDir, dexOutputDir, dexLibDir, parentLoader);

使用反射调用插件中的方法:

1     
2
3
4
5
6
7
8
9
10
11
12
try {     
Class<?> clazz = loader.loadClass(packageName + ".VideoPlayerPlugin");

//Object obj = clazz.newInstance();
Constructor<?> localConstructor = clazz.getConstructor();
Object obj = localConstructor.newInstance();

//Method method = ((Class<?>) obj).getMethod("play", String.class);
Method method = clazz.getDeclaredMethod("play", String.class);

method.invoke(obj, "/sdcard/demo.mp4");
} catch (Exception e) {}

完整代码

更多相关文章

  1. Kotlin + Android(安卓)KTX让开发更简单
  2. Android(安卓)NDK&JNI开发之Native崩溃日志分析方法
  3. Android(安卓)UI- 对话框 (AlertDialog & ProgressDialog)
  4. Android(安卓)Studio报Element XXXX must be declared的解决方法
  5. 使用android画布的save()和restore()方法
  6. Android运行Socket项目 Error: ShouldNotReachHere()
  7. android studio中的so库调用
  8. 细说:AndroidStudio插件
  9. 毕业设计遇到的问题及解决方法

随机推荐

  1. Androidの自定义进度条ProgressBar实现
  2. 解决Android的ListView控件滚动时背景变
  3. Android eclipse - aapt.exe has stopped
  4. android handlerthread 通知机制
  5. Android常见问题总结(五)
  6. Android Service学习
  7. Android中的进程和线程学习笔记
  8. 将项目从Eclipse转到Android Studio
  9. Android消息机制(Handler原理)
  10. Android消息推送(二)--基于MQTT协议实现