前言:
1、什么是插件化?
能运行的宿主APP去加载没有下载的APK文件,并使用APK文件里面的功能,这就叫插件化。
2、插件化的使用场景?
很多大厂APP内会有很多功能模块,但是包体积却很小,那么就用到了插件化技术,点击某个模块后,从服务器获取对应的APK文件,并使用其内部的功能。
实现后的效果图如下:
接下来手写实现占位式插件化框架之Activity之间的通信
根据上图首先定义一个项目叫PluginProject,之后再新建一个Android Library库名为:stander,然后再定义一个插件包名为:plugin_package
项目目录如下:
一、首先在stander库中,定义一个接口名为ActivityInterface,ServiceInterface,ReceiverInterface三个接口
1.1、ActivityInterface接口
/** * @Author: ly * @Date: 2023/7/14 * @Description: 定义的Activity标准接口,需要什么方法可以再加 */ public interface ActivityInterface { /** * 把宿主(app)的环境给插件 * * @param appActivity 宿主的Activity */ void insertAppContext(Activity appActivity); void onCreate(Bundle savedInstanceState); void onStart(); void onResume(); void onPause(); void onStop(); void onDestroy(); }
1.2 ServiceInteface接口
/** * @Author: ly * @Date: 2023/7/15 * @Description: 宿主与插件间进行Service通信,标准接口 */ public interface ServiceInterface { /** * 把宿主(app)的环境给插件 * * @param service 宿主的Service */ void insertAppContext(Service service); void onCreate(); int onStartCommand(Intent intent, int flags, int startId); void onDestroy(); }
1.3、ReceiverInterface接口
/** * @Author: ly * @Date: 2023/7/15 * @Description: 宿主与插件间进行广播通信标准接口 */ public interface ReceiverInterface { void onReceive(Context context, Intent intent); }
二、在宿主APP中,定义插件管理类PluginManager
/** * @Author: ly * @Date: 2023/7/14 * @Description: 插件管理类,获取插件中的资源Resources和类加载器DexClassLoader */ public class PluginManager { private static final String TAG = PluginManager.class.getSimpleName(); private static PluginManager pluginManager; private Context context; //Activity class private DexClassLoader dexClassLoader; private Resources resources; private PluginManager(Context context) { this.context = context; } public static PluginManager getInstance(Context context) { if (pluginManager == null) { synchronized (PluginManager.class) { if (pluginManager == null) { pluginManager = new PluginManager(context); } return pluginManager; } } return pluginManager; } /** * 加载插件(2.1 Activity class, 2.2 layout) */ public void loadPlugin() { try { //getExternalFilesDir:表示应用程序的私有目录 File privateDir = context.getExternalFilesDir(null); //路径: /storage/emulated/0/Android/data/com.example.pluginproject/files Log.i(TAG, "privateDir: " + privateDir.getAbsolutePath()); File file = new File(privateDir.getAbsolutePath() + File.separator + "p.apk"); if (!file.exists()) { Log.d(TAG, "插件包,不存在"); return; } String pluginPath = file.getAbsolutePath(); //下面是加载插件里面的class //dexClassLoader 需要一个缓存目录 /data/data/当前应用的包名/pDir File fileDir = context.getDir("pDir", Context.MODE_PRIVATE); //fileDir.getAbsolutePath(): /data/user/0/com.example.pluginproject/app_pDir Log.d(TAG, "fileDir: " + fileDir.getAbsolutePath()); //pluginPath:插件文件的路径,表示插件APK文件的位置。 //fileDir.getAbsolutePath():表示应用程序的私有目录路径,作为DexClassLoader的第二个参数传递,用于指定Dex文件的输出目录。 //null:表示没有指定库(Native Library)的路径,如果插件中有依赖的库文件,可以传入库目录的路径。 //context.getClassLoader():获取应用程序的类加载器作为DexClassLoader的父类加载器。 dexClassLoader = new DexClassLoader(pluginPath, fileDir.getAbsolutePath(), null, context.getClassLoader()); //下面是加载插件里面的layout文件 //加载资源 AssetManager assetManager = AssetManager.class.newInstance(); //我们执行此方法,为了把插件包的路径添加进去 // public int addAssetPath(String path) Method method = assetManager.getClass().getMethod("addAssetPath", String.class);//类类型Class method.invoke(assetManager, pluginPath);//插件包的路径,pluginPath Resources r = context.getResources();//宿主的资源配置信息 //特殊的resource,加载插件里面的资源的resource this.resources = new Resources(assetManager, r.getDisplayMetrics(), r.getConfiguration());//参数二和参数三,配置信息 } catch (Exception e) { e.printStackTrace(); } } public ClassLoader getClassLoader() { return dexClassLoader; } public Resources getResources() { return resources; } }
2.1然后在MainActivity定义两个按钮,分别为加载插件,和启动插件里面的Activity
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 0); } } @Override protected void onStart() { super.onStart(); } @Override protected void onResume() { super.onResume(); } @Override protected void onPause() { super.onPause(); } @Override protected void onStop() { super.onStop(); } @Override protected void onDestroy() { super.onDestroy(); } /** * 加载插件 * * @param view */ public void loadPlugin(View view) { PluginManager.getInstance(this).loadPlugin(); } /** * 启动插件里面的Activity * * @param view */ public void startPluginActivity(View view) { File privateDir = getExternalFilesDir(null); File file = new File(privateDir.getAbsolutePath() + File.separator + "p.apk"); String path = file.getAbsolutePath(); File file1 = new File(path); if (!file1.exists() || file1.isFile()) { Log.i("TAG", "插件包路径无效"); } Log.i("TAG", "path: " + path); //获取插件包里面的Activity PackageManager packageManager = getPackageManager(); PackageInfo packageInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES); ActivityInfo activityInfo = packageInfo.activities[1]; //占位 代理Activity Intent intent = new Intent(this, ProxyActivity.class); // intent.putExtra("className", "com.example.plugin_package.PluginActivity"); intent.putExtra("className", activityInfo.name); startActivity(intent); } }
2.2 写代理类ProxyActivity,用代理类的上下文环境,实现插件包页面正常加载
/** * @Author: ly * @Date: 2023/7/14 * @Description: 代理的Activity,代理/占位 插件里面的Activity */ public class ProxyActivity extends Activity { private static final String TAG = "ProxyActivity"; @Override public Resources getResources() { return PluginManager.getInstance(this).getResources(); } @Override public ClassLoader getClassLoader() { return PluginManager.getInstance(this).getClassLoader(); } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); //真正的加载,插件里面的Activity String className = getIntent().getStringExtra("className"); Log.i(TAG, "className: " + className); try { Class<?> pluginActivityClass = getClassLoader().loadClass(className); //实例化插件包里面的Activity Constructor<?> constructor = pluginActivityClass.getConstructor(new Class[]{}); Object pluginActivity = constructor.newInstance(new Object[]{}); ActivityInterface activityInterface = (ActivityInterface) pluginActivity; //注入 activityInterface.insertAppContext(this); Bundle bundle = new Bundle(); bundle.putString("appName", "我是宿主传递过来的信息"); //执行插件里面的onCreate()方法 activityInterface.onCreate(bundle); } catch (Exception e) { e.printStackTrace(); } } @Override public void startActivity(Intent intent) { String className = intent.getStringExtra("className"); Intent proxyIntent = new Intent(this, ProxyActivity.class); proxyIntent.putExtra("className", className);//包名TestActivity //要给TestActivity进栈 super.startActivity(proxyIntent); } @Override public ComponentName startService(Intent service) { String className = service.getStringExtra("className"); Intent intent = new Intent(this, ProxyService.class); intent.putExtra("className", className);//ProxyService全类名 return super.startService(intent); } @Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter intentFilter) { //MyReceiver全类名 String pluginReceiverName = receiver.getClass().getName(); //在宿主app注册广播 return super.registerReceiver(new ProxyReceiver(pluginReceiverName), intentFilter); } @Override public void sendBroadcast(Intent intent) { super.sendBroadcast(intent);//发送广播到ProxyReceiver } }
2.3、ProxyService类
/** * @Author: ly * @Date: 2023/7/15 * @Description: 代理Service类,代理/占位插件中的Service */ public class ProxyService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { String className = intent.getStringExtra("className"); //com.example.plugin_package.TestService try { Class<?> testServiceClass = PluginManager.getInstance(this).getClassLoader().loadClass(className); Object testService = testServiceClass.newInstance(); ServiceInterface serviceInterface = (ServiceInterface) testService; serviceInterface.insertAppContext(this); serviceInterface.onStartCommand(intent, flags, startId); } catch (Exception e) { e.printStackTrace(); } return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); } }
ProxyService需要在AndroidManifest.xml中注册
<service android:name=".ProxyService" />
2.4、ProxyReceiver类
/** * @Author: ly * @Date: 2023/7/15 * @Description: 能接收的广播接收者, 代理/占位,插件里面的BroadcastReceiver */ public class ProxyReceiver extends BroadcastReceiver { /** * 插件里面的MyReceiver全类名 */ private String pluginReceiverName; public ProxyReceiver(String pluginReceiverName) { this.pluginReceiverName = pluginReceiverName; } @Override public void onReceive(Context context, Intent intent) { //加载插件里面的MyReceiver try { Class myReceiverClass = PluginManager.getInstance(context).getClassLoader().loadClass(pluginReceiverName); //实例化Class Object myReceiver = myReceiverClass.newInstance(); ReceiverInterface receiverInterface = (ReceiverInterface) myReceiver; receiverInterface.onReceive(context, intent); } catch (Exception e) { e.printStackTrace(); } } }