Android 四大组件 BroadcastReceiver

 

1. Broadcast简介

Broadcast用到的场景是非常多的,比如说实现在同一个App内的通信;或者不同App之间的通信;还有在特定情况下的通信(开机需要给更新App的提示等)。

Broadcast的角色主要有一个广播发送者,和需要接受到消息的广播接收者。

广播发送者先将广播发送出去,然后由广播接收者进行接收,收到广播后,就可以在广播接收器中做一些操作了。值得注意的是广播接收器是运行在UI线程的,所以不能做一些耗时操作,一般是起一个Service之类的,否则就会出现ANR。

 

2. Broadcast原理

首先,Broadcast采用的设计模式为观察者模式。

在这个模型中,主要有三个角色:广播发送者,广播接收者,消息中心(ActivityManagerService)。

Broadcast的具体流程如下:

1. 广播接收者首先在消息中心通过Binder机制注册。

2. 广播发送者就开始通过Binder机制向消息中心发送消息。

3. 然后消息中心通过匹配IntentFilter、Permission,在消息中心注册的广播接收者查中找合适的广播接收者。

4. 匹配到合适的广播接收者后,消息中心就要把广播消息发送到广播接收者相应的消息循环队列中。

5. 最后,广播接收者再通过消息循环拿到这个消息,并回调onReceive()方法。

这样,Broadcast的发送和接收操作就完成了。

 

3. 广播接收器

关于广播接收器的注册,可以分为两种类型:动态注册和静态注册。

动态注册适合使用在场景灵活,或者是特定场景下使用,因为它可以随意的注册和反注册(不使用的广播要将他注销掉);

而静态注册因为不受任何组件的生命周期影响,所以适合使用在需要时刻监听的广播的场景中。

可以根据使用场景的不同去选择不同的注册方式,下面来看具体的实现方式。

 

静态注册

首先,写一个广播接收器,并实现它的方法;在实现的onReceive()方法中,就是接受到广播之后要实现的逻辑了。

public class StaticBroadcast extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "This is StaticBroadcast", Toast.LENGTH_SHORT).show();
    }
}

接着,在AndroidManifest.xml中静态注册下该广播接收器,intent-filter标签中就是填写一些匹配条件,这样可以指定广播接收器接收广播,这里的action就是需要匹配的字符串,只要与发送方的action匹配成功(字符串完全相同),广播接收器就可以接收到发送的广播了。

        <receiver android:name=".StaticBroadcast">
            <intent-filter>
                <action android:name="INTENT.SUYICHEN.STATIC.BROADCAST"/>
            </intent-filter>
        </receiver>

最后就是发送广播,以触发这个广播接收器。

   private static final String STATIC_BROADCAST = "INTENT.SUYICHEN.STATIC.BROADCAST";

   public void BroadcastToStatic(View view) {

        Intent intent = new Intent();
        intent.setAction(STATIC_BROADCAST);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        sendBroadcast(intent);
    }

 

动态注册

接下爱就是动态注册的广播了,跟静态广播一样,首先要写的也是广播接收器,就是定义一个BroadcastReceiver,我们在接收到的地方加一个吐司。

    private BroadcastReceiver DynamicBroadcastReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "This is DynamicBroadcast", Toast.LENGTH_SHORT).show();
        }
    };

必不可少的还有匹配这个意图。

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("INTENT.SUYICHEN.DYNAMIC.BROADCAST");

好了,万事俱备,注册!

        this.registerReceiver(DynamicBroadcastReceiver,intentFilter);

不能着急,动态注册与静态注册不同的地方还在于他可以反注册,这也是他的优势之一,一定要在一个合适的地方将反注册 添加上。

        this.unregisterReceiver(DynamicBroadcastReceiver);

最后我们试着发送一个广播看看能否弹出吐司。

    private static final String DYNAMIC_BROADCAST = "INTENT.SUYICHEN.DYNAMIC.BROADCAST";

    public void BroadcastToDynamic(View view) {
        Intent intent = new Intent();
        intent.setAction(DYNAMIC_BROADCAST);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        sendBroadcast(intent);
    }

 

4. 广播的发送

广播的接收只有静态和动态两种的分别,而广播的发送方根据发送方式的不同被分为几种类型:普通广播、系统广播、有序广播、应用内广播。

分类虽然多,相似性非常大,也可以说换汤不换药把,使用最多的依旧是最简单的普通广播,下面来一一介绍下各个发送方式。

 

普通广播

普通广播非常简单,使用广,可以说是码农必会了。细心的小伙伴可能已经发现,我在之前写的静态和动态接收器的时候用到的就是普通广播,比如前面的这个:

        Intent intent = new Intent();
        intent.setAction(STATIC_BROADCAST);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        sendBroadcast(intent);

 

系统广播

系统广播无非就是系统已经写好了的广播,他会在特定的情况下去发送,我们只需写一个关闭接收器,将action字符串匹配上,就可以实现接收。与普通广播一样,只不过写发送方代码的人不同而已。

 

有序广播

关于有序广播,我们一起来做一个实验,分别创建4个广播接收器,让他们来依次接收到广播,但是,我们在第三个广播接收器中调用 abortBroadcast() 方法来进行广播的拦截,看是否能拦住广播不往下传递,也就是说第四个广播接收器不到广播。

首先创建四个广播接收器,并在第三个接收器的时候abort住:

public class OrderedBroadcastOne extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e("OrderedBroadcast",this.getClass().getName()+"already received");
    }
}
public class OrderedBroadcastTwo extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e("OrderedBroadcast",this.getClass().getName()+"already received");
    }
}
public class OrderedBroadcastThree extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e("OrderedBroadcast",this.getClass().getName()+"already received");
        abortBroadcast();//Intercept
    }
}
public class OrderedBroadcastFour extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e("OrderedBroadcast",this.getClass().getName()+"already received");
    }
}

其中,在AndroidManifest中,有个priority属性是设置广播接收器的级别,这个值的取值范围是[-1000,1000],取值越大,优先级越高。这样就可以用他来控制四个广播的接收顺序了。

        <receiver android:name=".OrderedBroadcastOne">
            <intent-filter android:priority="1000">
                <action android:name="INTENT.SUYICHEN.ORDERED"/>
            </intent-filter>
        </receiver>

        <receiver android:name=".OrderedBroadcastTwo">
            <intent-filter android:priority="500">
                <action android:name="INTENT.SUYICHEN.ORDERED"/>
            </intent-filter>
        </receiver>

        <receiver android:name=".OrderedBroadcastThree">
            <intent-filter android:priority="0">
                <action android:name="INTENT.SUYICHEN.ORDERED"/>
            </intent-filter>
        </receiver>

        <receiver android:name=".OrderedBroadcastFour">
            <intent-filter android:priority="-1000">
                <action android:name="INTENT.SUYICHEN.ORDERED"/>
            </intent-filter>
        </receiver>

接下来就要发送一个有序广播,来验证一下结果。

    public void BroadcastToOrdered(View view) {
        Intent intent = new Intent();
        intent.setAction(ORDERED_BROADCAST);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        sendOrderedBroadcast(intent,null);
    }

打印如下:

打印显示广播依次接收到了,但到了第三个广播因为执行了abort(),广播就被吞掉,第四个广播自然不会接收到。

 

应用内广播

虽然都是广播,但应用内的广播和其他广播的具体实现确大不相同。

一般的广播的实现原理都是将广播注册在消息中心(ActivityManagerService)里,如果消息中心的工作很多,消息中心繁忙,那么最直观的现象就是导致我们接收广播的速度大幅下降。

如果是广播只会在自己应用(一个进程)内使用,那么完全可以使用LocalBroadcastManager实现,LocalBroadcastManager是采用的是Handler的消息机制来处理的广播,注册到系统中的是通过Binder机制实现的,所以速度要快许多。

接下来就简单介绍下具体使用把,原理实现等用一个篇幅来讲解。

首先创建一个自定义的广播接收器类。

public class LocalReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e("LocalReceiver","LocalReceiver is accept");
    }
}

然后,在需要注册的地方获取他。

    LocalReceiver mLocalReceiver = new LocalReceiver();

紧接着,注册和注销这个广播。

    private static final String LOCAL_BROADCAST = "INTENT.SUYICHEN.BROADCAST.LOCAL";

    LocalBroadcastManager.getInstance(this).registerReceiver(mLocalReceiver,new IntentFilter(LOCAL_BROADCAST));

    LocalBroadcastManager.getInstance(this).unregisterReceiver(mLocalReceiver);

最后就是发送一个广播,看能否接收到了。

    public void LocalBroadcast(View view) {
        LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(LOCAL_BROADCAST));
    }

不出所料,广播收到了。

 

5. 一个广播接收器对应多个广播

如果说需要一个广播接收器同时接收多个广播,然后根据接收到的广播而做不同的处理,那怎么办呢?

有几个对应的广播,然后对应的写上几个广播接收器,这种方法是可以实现的,如果需要接收的广播多了,也就是对应的Action多了,也难免让代码看起来臃肿,解决这个也是非常简单,只是用广播接收器里传来的intent获取到当前是接受到的哪个广播,然后做一下判断,根据不同的广播做不同的处理就可以了。

由于这个仅为补充,比较简单,就直接上代码了。

广播接收器:

public class DoubleActionBroadcast extends BroadcastReceiver {

    private static final String DOUBLE_ACTION_ONE = "INTENT.SUYICHEN.DOUBLE.ACTION.ONE";
    private static final String DOUBLE_ACTION_TWO = "INTENT.SUYICHEN.DOUBLE.ACTION.TWO";

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if(action.equals(DOUBLE_ACTION_ONE)){
            Log.e(this.getClass().getName(),"accept to"+DOUBLE_ACTION_ONE);
        }

        if(action.equals(DOUBLE_ACTION_TWO)){
            Log.e(this.getClass().getName(),"accept to"+DOUBLE_ACTION_TWO);
        }
    }
}

注册广播接收器:

        <receiver android:name=".DoubleActionBroadcast">
            <intent-filter>
                <action android:name="INTENT.SUYICHEN.DOUBLE.ACTION.ONE"/>
                <action android:name="INTENT.SUYICHEN.DOUBLE.ACTION.TWO"/>
            </intent-filter>
        </receiver>

分别携带不同的Action,发送两个广播。

    public void DoubleBroadcast1(View view) {
        Intent intent = new Intent();
        intent.setAction(DOUBLE_ACTION_ONE);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        sendBroadcast(intent);
    }

    public void DoubleBroadcast2(View view) {
        Intent intent = new Intent();
        intent.setAction(DOUBLE_ACTION_TWO);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        sendBroadcast(intent);
    }

 

6. GitHub

https://github.com/suyichen/Blog-Broadcast

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值