示例代码:
https://gitee.com/vigiles/ghost-plugin-demo
1.项目
1.1.宿主
打开AndroidStudio,新建项目,PhoneAndTablet项目。
填写配置信息。
默认入口宿主模块是app。
1.2.中间人
创建一个新的模块。
模块类型是Library。填写模块名及其它配置。
模块内默认为空。
1.3.插件
新建模块。
类型和入口模块相同,PhoneAndTablet。
模块自动创建默认的入口Activity。
1.4.添加依赖
配置项目结构。
依赖面板,选择宿主模块和插件模块,添加其依赖模块为中间人模块。
选择中间人模块。
确定,依赖列表出现中间人。
即在宿主和插件的gradle里添加引用中间人。
2.中间人
充当桥梁,标准。插件依据此标准创造;宿主依据此标准调用。
2.1.工具类
职责是加载插件apk,并创造专属的各种上下文相关对象。并同时被插件和宿主使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; import android.util.Log; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import dalvik.system.DexClassLoader; public class PluginManager { private static PluginManager instance = new PluginManager(); private Context context; private DexClassLoader dexClassLoader; private PackageInfo packageInfo; private Resources resources; private PluginManager() { } public static PluginManager getInstance() { return instance; } /** * 传入当前应用(宿主)的ctx * * @param context 宿主的ctx */ public void setContext(Context context) { this.context = context; Log.e("ard", "中间人:向我的插件mng放了 context"); } /** * 加载插件apk文件 * * @param path 文件路径 */ public void loadPluginApkFile(String path) { File outDexFile = context.getDir("dex", Context.MODE_PRIVATE); // 当前应用(宿主)的内部私有存储路径 dexClassLoader = new DexClassLoader(path, outDexFile.getAbsolutePath(), null, context.getClassLoader()); // 插件apk的类加载器 PackageManager packageManager = context.getPackageManager(); // 包管理器-系统级单例 packageInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES); // 插件apk内的全部aty try { Class<AssetManager> assetManagerClass = AssetManager.class; AssetManager assetManager = assetManagerClass.newInstance(); // 创建一个AssetManager Method addAssetPath = assetManagerClass.getMethod("addAssetPath", String.class); addAssetPath.invoke(assetManager, path); // 给AssetManager设置apk路径 Resources resources = context.getResources(); this.resources = new Resources(assetManager, resources.getDisplayMetrics(), resources.getConfiguration()); // 插件apk的Resource } catch (InstantiationException e) { Log.e("ard", "中间人:assetsmng实例化错误:" + e.getMessage()); } catch (IllegalAccessException e) { Log.e("ard", "中间人:assetsmng实例反射方法调用非法:" + e.getMessage()); } catch (NoSuchMethodException e) { Log.e("ard", "中间人:assetsmng实例反射方法错误:" + e.getMessage()); } catch (InvocationTargetException e) { Log.e("ard", "中间人:assetsmng实例反射方法调用错误" + e.getMessage()); } Log.e("ard", "中间人:我的插件mng加载了插件apk"); } public DexClassLoader getDexClassLoader() { Log.e("ard", "中间人:从我的插件mng拿了 DexClassLoader"); return dexClassLoader; } public PackageInfo getPackageInfo() { Log.e("ard", "中间人:从我的插件mng拿了 PackageInfo"); return packageInfo; } public Resources getResources() { Log.e("ard", "中间人:从我的插件mng拿了 Resources"); return resources; } } |
2.2.插件的接口
即插件的建造标准、宿主的使用标准。目的在于接管插件的生命周期。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; import android.os.Bundle; import android.os.Looper; import android.os.PersistableBundle; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.Window; import android.view.WindowManager; import androidx.appcompat.app.AppCompatActivity; import java.util.concurrent.Executor; /** * Activity的生命周期接口 * 插件的Activity都须实现此接口 */ public interface IPluginActivity { public void attach(AppCompatActivity rootActivity); public void onCreate( Bundle savedInstanceState); public void onNewIntent(Intent intent); public void onStart(); public void onRestart(); public void onResume(); public void onPause(); public void onStop(); public void onDestroy(); public void setContentView(View view); public void setContentView(int layoutResID); public <T extends View> T findViewById(int id); public Intent getIntent(); public void onSaveInstanceState(Bundle outState); public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState); public boolean onKeyDown(int keyCode, KeyEvent event); public void onBackPressed(); public void onTouchEvent(); public LayoutInflater getLayoutInflater(); public ClassLoader getClassLoader(); public Context getBaseContext(); public AssetManager getAssets(); public Resources getResources(); public PackageManager getPackageManager(); public ContentResolver getContentResolver(); public Looper getMainLooper(); public Executor getMainExecutor(); public Context getApplicationContext(); public void startActivity(Intent intent); public void startActivityForResult(Intent intent, int requestCode); public WindowManager getWindowManager(); public ApplicationInfo getApplicationInfo(); public Window getWindow(); } |
2.3.接口实现类
每个插件的Activity都要实现标准,这里统统实现作为父类,简化代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; import android.os.Build; import android.os.Bundle; import android.os.Looper; import android.os.PersistableBundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.Window; import android.view.WindowManager; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import java.util.concurrent.Executor; /* * 插件不是系统加载的,没有天生的生命周期。 * 实现IPluginActivityLifecycle,强加生命,赋予上下文。 * * */ public class BasePluginActivity extends AppCompatActivity implements IPluginActivity { /* 通过中间人传入的应用(宿主)的上下文 */ protected AppCompatActivity that; @Override public void attach(AppCompatActivity rootActivity) { that = rootActivity; Log.e("ard", "中间人base:获得上下文:" + that.getClass().getSimpleName()); } @Override public void setContentView(View view) { if (null == that) { super.setContentView(view); } else { that.setContentView(view); } Log.e("ard", "中间人base:执行:setContentView(View view)"); } @Override public void setContentView(int layoutResID) { that.setContentView(layoutResID); Log.e("ard", "中间人base:执行:setContentView(int layoutResID)"); } @Override public <T extends View> T findViewById(int id) { Log.e("ard", "中间人base:执行:View findViewById(int id)"); return that.findViewById(id); } @Override public Intent getIntent() { Log.e("ard", "中间人base:执行:Intent getIntent()"); return that.getIntent(); } @Override public void onCreate(Bundle savedInstanceState) { Log.e("ard", "中间人base:执行:onCreate(Bundle savedInstanceState)"); } @Override public void onStart() { Log.e("ard", "中间人base:执行:onStart()"); } @Override public void onRestart() { Log.e("ard", "中间人base:执行:onRestart()"); } @Override public void onNewIntent(Intent intent) { Log.e("ard", "中间人base:执行:onNewIntent(intent)"); } @Override public void onResume() { Log.e("ard", "中间人base:执行:onResume()"); } @Override public void onPause() { Log.e("ard", "中间人base:执行:onPause()"); } @Override public void onStop() { Log.e("ard", "中间人base:执行:onStop()"); } @Override public void onDestroy() { Log.e("ard", "中间人base:执行:onDestroy()"); } @Override public void onSaveInstanceState(Bundle outState) { Log.e("ard", "中间人base:执行:onSaveInstanceState(outState)"); } @Override public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) { Log.e("ard", "中间人base:执行:onSaveInstanceState(outState, outPersistentState)"); } @Override public void onTouchEvent() { Log.e("ard", "中间人base:执行:onTouchEvent()"); } @Override public LayoutInflater getLayoutInflater() { Log.e("ard", "中间人base:执行:LayoutInflater getLayoutInflater()"); return that.getWindow().getLayoutInflater(); } @Override public ClassLoader getClassLoader() { Log.e("ard", "中间人base:执行:ClassLoader getClassLoader()"); return PluginManager.getInstance().getDexClassLoader(); } public Context getBaseContext() { Log.e("ard", "中间人base:执行:Context getBaseContext()"); return that.getBaseContext(); } @Override public AssetManager getAssets() { Log.e("ard", "中间人base:执行:AssetManager getAssets()"); return that.getAssets(); } @Override public Resources getResources() { Log.e("ard", "中间人base:执行:Resources getResources()"); return PluginManager.getInstance().getResources(); } @Override public PackageManager getPackageManager() { Log.e("ard", "中间人base:执行:PackageManager getPackageManager()"); return that.getPackageManager(); } @Override public ContentResolver getContentResolver() { Log.e("ard", "中间人base:执行:ContentResolver getContentResolver()"); return that.getContentResolver(); } @Override public Looper getMainLooper() { Log.e("ard", "中间人base:执行:Looper getMainLooper()"); return that.getMainLooper(); } @RequiresApi(api = Build.VERSION_CODES.P) @Override public Executor getMainExecutor() { Log.e("ard", "中间人base:执行:Executor getMainExecutor()"); return that.getMainExecutor(); } @Override public Context getApplicationContext() { Log.e("ard", "中间人base:执行:Context getApplicationContext()"); return that.getApplicationContext(); } @Override public void startActivity(Intent intent) { Intent i = new Intent(); i.putExtra("className", intent.getComponent().getClassName()); that.startActivity(i); Log.e("ard", "中间人base:执行:startActivity(Intent intent)"); } @Override public void startActivityForResult(Intent intent, int requestCode) { Intent i = new Intent(); i.putExtra("className", intent.getComponent().getClassName()); that.startActivityForResult(i, requestCode); Log.e("ard", "中间人base:执行:startActivityForResult(intent, requestCode)"); } @Override public WindowManager getWindowManager() { Log.e("ard", "中间人base:执行:WindowManager getWindowManager()"); return that.getWindowManager(); } @Override public ApplicationInfo getApplicationInfo() { Log.e("ard", "中间人base:执行:ApplicationInfo getApplicationInfo()"); return that.getApplicationInfo(); } @Override public Window getWindow() { Log.e("ard", "中间人base:执行:Window getWindow()"); return that.getWindow(); } } |
3.插件
插件即普通的安卓应用。按照平常开发即可。但因为须要实现中间人规定的接口,单独运行会出问题。
AndroidManifest.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.cuiweiyou.pluginapp"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.RootApp"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".SecondActivity"/> </application> </manifest> |
3.1.入口Activity
这里继承的是中间人的BasePluginActivity,实际还是普通的Activity。但个别之处有问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import com.cuiweiyou.middleman.BasePluginActivity; public class MainActivity extends BasePluginActivity implements View.OnClickListener { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_one); findViewById(R.id.tv).setOnClickListener(this); } // 不能使用布局文件中配置android:onClick的形式。 @Override public void onClick(View v) { Log.e("ard", "插件:点击,下一个aty"); Intent i = new Intent(that, SecondActivity.class); startActivity(i); } } |
3.2.被调起Activity
继承BasePluginActivity。
1 2 3 4 5 6 7 8 9 10 11 12 |
import android.os.Bundle; import com.cuiweiyou.middleman.BasePluginActivity; public class SecondActivity extends BasePluginActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_two); } } |
4.宿主
AndroidManifest.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.cuiweiyou.rootapp"> <!-- 插件apk通常在sdcard上 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.RootApp"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- 替身aty --> <activity android:name=".GhostActivity"/> </application> </manifest> |
4.1.插件的替身
想调用插件,就调用替身。用替身管理插件的生命周期。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
import android.content.Intent; import android.content.res.Resources; import android.os.Bundle; import android.util.Log; import android.view.View; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import com.cuiweiyou.middleman.IPluginActivity; import com.cuiweiyou.middleman.PluginManager; /* * 替身Activity。通过这个aty跳转到插件的aty。 * 必须注册到宿主的Manifest。 */ public class GhostActivity extends AppCompatActivity { private IPluginActivity pluginActivity; @Override protected void onCreate(Bundle savedInstanceState) { Log.e("ard", "替身:onCreate()"); super.onCreate(savedInstanceState); String className = getIntent().getStringExtra("className"); Log.e("ard", "替身:拿到插件aty:" + className); try { Class<?> aClass = PluginManager.getInstance().getDexClassLoader().loadClass(className); Object instance = aClass.newInstance(); if (instance instanceof IPluginActivity){ pluginActivity = (IPluginActivity) instance; pluginActivity.attach(this); Bundle bundle = new Bundle(); pluginActivity.onCreate(bundle); Log.e("ard", "替身:插件aty是IPluginActivityLifecycle"); }else{ Log.e("ard", "替身:插件aty 不是 IPluginActivityLifecycle"); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } } @Override public void setContentView(int layoutResID) { Log.e("ard", "替身:super.setContentView(layoutResID)"); super.setContentView(layoutResID); // pluginActivity.setContentView(layoutResID); // 死循环 } @Override public void setContentView(View view) { Log.e("ard", "替身:super.setContentView(view)"); super.setContentView(view); // pluginActivity.setContentView(view); // 死循环 } @Override protected void onSaveInstanceState(@NonNull Bundle outState) { Log.e("ard", "替身:onSaveInstanceState(outState)"); super.onSaveInstanceState(outState); pluginActivity.onSaveInstanceState(outState); } @Override protected void onStart() { Log.e("ard", "替身:onStart()"); super.onStart(); pluginActivity.onStart(); } public void onRestart() { Log.e("ard", "替身:onRestart()"); super.onRestart(); pluginActivity.onRestart(); } public void onResume() { Log.e("ard", "替身:onResume()"); super.onResume(); pluginActivity.onResume(); } public void onPause() { Log.e("ard", "替身:onPause()"); super.onPause(); pluginActivity.onPause(); } public void onStop() { Log.e("ard", "替身:onStop()"); super.onStop(); pluginActivity.onStop(); } public void onDestroy() { Log.e("ard", "替身:onDestroy()"); super.onDestroy(); pluginActivity.onDestroy(); } @Override public ClassLoader getClassLoader() { Log.e("ard", "替身:getClassLoader()"); return PluginManager.getInstance().getDexClassLoader(); } @Override public Resources getResources() { Log.e("ard", "替身:getResources()"); return PluginManager.getInstance().getResources(); } @Override public void startActivity(Intent intent) { Log.e("ard", "替身:startActivity(i)"); String className = intent.getStringExtra("className"); Intent i = new Intent(this, GhostActivity.class); i.putExtra("className", className); super.startActivity(i); } } |
4.2.启动插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.view.View; import com.cuiweiyou.middleman.PluginManager; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } // 布局中控件点击事件 public void runPlugin(View view) { PluginManager.getInstance().setContext(this); PluginManager.getInstance().loadPluginApkFile("/sdcard/ddd.apk"); // sd卡上的插件apk。申请权限略。 String launcherActivityName = PluginManager.getInstance().getPackageInfo().activities[0].name; // 插件入口 Intent intent = new Intent(this, GhostActivity.class); // 替身 intent.putExtra("className", launcherActivityName); // 告诉替身插件的入口 startActivity(intent); // 启动替身 } } |
- end
本文由崔维友 威格灵 cuiweiyou vigiles cuiweiyou 原创,转载请注明出处:http://www.gaohaiyan.com/4330.html
承接App定制、企业web站点、办公系统软件 设计开发,外包项目,毕设