IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android插件化-Broadcast篇,安卓开发基础面试题 -> 正文阅读

[移动开发]Android插件化-Broadcast篇,安卓开发基础面试题

1、动态注册

所谓动态注册,简单来说就是我们在通过Java代码实现广播的注册,当注册这个广播的进程死亡了之后,那么该广播随之被取消注册。比如我们通过Java代码动态注册了MyReceiver广播,而该广播则可接收IntentFilter中所携带的三个Action。

 IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("com.andorid.test.ACTION");
    intentFilter.addAction("com.andorid.test.ACTION1");
    intentFilter.addAction("com.andorid.test.ACTION2");
    context.registerReceiver(new MyReceiver(), intentFilter); 

2、静态注册

&ems;所谓静态注册,简单来说就是在AndroidManifest.xml文件中对广播就进行注册。而通过该种方式注册的广播即使应用进程没有存活应用也能够接收到对应的广播,这也是实现拉活的手段之一。比如我们在AndroidManifest.xml中注册能够接收应用安装、卸载广播。

 <receiver
        android:name=".MyReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.PACKAGE_REMOVED" />
            <action android:name="android.intent.action.PACKAGE_REPLACED" />

            <data android:scheme="package" />
        </intent-filter>
    </receiver> 

对于动态注册的广播,我们直接创建了广播接收器对象并注册到框架中,因此对于插件中动态注册的广播我们并不需要做太多的处理。唯一需要处理的就是静态注册的广播接收器。

三、静态注册广播插件化处理

首先我们回忆一下在实现Activity与Service插件化之前我们都会在宿主中预埋一个对应的Activity或者Service用于欺骗AMS;对于这里的静态广播插件化处理也同样不例外需要在宿主中预埋一个Receiver,只是该Receiver的作用不再是欺骗AMS,而单纯是在接收到对应Action的广播之后再转发给插件静态注册的广播接收器。

1、插件中静态注册广播解析原理

在Android框架层中的PackageParser.java类存在一个叫做parsePackage的方法,它能够解析诸如存在在本地apk文件中的AndroidManifest.xml文件。但是该类并不直接提供给上层应用使用,所以我们需要通过反射的方式去使用该方法。方法声明如下:

public Package parsePackage(File packageFile, int flags, boolean useCaches)
        throws PackageParserException {
    Package parsed = useCaches ? getCachedResult(packageFile, flags) : null
    //其它实现
    ......
    return parsed;
} 

该方法返回的Package对象是PackageParser类的内部类。在该内部类中中存储着从AndroidManifest.xml文件中解析出来的数据,诸如:

 public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
    public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
    public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
    public final ArrayList<Service> services = new ArrayList<Service>(0); 

这里我们只需要关心存储着类对象Activity的receivers列表,注意此Activity非彼Activity,这里的Activity也是PackageParser类的一个内部类而已,其声明如下:

public final static class Activity extends Component<ActivityIntentInfo> implements Parcelable {
    public final ActivityInfo info;
    //其它实现
    ........
} 

该类又实现了Component类,该类不例外的也是PackageParser类的一个内部类,所以我们继续往上看。

public static abstract class Component<II extends IntentInfo> {
    public final ArrayList<II> intents;
    public final String className;

    public Bundle metaData;
    public Package owner;
    /** The order of this component in relation to its peers */
    public int order;

    ComponentName componentName;
    String componentShortName;
    //其他方法实现
    ......
} 

注意对于静态注册在插件AndroidManifest.xml文件中的Receiver,宿主需要知道Receiver的className以及其对应注册的Action,这样在宿主中才能实现广播的中转;因此整个解析过程也就需要解析出广播的ClassName以及其注册的Action就OK了。

对于ClassName在Component已经出现,而对于Action我们则继续看Component类中的intents这个列表,该列表中所存储的对象对应的类集成至IntentInfo,因此我们还需要继续往上看才行。

public static abstract class IntentInfo extends IntentFilter {
        public boolean hasDefault;
        // 其他属性以及方法
        ....
} 

而该内部类又继承至类IntentFilter,这里IntentFilter则不再是PackageParser类的内部类了,它就是我们在动态注册Reciver所用到的IntentFilter;而在IntentFilter类中我们则能够直接通过其mActions属性拿到当前ClasName对应Receiver所静态注册的Action了。

2、插件中静态注册广播解析实现

接下来就是通过反射的方式调用PackageParser的parsePackage获取到PackageParser$Package对象,接着就是一步一步的对该对象进行解析了。对应源码如下:

public static void parsePackage(String apkPath) {
    try {
        //根据插件本地存储的文件地址生成对应的文件
        File file = new File(apkPath);
        if (!file.exists()) {
            Log.i(TAG, "parse plugin receiver apk not exist");
            return;
        }
        Log.i(TAG, "parse package path is " + apkPath);
        //获取到PackageParser类对象
        Class<?> cls = Class.forName("android.content.pm.PackageParser");
        Object packageParserObj = RefInvoke.createObject(cls, null, null);
        if (null == packageParserObj) {
            Log.i(TAG, "parse package create packageParser object failed");
            return;
        }
        //调用parsePackage方法
        Object packageObj = RefInvoke.on(packageParserObj, "parsePackage", new Class[]{File.class, int.class})
                .invoke(file, PackageManager.GET_RECEIVERS);
        if (null == packageObj) {
            Log.i(TAG, "parse package get packageObj failed");
            return;
        }
        //获取PackageParser$Package对象中的receivers列表
        List<Object> receivers = (List<Object>) RefInvoke.getFieldValue(RefInvoke.getField(packageObj.getClass(), "receivers"), packageObj);
        if (null == receivers) {
            Log.i(TAG, "parse package get receivers failed");
            return;
        }
        //遍历receivers列表获取AndroidManifest.xml文件中所注册的Receiver信息
        for (Object receiver : receivers) {
            parseAction(receiver, apkPath);
        }
    } catch (Exception e) {
        Log.i(TAG, "parse package failed " + e);
    }
} 

上面代码就是通过反射的方式调用PackageParser的parsePackage方法,并遍历获取到的receivers列表。接下来就就是对receivers列表中的每个Receiver相关信息进行解析了。

private static void parseAction(Object receiver, String path) {
    try {
        //根据反射获取到PackageParser$Component对象中的intents列表
        Class<?> cls = RefInvoke.getClass("android.content.pm.PackageParser$Component");
        ArrayList<IntentFilter> intents = (ArrayList<IntentFilter>) RefInvoke.getFieldValue(RefInvoke.getField(cls, "intents"), receiver);
        if (null == intents || 0 == intents.size()) {
            return;
        }
        //获取该Receiver对应的ClassName
        String clsName = (String) RefInvoke.getFieldValue(RefInvoke.getField(cls, "className"), receiver);
        Log.i(TAG, "parseAction current receiver name is " + clsName);
        //根据插件所在文件地址生成对应的ClassLoader并根据获取到的ClassName生成对应的对象
        Object receiverObj = creatReceiverObj(clsName, path);
        if (null == receiverObj) {
            Log.i(TAG, "parseAction create receiver obj failed");
            return;
        }
        
        //接着就是遍历该Receiver中所注册的Action并存储在内存中以便宿主在接收到对应Action的广播之后能够直接进行转发
        for (IntentFilter intentFilter : intents) {
            Class<?> intentFilterCls = RefInvoke.getClass("android.content.IntentFilter");
#### 小福利:

在当下这个碎片化信息环境的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了

很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘

如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。

**2021大厂最新Android面试真题解析**

>![Android大厂面试真题解析](https://img-blog.csdnimg.cn/img_convert/123b098d450db407ed57ef88bd96377f.png)

**各个模块学习视频:如数据结构与算法**

**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**

![算法与数据结构资料图](https://img-blog.csdnimg.cn/img_convert/0e21499886901b1f6d1abfff26b22c2c.png)

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。
![一线互联网架构师](https://img-blog.csdnimg.cn/img_convert/a846707caef52acbe8b5d27f5ca4fcd1.png)

>![](https://img-blog.csdnimg.cn/img_convert/9b64ce0f18ae75adbb8cc3ab4017570e.png)

> 这份体系学习笔记,适应人群:**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。**第三**,到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。如果你有需要,我这里恰好有为什么,不来领取!说不定能改变你现在的状态呢!**点赞+评论即可获得!**

Tz2pvgu-1630507138161)]

> 这份体系学习笔记,适应人群:**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。**第三**,到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。如果你有需要,我这里恰好有为什么,不来领取!说不定能改变你现在的状态呢!**点赞+评论即可获得!**

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-09-02 11:30:10  更:2021-09-02 11:30:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 5:37:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码