前言
由于近期项目中要用到插件,所以特地去翻找资料学习了一番,现在在这里分享我所学到的东西给大家,有什么错误的希望能给我指出来,文章有点长,希望大家能认真读完。
近些年来,插件化可谓是特别的火热,就拿支付宝美团等软件来说,都是使用这个技术来支撑他们的产品。但是什么是插件化呢,插件化到底有什么好处呢?
插件化也就是运行的APP(宿主APP)去加载插件APP(没有安装的APP),这就是所谓的插件化开发。

插件化到底运行在什么场景下呢?其实插件化使用的场景有很多,这里就比如下图的支付宝或者美团等APP,点击某个相应的item,就会跳转到相应的页面当中,其实这个页面是插件apk中的页面,但是它到底怎么做到的呢?怎么做到不安装apk而加载插件中的页面呢?

下面我们就来探索探索不用安装插件apk是怎么去加载里面的Activity、Service、BroadCastReceiver等这些组件的。本篇文章所提的是占位式(插桩式)插件化。
由于插件apk是没有安装的,也就是插件apk没有组件的一些环境,比如context上下文对象之类的,如果要用到这些环境就必须依赖宿主的环境运行。所以我们就要宿主跟插件之间定义一个标准。用来传递宿主中的环境给插件。

加载插件中的Activity
第一步:
首先我们先定义一个标准,让插件实现我们的标准来传递宿主APP的环境,下图是定义Activity类的标准,下面我们从加载插件中的Activity开始讲起。


首先我们要在插件中实现刚才我们定义的标准,由于插件都需要宿主APP的环境,所以我们就定义一个基类来实现该标准,然后让我们的插件的Activity来继承该基类,该Activity就具有了宿主的环境了。如图所示。

第二步: build工程,得到插件apk,命名为 plugin.apk 并把它放到我们的sd目录下。让宿主APP来加载插件。 首先我们要加载插件apk中的类,就需要用到DexClassLoader这个类,下面是用该类来加载插件apk的方法。
File file = new File(path);
File pluginDir = context.getDir("plugin", Context.MODE_PRIVATE);
//加载插件的class
dexClassLoader = new DexClassLoader(path, pluginDir.getAbsolutePath(), null, context.getClassLoader());
参数说明:
path:插件所存放的目录(plugin.apk存放的目录)
pluginDir.getAbsolutePath():插件apk解析后dex文件所存放的路径
null:该参数是so库存放的路径,由于插件里没有so库,所以为null。
context.getClassLoader():类加载器
获取到了DexClassLoader的对象,我们就可以拿到插件中的类了,接下来我们要获取插件apk中的资源对象,也就是 Resources对象。由于插件apk没有宿主的环境,也就无法使用 context.getResources()的方式来得到一个Resources对象,我们只能new 一个对象出来。
//Resources的构造方法
@Deprecated
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
this(null);
mResourcesImpl = new ResourcesImpl(assets, metrics, config, new DisplayAdjustments());
}
从上面代码中可以看出,我们可以看出创建一个 Resources对象需要传递一个 AssetManager、DisplayMetrics、Configuration对象,而这些对象我们怎么可以获取到呢?其实后两个参数很容易获取到。后面的两个参数我们可以直接用宿主的Resources对象里面的属性就可以了,如下
Resources appResources = context.getResources();
DisplayMetrics displayMetrics = appResources.getDisplayMetrics();
Configuration configuration = appResources.getConfiguration();
现在我们比较头疼的问题是如何获取AssetManager这个类的对象?其实我们可以利用反射的方式来获取一个AssetManager的对象,如下
AssetManager pluginAssetManager = AssetManager.class.newInstance();
阅读源码发现,AssetManager类里面要更改资源路径就必须要调用 addAssetPath 方法。从这里得出我们可以反射拿到这个方法,然后传入插件apk的资源路径即可。

AssetManager pluginAssetManager = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
addAssetPath.setAccessible(true);
addAssetPath.invoke(pluginAssetManager, path);
通过上述代码可以获取到我们的插件apk的 AssetManager对象了,拥有该对象,我们就可以获取到 Resources对象了,总体代码如下:
//加载插件的资源文件
//1、获取插件的AssetManager
AssetManager pluginAssetManager = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
addAssetPath.setAccessible(true);
addAssetPath.invoke(pluginAssetManager, path);
//2、获取宿主的Resources
Resources appResources = context.getResources();
//实例化插件的Resources
pluginResource = new Resources(pluginAssetManager, appResources.getDisplayMetrics(), appResources.getConfiguration());
拿到了DexClassLoader对象和Resources对象我们就可以加载插件apk中的activity等组件了。要跳转到插件中的activity,我们首先要获取到插件找那个的Activity,通过 PackageManager.getPackageArchiveInfo()我们可以得到一个 PackageInfo对象,而这个对象里面就有我们需要的Activity数组,如下图:

//获取插件包的Activity
PackageManager packageManager = getPackageManager();
PackageInfo packageArchiveInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
//获取在manifest文件中注册的第一个activity
ActivityInfo activity = packageArchiveInfo.activities[0];
Intent intent = new Intent(this, ProxyActivity.class);
intent.putExtra("className", activity.name);
通过以上代码显示,我们先是跳转到了一个ProxyActivity,然后把插件的Activity类名传递过去,为什么不直接跳转到插件的Activity呢?原因是因为插件的Activity没有在我们的宿主的manifest文件中进行注册,如果直接跳转就会发生崩溃,所以我们这里先跳转到一个代理的Activity,因为该Activity是宿主里面的并且在manifest文件中进行注册了,所以我们用它来进行模拟Activity入栈出栈的操作,当我们的代理Activity回调onCreate() 的时候,我们可以获取到传递过来的className来反射得到我们的插件中的Activity对象,由于我们插件中的Activity是实现了上面所述的标准,我们可以通过标准来调用插件Activity中的生命周期。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//真正的加载插件里面的Activity
String className = getIntent().getStringExtra("className");
try {
Class<?> pluginActivity1Clazz = getClassLoader().loadClass(className);
Constructor<?> constructor = pluginActivity1Clazz.getConstructor(new Class[]{});
pluginActivity1 = (IActivityInterface) constructor.newInstance(new Object[]{});
pluginActivity1.insertAppContext(this);
Bundle bundle = new Bundle();
bundle.putString("value", "我是宿主传递过来的字符串");
pluginActivity1.onCreate(bundle);
} catch (Exception e) {
e.printStackTrace();
}
}
当执行完上述代码后我们就可以加载出插件的Activity了。执行效果如下:

插件内跳转Activity
跳转成功后,我们就要实现插件中的Activity跳转到插件中的另一个Activity了。大家都知道跳转Activity的话我们都要执行startActivity() 方法,而查看源码而知,startActivity() 里调用了 this.startActivity(intent, null);这里的 this表示着上下文对象,但是我们的插件APP没有上下文对象的环境,如果执行了 startActivity() 方法肯定会发生崩溃的,那么怎样才能避免崩溃,并且成功跳转呢。其实非常简单,我们只要使用宿主APP的环境就可以来实现该功能了,只不过是稍微有些麻烦。我们需要重写 startActivity() 方法,并且传递我们需要跳转的Activity的全类名给宿主,且跳转的方法交给我们的宿主来进行就可以了,然后我们重写宿主APP的代理Activity 的startActivity() 方法用来接收插件APP传递过来的全类名,最后执行宿主APP的代理Activity 的startActivity() 方法即可。如下:


另外提示一点,我们插件的Activity 所有使用的都是宿主的环境,比如 setContentView() 、findViewById() 等方法,运行后效果如下:

至此,我们动态加载插件APP的Activity已经讲完了,至于动态加载Service与动态加载广播的方法跟加载Activity的方法类似,这里不做讲解。下面讲解的是静态注册插件中的广播。

注册插件内的静态广播
要加载插件中的静态广播,我们先来提问一个问题。我们APP中的静态广播到底是什么时候被注册的呢? 其实在手机开机的时候,手机里面所有的已经安装APP,系统会再次进行安装一遍,这也就能说明Android手机开机的时候为什么会那么慢。等到安装完成后,会马上扫描 /data/app/ 目录,然后逐个去解析该目录下的所有 apk里面的 Manifest.xml 文件, 如果里面有静态广播后,就会自动注册。
我们现在来研究系统是如何去解析 apk文件里面的组件信息的。我们知道解析apk 文件会用到 PackageManagerService 这个类,所以我们来查看这个类的源码。

跟踪源码发现,执行到 packageParser.parsePackage() 这个方法的时候会返回一个 PackageParser.Package 对象,我们进去查看一下 PackageParser 这个类的源码,发现 Package 是其里面的静态内部类。

第一步:
我们应该先模拟 PMS 解析安装包的形式获取到这些信息。阅读源码发现,PackageParser.class 里的 Package parsePackage(File packageFile, int flags) 正好返回一个 Package 对象,所以我们需要执行这个方法来获取它。

Class<?> mPackageParserClass = Class.forName("android.content.pm.PackageParser");
Object mPackageParser = mPackageParserClass.newInstance();
Method parsePackageMethod = mPackageParserClass.getMethod("parsePackage", File.class, int.class);
Object mPackage = parsePackageMethod.invoke(mPackageParser, file, PackageManager.GET_ACTIVITIES);
//2、获取Package类下的 public final ArrayList<Activity> receivers = new ArrayList<Activity>(0); 广播集合
Field mReceiversField = mPackage.getClass().getDeclaredField("receivers");
//本质上是 ArrayList<Activity> receivers
ArrayList<Object> receivers = (ArrayList<Object>) mReceiversField.get(mPackage);
第二步:
在第一步我们获取到了 receivers 这个广播集合,有了这个集合我们就可以拿到里面的广播来进行注册了。我们先来看看先跟到这个 List<Activity>
尖括号里面的对象,发现这个Activity 不是我们四大组件的Activity,它纯粹是为了封装这些数据的Java bean,所以千万不要把它搞混了。



Class<?> mPackageUserStateClass = Class.forName("android.content.pm.PackageUserState");
Object mPackageUserState = mPackageUserStateClass.newInstance();
现在就剩下最后一个参数了,就是我们的userId,到底怎么获取这个参数值呢?其实我们可以从 android.os.UserHandle 这个类入手,分析该类得出的结果

//先获取到 ActivityInfo类
Class<?> mPackageUserStateClass = Class.forName("android.content.pm.PackageUserState");
Object mPackageUserState = mPackageUserStateClass.newInstance();
Method generateActivityInfoMethod = mPackageParserClass.getMethod("generateActivityInfo", mActivity.getClass(),
int.class, mPackageUserStateClass, int.class);
//获取userId
Class<?> mUserHandleClass = Class.forName("android.os.UserHandle");
//public static @UserIdInt int getCallingUserId()
int userId = (int) mUserHandleClass.getMethod("getCallingUserId").invoke(null);
//执行此方法 由于是静态方法 所以不用传对象
ActivityInfo activityInfo = (ActivityInfo) generateActivityInfoMethod.invoke(null, mActivity, 0, mPackageUserState, userId);
以上我们就可以获取到一个ActivityInfo 对象了。
第三步: 我们知道注册广播的时候还要添加一个IntentFliter,但是这个应该从哪里取到呢? 其实我们刚才分析的 Activity 那个类说起 ,它继承与 Component 在跟进这个类去看,发现里面有个 intents 的集合,查看泛型继承的 IntentInfo,就是我们想要的 IntentFliter,所以我们要得到这个集合,然后进行遍历就可以得到 IntentFliter,然后我们就可以注册广播了。


所以注册广播的代码如下:
//3、遍历所有的静态广播
//Activity 该Activity 不是四大组件里面的activity,而是一个Java bean对象,用来封装清单文件中的activity和receiver
for (Object mActivity : receivers) {
//4、获取该广播的全类名 即 <receiver android:name=".PluginStaticReceiver"> android:name属性后面的值
// /**
// * Public name of this item. From the "android:name" attribute.
// */
// public String name;
// public static final ActivityInfo generateActivityInfo(Activity a, int flags,
// PackageUserState state, int userId)
//先获取到 ActivityInfo类
Class<?> mPackageUserStateClass = Class.forName("android.content.pm.PackageUserState");
Object mPackageUserState = mPackageUserStateClass.newInstance();
Method generateActivityInfoMethod = mPackageParserClass.getMethod("generateActivityInfo", mActivity.getClass(),
int.class, mPackageUserStateClass, int.class);
//获取userId
Class<?> mUserHandleClass = Class.forName("android.os.UserHandle");
//public static @UserIdInt int getCallingUserId()
int userId = (int) mUserHandleClass.getMethod("getCallingUserId").invoke(null);
//执行此方法 由于是静态方法 所以不用传对象
ActivityInfo activityInfo = (ActivityInfo) generateActivityInfoMethod.invoke(null, mActivity, 0, mPackageUserState, userId);
String receiverClassName = activityInfo.name;
Class<?> receiverClass = getClassLoader().loadClass(receiverClassName);
BroadcastReceiver receiver = (BroadcastReceiver) receiverClass.newInstance();
//5、获取 intent-filter public final ArrayList<II> intents;这个是intent-filter的集合
//静态内部类反射要用 $+类名
//getField(String name)只能获取public的字段,包括父类的;
//而getDeclaredField(String name)只能获取自己声明的各种字段,包括public,protected,private。
Class<?> mComponentClass = Class.forName("android.content.pm.PackageParser$Component");
Field intentsField = mActivity.getClass().getField("intents");
ArrayList<IntentFilter> intents = (ArrayList<IntentFilter>) intentsField.get(mActivity);
for (IntentFilter intentFilter : intents) {
//6、注册广播
context.registerReceiver(receiver, intentFilter);
}
}
到这里我们已经完成了对插件中的静态广播进行注册。运行效果如下:

至此,我们的占位式插件化已经讲解完毕了,这里贴一下部分类的完整代码
MainActivity.class
public class MainActivity extends AppCompatActivity {
private String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "plugin.apk";
@SuppressLint("HandlerLeak")
private Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 666) {
isLoadSuccess = true;
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
Toast.makeText(MainActivity.this, "加载插件成功!", Toast.LENGTH_SHORT).show();
} else if (msg.what == 0) {
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
Toast.makeText(MainActivity.this, "加载插件失败,请检查插件是否存在!", Toast.LENGTH_SHORT).show();
}
}
};
//是否加载完成
private boolean isLoadSuccess = false;
private ProgressDialog dialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@RequiresApi(api = Build.VERSION_CODES.M)
public void loadPlugin(View view) {
//判断是否已经赋予权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE}, 666);
} else {
showProgress();
PluginManager.getInstance(this).loadPlugin(handler, path);
}
} else {
showProgress();
PluginManager.getInstance(this).loadPlugin(handler, path);
}
}
public void jumpPluginActivity(View view) {
if (isLoadSuccess) {
//获取插件包的Activity
PackageManager packageManager = getPackageManager();
PackageInfo packageArchiveInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
//获取在manifest文件中注册的第一个activity
ActivityInfo activity = packageArchiveInfo.activities[0];
Intent intent = new Intent(this, ProxyActivity.class);
intent.putExtra("className", activity.name);
startActivity(intent);
} else {
Toast.makeText(this, "请先加载插件!", Toast.LENGTH_SHORT).show();
}
}
private void showProgress() {
if (dialog == null) {
dialog = new ProgressDialog(this);
}
dialog.setTitle("加载插件中,请稍后。。。");
dialog.setCancelable(false);
dialog.show();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 666) {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
showProgress();
PluginManager.getInstance(this).loadPlugin(handler, path);
} else {
Toast.makeText(MainActivity.this, "请打开读取SD卡权限", Toast.LENGTH_SHORT).show();
}
}
}
public void parsePlugin(View view) {
PluginManager.getInstance(this).parsePlugin(path);
}
public void sendStaticReceiver(View view) {
Intent intent = new Intent("plugin_static_receiver");
intent.putExtra("str", "我是从宿主中发来的字符");
sendBroadcast(intent);
}
}
PluginManager.class
public class PluginManager {
private static final String TAG = PluginManager.class.getSimpleName();
private static PluginManager instance;
private Context context;
private DexClassLoader dexClassLoader;
private Resources pluginResource;
private PluginManager(Context context) {
this.context = context;
}
public static PluginManager getInstance(Context context) {
if (instance == null) {
synchronized (PluginManager.class) {
if (instance == null) {
instance = new PluginManager(context);
}
}
}
return instance;
}
public void loadPlugin(final Handler handler, final String path) {
new Thread(new Runnable() {
@Override
public void run() {
try {
File file = new File(path);
if (!file.exists()) {
Log.e(TAG, "插件不存在");
return;
}
File pluginDir = context.getDir("plugin", Context.MODE_PRIVATE);
//加载插件的class
dexClassLoader = new DexClassLoader(path, pluginDir.getAbsolutePath(), null, context.getClassLoader());
//加载插件的资源文件
//1、获取插件的AssetManager
AssetManager pluginAssetManager = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
addAssetPath.setAccessible(true);
addAssetPath.invoke(pluginAssetManager, path);
//2、获取宿主的Resources
Resources appResources = context.getResources();
//实例化插件的Resources
pluginResource = new Resources(pluginAssetManager, appResources.getDisplayMetrics(), appResources.getConfiguration());
if (dexClassLoader != null && pluginResource != null) {
handler.sendEmptyMessage(666);
} else {
handler.sendEmptyMessage(0);
}
}catch (Exception e) {
e.printStackTrace();
handler.sendEmptyMessage(0);
}
}
}).start();
}
@SuppressLint("PrivateApi")
public void parsePlugin(String pluginPath) {
try {
File file = new File(pluginPath);
if (!file.exists()) {
Log.e(TAG, "插件不存在");
return;
}
//1、解析插件包 public Package parsePackage(File packageFile, int flags)
Class<?> mPackageParserClass = Class.forName("android.content.pm.PackageParser");
Object mPackageParser = mPackageParserClass.newInstance();
Method parsePackageMethod = mPackageParserClass.getMethod("parsePackage", File.class, int.class);
Object mPackage = parsePackageMethod.invoke(mPackageParser, file, PackageManager.GET_ACTIVITIES);
//2、获取Package类下的 public final ArrayList<Activity> receivers = new ArrayList<Activity>(0); 广播集合
Field mReceiversField = mPackage.getClass().getDeclaredField("receivers");
ArrayList<Object> receivers = (ArrayList<Object>) mReceiversField.get(mPackage);
//3、遍历所有的静态广播
//Activity 该Activity 不是四大组件里面的activity,而是一个Java bean对象,用来封装清单文件中的activity和receiver
for (Object mActivity : receivers) {
//4、获取该广播的全类名 即 <receiver android:name=".PluginStaticReceiver"> android:name属性后面的值
// /**
// * Public name of this item. From the "android:name" attribute.
// */
// public String name;
// public static final ActivityInfo generateActivityInfo(Activity a, int flags,
// PackageUserState state, int userId)
//先获取到 ActivityInfo类
Class<?> mPackageUserStateClass = Class.forName("android.content.pm.PackageUserState");
Object mPackageUserState = mPackageUserStateClass.newInstance();
Method generateActivityInfoMethod = mPackageParserClass.getMethod("generateActivityInfo", mActivity.getClass(),
int.class, mPackageUserStateClass, int.class);
//获取userId
Class<?> mUserHandleClass = Class.forName("android.os.UserHandle");
//public static @UserIdInt int getCallingUserId()
int userId = (int) mUserHandleClass.getMethod("getCallingUserId").invoke(null);
//执行此方法 由于是静态方法 所以不用传对象
ActivityInfo activityInfo = (ActivityInfo) generateActivityInfoMethod.invoke(null, mActivity, 0, mPackageUserState, userId);
String receiverClassName = activityInfo.name;
Class<?> receiverClass = getClassLoader().loadClass(receiverClassName);
BroadcastReceiver receiver = (BroadcastReceiver) receiverClass.newInstance();
//5、获取 intent-filter public final ArrayList<II> intents;这个是intent-filter的集合
//静态内部类反射要用 $+类名
//getField(String name)只能获取public的字段,包括父类的;
//而getDeclaredField(String name)只能获取自己声明的各种字段,包括public,protected,private。
Class<?> mComponentClass = Class.forName("android.content.pm.PackageParser$Component");
Field intentsField = mActivity.getClass().getField("intents");
ArrayList<IntentFilter> intents = (ArrayList<IntentFilter>) intentsField.get(mActivity);
for (IntentFilter intentFilter : intents) {
//6、注册广播
context.registerReceiver(receiver, intentFilter);
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
public Resources getResource() {
return pluginResource;
}
public DexClassLoader getClassLoader() {
return dexClassLoader;
}
}
ProxyActivity.class
public class ProxyActivity extends Activity {
private IActivityInterface pluginActivity1;
private boolean isRegister;
private ProxyReceiver proxyReceiver;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//真正的加载插件里面的Activity
String className = getIntent().getStringExtra("className");
try {
Class<?> pluginActivity1Clazz = getClassLoader().loadClass(className);
Constructor<?> constructor = pluginActivity1Clazz.getConstructor(new Class[]{});
pluginActivity1 = (IActivityInterface) constructor.newInstance(new Object[]{});
pluginActivity1.insertAppContext(this);
Bundle bundle = new Bundle();
bundle.putString("value", "我是宿主传递过来的字符串");
pluginActivity1.onCreate(bundle);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onStart() {
super.onStart();
pluginActivity1.onStart();
}
@Override
protected void onResume() {
super.onResume();
pluginActivity1.onResume();
}
@Override
protected void onPause() {
super.onPause();
pluginActivity1.onPause();
}
@Override
protected void onStop() {
super.onStop();
pluginActivity1.onStop();
}
@Override
protected void onRestart() {
super.onRestart();
pluginActivity1.onRestart();
}
@Override
protected void onDestroy() {
super.onDestroy();
pluginActivity1.onDestroy();
if (isRegister) {
unregisterReceiver(proxyReceiver);
}
}
@Override
public Resources getResources() {
return PluginManager.getInstance(this).getResource();
}
@Override
public ClassLoader getClassLoader() {
return PluginManager.getInstance(this).getClassLoader();
}
@Override
public void startActivity(Intent intent) {
String className = intent.getStringExtra("className");
//自己跳自己
Intent newIntent = new Intent(this, this.getClass());
newIntent.putExtra("className", className);
super.startActivity(newIntent);
}
@Override
public ComponentName startService(Intent service) {
String className = service.getStringExtra("className");
ProxyService.pluginServiceClassName = className;
//自己跳自己
Intent newService = new Intent(this, ProxyService.class);
newService.putExtra("className", className);
return super.startService(newService);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
proxyReceiver = new ProxyReceiver(receiver.getClass().getName());
isRegister = true;
return super.registerReceiver(proxyReceiver, filter);
}
}
插件中的BaseActivity.class
public abstract class BaseActivity extends Activity implements IActivityInterface {
protected Activity appActivity;
@Override
public void insertAppContext(Activity appActivity) {
this.appActivity = appActivity;
}
@SuppressLint("MissingSuperCall")
@Override
public void onCreate(Bundle savedInstanceState) { }
@SuppressLint("MissingSuperCall")
@Override
public void onStart() { }
@SuppressLint("MissingSuperCall")
@Override
public void onResume() { }
@SuppressLint("MissingSuperCall")
@Override
public void onPause() { }
@SuppressLint("MissingSuperCall")
@Override
public void onStop() { }
@SuppressLint("MissingSuperCall")
@Override
public void onRestart() { }
@SuppressLint("MissingSuperCall")
@Override
public void onDestroy() { }
@Override
public void setContentView(int resId) {
appActivity.setContentView(resId);
}
@Override
public <T extends View> T findViewById(int id) {
return appActivity.findViewById(id);
}
@Override
public void startActivity(Intent intent) {
Intent newIntent = new Intent();
newIntent.putExtra("className", intent.getComponent().getClassName());
appActivity.startActivity(newIntent);
}
@Override
public ComponentName startService(Intent service) {
Intent newService = new Intent();
newService.putExtra("className", service.getComponent().getClassName());
return appActivity.startService(newService);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
return appActivity.registerReceiver(receiver, filter);
}
@Override
public void sendBroadcast(Intent intent) {
appActivity.sendBroadcast(intent);
}
}
Plugin1Activity.class
public class Plugin1Activity extends BaseActivity {
private static final String TAG = Plugin1Activity.class.getSimpleName();
private boolean isRegister;
@SuppressLint("MissingSuperCall")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreateActivity");
setContentView(R.layout.activity_plugin1);
TextView textView = findViewById(R.id.textView);
Button button = findViewById(R.id.btn_jump);
String value = savedInstanceState.getString("value");
textView.setText(value != null ? value : "");
Toast.makeText(appActivity, value, Toast.LENGTH_SHORT).show();
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(appActivity, Plugin2Activity.class);
startActivity(intent);
}
});
findViewById(R.id.btn_start_plugin_service).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startService(new Intent(appActivity, PluginService.class));
}
});
findViewById(R.id.btn_register_plugin_receiver).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!isRegister) {
IntentFilter filter = new IntentFilter();
filter.addAction("yuongzw");
registerReceiver(new PluginReceiver(), filter);
isRegister = true;
}
}
});
findViewById(R.id.btn_send_plugin_receiver).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("yuongzw");
intent.putExtra("str", "我是从插件发送的广播");
sendBroadcast(intent);
}
});
}
@SuppressLint("MissingSuperCall")
@Override
public void onStart() {
super.onStart();
Log.d(TAG, "onStartActivity");
}
@SuppressLint("MissingSuperCall")
@Override
public void onResume() {
Log.d(TAG, "onResumeActivity");
}
@SuppressLint("MissingSuperCall")
@Override
public void onPause() {
Log.d(TAG, "onPauseActivity");
}
@SuppressLint("MissingSuperCall")
@Override
public void onStop() {
Log.d(TAG, "onStopActivity");
}
@SuppressLint("MissingSuperCall")
@Override
public void onRestart() {
Log.d(TAG, "onRestartActivity");
}
@SuppressLint("MissingSuperCall")
@Override
public void onDestroy() {
Log.d(TAG, "onDestroyActivity");
}
}
PluginStaticReceiver.class
public class PluginStaticReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if ("plugin_static_receiver".equals(intent.getAction())) {
Toast.makeText(context, "我是插件的静态广播,我收到了:" + intent.getStringExtra("str"), Toast.LENGTH_SHORT).show();
}
}
}
下面附上Github的项目地址,给需要的小伙伴进行参考:
项目地址:PluginLoadDemo