因爲近期項目中要用到插件,因此特意去翻找資料學習了一番,如今在這裏分享我所學到的東西給你們,有什麼錯誤的但願能給我指出來,文章有點長,但願你們能認真讀完。android
近些年來,插件化可謂是特別的火熱,就拿支付寶美團等軟件來講,都是使用這個技術來支撐他們的產品。可是什麼是插件化呢,插件化到底有什麼好處呢?git
插件化也就是運行的APP(宿主APP)去加載插件APP(沒有安裝的APP),這就是所謂的插件化開發。github
插件化到底運行在什麼場景下呢?其實插件化使用的場景有不少,這裏就好比下圖的支付寶或者美團等APP,點擊某個相應的item,就會跳轉到相應的頁面當中,其實這個頁面是插件apk中的頁面,可是它到底怎麼作到的呢?怎麼作到不安裝apk而加載插件中的頁面呢?api
下面咱們就來探索探索不用安裝插件apk是怎麼去加載裏面的Activity、Service、BroadCastReceiver等這些組件的。本篇文章所提的是佔位式(插樁式)插件化。數組
因爲插件apk是沒有安裝的,也就是插件apk沒有組件的一些環境,好比context上下文對象之類的,若是要用到這些環境就必須依賴宿主的環境運行。因此咱們就要宿主跟插件之間定義一個標準。用來傳遞宿主中的環境給插件。bash
第一步:app
首先咱們先定義一個標準,讓插件實現咱們的標準來傳遞宿主APP的環境,下圖是定義Activity類的標準,下面咱們從加載插件中的Activity開始講起。ide
首先咱們要在插件中實現剛纔咱們定義的標準,因爲插件都須要宿主APP的環境,因此咱們就定義一個基類來實現該標準,而後讓咱們的插件的Activity來繼承該基類,該Activity就具備了宿主的環境了。如圖所示。學習
第二步: build工程,獲得插件apk,命名爲 plugin.apk 並把它放到咱們的sd目錄下。讓宿主APP來加載插件。 首先咱們要加載插件apk中的類,就須要用到DexClassLoader這個類,下面是用該類來加載插件apk的方法。ui
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對象了,整體代碼以下:
//加載插件的資源文件
//一、獲取插件的AssetManager
AssetManager pluginAssetManager = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
addAssetPath.setAccessible(true);
addAssetPath.invoke(pluginAssetManager, path);
//二、獲取宿主的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數組咱們就能夠拿到 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的話咱們都要執行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 是其裏面的靜態內部類。
從這個內部類裏咱們找到了咱們想要的東西,就是解析 Manifest.xml 文件後的數據類。咱們能夠經過反射來獲取這些屬性的值。那操做的步驟是怎樣的呢?第一步:
咱們應該先模擬 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);
//二、獲取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 這個類入手,分析該類得出的結果
經過圖中的方法能夠獲取一個userId;因此咱們就以反射來獲取該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);
複製代碼
以上咱們就能夠獲取到一個ActivityInfo 對象了。
第三步: 咱們知道註冊廣播的時候還要添加一個IntentFliter,可是這個應該從哪裏取到呢? 其實咱們剛纔分析的 Activity 那個類提及 ,它繼承與 Component 在跟進這個類去看,發現裏面有個 intents 的集合,查看泛型繼承的 IntentInfo,就是咱們想要的 IntentFliter,因此咱們要獲得這個集合,而後進行遍歷就能夠獲得 IntentFliter,而後咱們就能夠註冊廣播了。
因此註冊廣播的代碼以下:
//三、遍歷全部的靜態廣播
//Activity 該Activity 不是四大組件裏面的activity,而是一個Java bean對象,用來封裝清單文件中的activity和receiver
for (Object mActivity : receivers) {
//四、獲取該廣播的全類名 即 <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();
//五、獲取 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) {
//六、註冊廣播
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());
//加載插件的資源文件
//一、獲取插件的AssetManager
AssetManager pluginAssetManager = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
addAssetPath.setAccessible(true);
addAssetPath.invoke(pluginAssetManager, path);
//二、獲取宿主的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;
}
//一、解析插件包 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);
//二、獲取Package類下的 public final ArrayList<Activity> receivers = new ArrayList<Activity>(0); 廣播集合
Field mReceiversField = mPackage.getClass().getDeclaredField("receivers");
ArrayList<Object> receivers = (ArrayList<Object>) mReceiversField.get(mPackage);
//三、遍歷全部的靜態廣播
//Activity 該Activity 不是四大組件裏面的activity,而是一個Java bean對象,用來封裝清單文件中的activity和receiver
for (Object mActivity : receivers) {
//四、獲取該廣播的全類名 即 <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();
//五、獲取 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) {
//六、註冊廣播
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