Android插件开发可以把应用逻辑分拆成不同的模块,每个模块单独开发测试,最终统一集成到App中。本文将介绍Android插件的实现原理和详细步骤。
一、实现原理
Android插件开发主要是基于ClassLoader的机制实现的。普通的Android应用的类加载器是提前确定的,即应用启动后,系统以特定顺序加载dex文件,所有代码都在同一个ClassLoader中运行。而插件开发通过动态加载代表插件的dex文件,然后在特定的ClassLoader上运行。
ClassLoader机制本质上是一个类寻找机制,当一个类被Java虚拟机所需要时,ClassLoader就会根据指定的类名寻找对应的Class文件,并把它转换成Java中的Class对象。ClassLoader整个流程如下:
1.系统首先检查是否已经加载了这个类,如果已经加载则直接返回已加载的Class对象。否则进入第二步。
2.如果该类尚未加载,则委托它的Parent ClassLoader寻找。
3.如果父ClassLoader找到了该类,则返回已加载的Class对象。如果父ClassLoader还没有找到,则继续寻找,直到找到为止。如果父类都没有找到该类,则会自己去寻找类文件,如果找到了就会加载,否则会抛出ClassNotFoundException异常。
根据ClassLoader的机制,我们可以用动态加载插件的方式来实现插件化,即将插件作为一个单独的ClassLoader运行。
二、插件开发步骤
1.创建插件工程
首先我们要创建一个单独的插件工程,即插件代码单独开发,并且打成一个jar包或apk文件。在插件项目的build.gradle中需要设置为:
android {
...
defaultConfig {
...
//启用插件机制
pluginManager.enabled = true
//设置插件gradle版本
pluginManager.mavenUrl = "https://xxx.xx.xxx"
pluginManager.classpath = 'com.xxx.xxx:plugin:1.0.0'
}
}
其中,pluginManager.enabled设置为true,则代表启用插件机制;pluginManager.mavenUrl设置为可以访问插件的url地址,pluginManager.classpath设置为插件的依赖的gradle库。设置完毕后,我们再将工程打成jar包或apk文件。
2.动态加载插件
把插件写成独立的Class文件,在插件的Activtiy中继承Activity,然后通过反射的方式动态加载插件。
下面是一个简单的插件实现例子:
//插件MainActivity
public class PluginActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_plugin_layout);
}
}
//主app中启动插件MainActivity
private void startPluginActivity() {
//通过反射实例化插件
String apkPath = "/sdcard/testPlugin.apk";
String className = "com.test.plugin.MainActivity";
File dexOutputDir = this.getDir("dex", 0);
DexClassLoader classLoader =
new DexClassLoader(apkPath, dexOutputDir.getAbsolutePath(), null, getClass().getClassLoader());
try {
Class> clazz = classLoader.loadClass(className);
Intent intent = new Intent(MainActivity.this, clazz);
startActivity(intent);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
3.插件Api实现
在插件中我们可以通过Binder机制将插件的api接口暴露给插件主程序,插件主程序可以通过Api代理的方式调用插件中的api。
下面是插件Api实现的例子:
//插件服务
public class PluginService extends Service {
private MyBinder myBinder;
private class MyBinder extends IPluginInterface.Stub {
@Override
public String getVersionName() throws RemoteException {
return PluginService.this.getVersionName();
}
@Override
public int getVersionCode() throws RemoteException {
return PluginService.this.getVersionCode();
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
if (myBinder == null) {
myBinder = new MyBinder();
}
return myBinder;
}
private String getVersionName() {
try {
PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
return pi.versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return "";
}
private int getVersionCode() {
try {
PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
return pi.versionCode;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return -1;
}
}
//主程序Api代理
public class PluginManager {
private String mPluginApkPath;
private Context mContext;
private static PluginManager sPluginManager;
private PluginManager(Context context) {
mContext = context.getApplicationContext();
}
public static void init(Context context, String pluginApkPath) {
if (sPluginManager == null) {
sPluginManager = new PluginManager(context);
}
sPluginManager.mPluginApkPath = pluginApkPath;
}
public static PluginManager getInstance() throws Exception {
if (sPluginManager != null) {
return sPluginManager;
} else {
throw new Exception("PluginManager没有被初始化!");
}
}
public IBinder getPluginApi() {
DexClassLoader dexClassLoader =
new DexClassLoader(mPluginApkPath, mContext.getDir("dex", 0).getAbsolutePath(), null, mContext.getClassLoader());
try {
Class> clazz = dexClassLoader.loadClass("com.xx.xx.XXXService");
Service service = (Service) clazz.newInstance();
IBinder binder = service.onBind(new Intent());
return binder;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
4.插件混淆
为了保证插件的代码安全,需要对插件代码进行混淆,混淆的配置可以在插件工程的build.gradle中进行设置:
android {
...
buildTypes {
release {
...
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
需要注意的是,在插件中可能使用某些特定的框架或库,在混淆时需要加入相关的keep规则,保证插件代码的正确运行。
以上就是Android插件开发的原理和详细步骤。插件化的实现可以大大降低应用的耦合性,提高应用的扩展性,为今后的Android应用开发提供了更多的可能性。