Android手写占位式插件化框架之Activity通信、Service通信和BroadcastReceiver通信(一)

简介: Android手写占位式插件化框架之Activity通信、Service通信和BroadcastReceiver通信

前言:

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();
        }
    }
}


目录
相关文章
|
1月前
|
Android开发 开发者
Android UI设计: 请解释Activity的Theme是什么,如何更改应用程序的主题?
Android UI设计: 请解释Activity的Theme是什么,如何更改应用程序的主题?
23 1
|
1月前
|
Android开发
Android基础知识:什么是Fragment?与Activity的区别是什么?
Android基础知识:什么是Fragment?与Activity的区别是什么?
157 54
|
1月前
|
数据库 Android开发 开发者
Android基础知识:请解释Activity的生命周期。
Android基础知识:请解释Activity的生命周期。
35 2
|
2月前
|
Android开发 Kotlin
android开发,使用kotlin学习BroadcastReceiver
android开发,使用kotlin学习BroadcastReceiver
18 0
|
2月前
|
XML 安全 Java
Android Studio App开发入门之活动Activity中为活动补充附加信息讲解及实战(附源码 超详细必看)
Android Studio App开发入门之活动Activity中为活动补充附加信息讲解及实战(附源码 超详细必看)
28 0
|
2月前
|
Android开发
Android Studio App开发入门之在活动之间传递消息(附源码 超详细必看)(包括显示和隐式Intent,向上一个和下一个Activity发送数据)
Android Studio App开发入门之在活动之间传递消息(附源码 超详细必看)(包括显示和隐式Intent,向上一个和下一个Activity发送数据)
29 0
|
2月前
|
Android开发
Android Studio APP开发入门之活动Activity中启停活动页面的讲解及实战(附源码,包括Activity的启动结束、生命周期、跳转等)
Android Studio APP开发入门之活动Activity中启停活动页面的讲解及实战(附源码,包括Activity的启动结束、生命周期、跳转等)
31 0
|
3月前
|
Android开发
安卓activity管理器
安卓activity管理器
22 0
|
3月前
|
小程序 开发工具 Android开发
Donut多端框架小程序打包适配ios和安卓app
腾讯新出了一个 Donut 多端框架,可以直接将微信小程序转成 ios 和 安卓 app,小程序开发者工具里也集成了 app 相关升级、调试和打包的功能,终于可以一套代码开发出3个客户端了!
89 0
Donut多端框架小程序打包适配ios和安卓app
|
4月前
|
存储 SQL 人工智能
Android Activity启动流程一:从Intent到Activity创建
Android Activity启动流程一:从Intent到Activity创建

相关产品

  • 云迁移中心