Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all articles
Browse latest Browse all 11804

DexClassLoader 实现 Android 插件加载

$
0
0

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) {}

完整代码


Viewing all articles
Browse latest Browse all 11804

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>