当前位置:   article > 正文

Android-Broadcast Receiver解析_broadcastreceiver scheme

broadcastreceiver scheme

Broadcast Receiver是什么

Broadcast Receiver是四大组件之一,是一种广泛运用在应用程序之间传输信息的机制,通过发送Intent来传送我们的数据。 BroadcastReceiver

Broadcast Receiver使用场景

  • 应用内多个不同组件之间的消息通信。
  • 跨应用组件之间的消息通信。

Broadcast Receiver的种类

  • 按发送顺序

    • 标准广播:也叫无序广播, 其实也就是无序广播,一起发送给所有人完全异步执行的广播,所有广播接收器几乎同时收到,效率高,无法截断。
    • 有序广播:同步执行的广播,按优先级顺序发送,同一时间只有一个广播接收,前面的广播可以截断后面的。
  • 按发送范围

    • 全局广播:发出的广播可以被其他任何的任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播,数据不安全。
    • 本地广播:只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播,其他软件不会监听到我们的广播数据。
  • 按Broadcast Receiver的实现

    • 静态注册:在AndroidManifest.xml中通过receive标签进行申明。该方式注册的广播为常驻广播,注册后,即使应用处于非运行状态,也可以接收到广播,例如很常用的开机启动监听。
    • 动态注册:在代码中注册,必须启动软件后才能接收到广播;此外,动态注册的广播接收器一定都要取消注册。跟随Activity的生命周期

Broadcast Receiver的使用

广播的使用包括以下几个步骤:

  • 自定义广播接收者:重写onReceiver()
  • 注册广播接收者到消息中心(AMS):动态(代码中registerReceiver)或者静态注册(AndroidManifest.xml中申明)
  • 定义及发送广播到消息中心(AMS):Context.sendBroadcast()、Context.sendOrderedBroadcast;
  • AMS选择并发送广播给合适的广播接受者(根据Intent-filter,Permission)
  • 广播接受者通过消息循环获取广播,并调用onReceive()进行处理

「以上1~3步骤需要开发者完成,4~5步骤由Android系统完成。」

「样例:」

4.1静态注册

  • 4.1.1自定义广播接受者,继承BroadcastReceiver基类并重写onReceive()即可:
public class SCCReceiver extends BroadcastReceiver {  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        Log.e(getClass().getName(),"SCCReceiver,传递内容:"+intent.getStringExtra("scc"));  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 4.1.2在AndroidManifest.xml中通过receive标签进行申明
<receiver  
    android:name=".SCCReceiver"  
    android:enabled="true"  
    android:exported="true"/>  
  • 1
  • 2
  • 3
  • 4
  • 4.1.3发送广播
  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:gravity="center_horizontal"  
    android:orientation="vertical">  
    <Button  
        android:id="@+id/btn_send"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="发送静态广播" />  
</LinearLayout>  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        findViewById(R.id.btn_send).setOnClickListener(this);  
    }  
    @Override  
    public void onClick(View v) {  
        switch (v.getId()) {  
            case R.id.btn_send:  
                Log.e(getClass().getName(),"点击R.id.btn_send");  
                Intent intent = new Intent(this, SCCReceiver.class);  
                intent.putExtra("scc", "真的帅");  
                sendBroadcast(intent);  
                break;  
        }  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 点击发送静态广播,实现效果如下

4.2动态广播

  • 4.2.1自定义广播接受者,继承BroadcastReceiver基类并重写onReceive()即可:
public class SCCReceiver2 extends BroadcastReceiver {  
    public static final String ACTION = "com.scc.broadcastreceiver.SCCReceiver";  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        Log.e(getClass().getName(),"SCCReceiver2,传递内容:"+intent.getStringExtra("scc"));  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 4.2.2注册广播

在代码中通过registerReceiver(BroadcastReceiver receiver, IntentFilter filter)来动态注册广播,该方法包含两个参数,receiver即我们自己定义的SCCReceiver2,IntentFilter即需要过滤的条件。

private SCCReceiver2 sccReceiver2;  
@Override  
public void onClick(View v) {  
    switch (v.getId()) {  
        case R.id.btn_reg:  
            Log.e(getClass().getName(),"点击R.id.btn_reg");  
            if (sccReceiver2 == null) {//防止重复注册  
                sccReceiver2 = new SCCReceiver2();  
                registerReceiver(sccReceiver2, new IntentFilter(SCCReceiver2.ACTION));  
            }  
            break;  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 4.2.3发送广播
  • actvitiy_main.xml
<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:gravity="center_horizontal"  
    android:orientation="vertical">  
    <Button  
        android:id="@+id/btn_reg"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="注册动态广播" />  
    <Button  
        android:id="@+id/btn_unreg"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="注销动态广播" />  
    <Button  
        android:id="@+id/btn_send_action"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="发送动态广播" />  
</LinearLayout>  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • MainActivity.java
private SCCReceiver2 sccReceiver2;  
@Override  
public void onClick(View v) {  
    switch (v.getId()) {  
        case R.id.btn_reg:  
            Log.e(getClass().getName(),"点击R.id.btn_reg");  
            if (sccReceiver2 == null) {//防止重复注册  
                sccReceiver2 = new SCCReceiver2();  
                registerReceiver(sccReceiver2, new IntentFilter(SCCReceiver2.ACTION));  
            }  
            break;  
        case R.id.btn_unreg:  
            Log.e(getClass().getName(),"点击R.id.btn_unreg");  
            if (sccReceiver2 != null) {//防止重复注销  
                unregisterReceiver(sccReceiver2);  
                sccReceiver2 = null;  
            }  
            break;  
        case R.id.btn_send_action:  
            Log.e(getClass().getName(),"点击R.id.btn_send_action");  
            Intent intentAction = new Intent(SCCReceiver2.ACTION);  
            intentAction.putExtra("scc", "动态,真的帅");  
            sendBroadcast(intentAction);  
            break;  
    }  
}  
  • 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
  • 点击注册动态>发送动态广播,实现效果如下:

  • 4.2.4注销广播

使用unregisterReceiver(BroadcastReceiver receiver)来注销注册,动态注册的广播在应用停止运行后无法接收广播,比如在MainActivity中注册,则应当在MainActivity销毁前,使用unregisterReceiver(BroadcastReceiver receiver)来注销注册

对于动态广播注意:

  • 1.有注册就必然得有注销,否则会导致内存泄露
  • 2.重复注册、重复注销也不允许

4.3无序广播

无序广播直接通过Context.sendBroadcast()来发送。上面的4.1静态广播和4.2动态广播实现都是无序广播。无序广播被发送后,BroadCastReceiver之间是无顺序,完全异步的,各个Receiver之间无关联。无序广播无法通过abortBroadcast终止,也无法使用setResult和getResult来传递处理结果。

4.4有序广播

  • 有序广播需要通过Context.sendOrderedBroadcast()来发送。有序广播发送后会按照优先级顺序被不同的广播接收器接收,优先级可以通过intent-filter的android:priority属性来设置,定义范围为-1000~1000,数值越大,优先级越高(如果优先级相同,动态注册的广播优先)。

  • 先接收的广播接收者可以对广播进行截断abortBroadcast,即后接收的广播接收者不再接收到此广播;

  • 先接收的广播接收者可以对广播进行修改,那么后接收的广播接收者将接收到被修改后的广播。

  • 4.4.1不设置android:priority点击发送有序广播运行效果:

<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    package="com.scc.broadcastreceiver">  
    <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.BroadcastReceiverDemo">  
        <activity  
            android:name=".MainActivity"  
            android:label="@string/app_name"  
            android:theme="@style/Theme.BroadcastReceiverDemo.NoActionBar">  
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />  
                <category android:name="android.intent.category.LAUNCHER" />  
            </intent-filter>  
        </activity>  
        <receiver  
            android:name=".SCCReceiver"  
            android:enabled="true"  
            android:exported="true"/>  
        <receiver  
            android:name=".SCCReceiver3">  
            <intent-filter>  
                <action android:name="com.scc.broadcastreceiver.SCCReceiver"/>  
            </intent-filter>  
        </receiver>  
        <receiver  
            android:name=".SCCReceiver4">  
            <intent-filter>  
                <action android:name="com.scc.broadcastreceiver.SCCReceiver"/>  
            </intent-filter>  
        </receiver>  
        <receiver  
            android:name=".SCCReceiver5">  
            <intent-filter>  
                <action android:name="com.scc.broadcastreceiver.SCCReceiver"/>  
            </intent-filter>  
        </receiver>  
    </application>  
</manifest>  
  • 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
case R.id.btn_send_ordered:  
    Log.e(getClass().getName(),"点击R.id.btn_send_ordered");  
    Intent intentActionOrdered = new Intent(SCCReceiver2.ACTION);  
    intentActionOrdered.putExtra("scc", "有序,真的牛");  
    sendOrderedBroadcast(intentActionOrdered,null);  
    break;  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

运行效果:

SCCReceiver3>SCCReceiver4>SCCReceiver5

  • 4.4.2设置android:priority点击发送有序广播运行效果:
<receiver  
    android:name=".SCCReceiver3">  
    <intent-filter android:priority="-100">  
        <action android:name="com.scc.broadcastreceiver.SCCReceiver"/>  
    </intent-filter>  
</receiver>  
<receiver  
    android:name=".SCCReceiver4">  
    <intent-filter android:priority="0">  
        <action android:name="com.scc.broadcastreceiver.SCCReceiver"/>  
    </intent-filter>  
</receiver>  
<receiver  
    android:name=".SCCReceiver5">  
    <intent-filter android:priority="100">  
        <action android:name="com.scc.broadcastreceiver.SCCReceiver"/>  
    </intent-filter>  
</receiver>  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

运行效果:

SCCReceiver5(100)>SCCReceiver4(0)>SCCReceiver3(-100)

  • 4.4.3修改广播拦截广播
  • 4.4.3.1 SCCReceiver5.java
public class SCCReceiver5 extends BroadcastReceiver {  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        Log.e(getClass().getName(),"SCCReceiver5,传递内容:"+intent.getStringExtra("scc"));  
        Bundle bundle = getResultExtras(true);  
        bundle.putString("scc","改了数据");  
        setResultExtras(bundle);  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 4.4.3.2 SCCReceiver4.java
public class SCCReceiver4 extends BroadcastReceiver {  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        Bundle bundle = getResultExtras(true);  
        String name = bundle.getString("scc");  
        Log.e(getClass().getName(),"SCCReceiver4,传递内容:"+name);  
        abortBroadcast();  
        Log.e(getClass().getName(),"SCCReceiver4,拦截");  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

运行效果:

SCCReceiver5(100)修改数据>SCCReceiver4(0)拦截>没有SCCReceiver3(-100)

Broadcast Receiver实现原理

在Android中,广播的出现是为了组件间的通信。其实在Android中,进程间通信有Binder,而同进程的通信方式就更多了,之所以使用广播,发送者与接受者都不需要知道对方的存在,这样带来的好处便是,系统的各个组件可以松耦合地组织在一起,这样系统就具有高度的可扩展性,容易与其它系统进行集成。

Android中的广播使用了**「观察者模式」**:基于消息发布/订阅模式的事件驱动模型。

「广播模型中包含三个角色:」

  • 消息发布者(广播发布者)
  • 消息中心(AMS,即Activity Manager Service)
  • 消息订阅者(广播接收者)

实现步骤:

  • 广播接受者通过Binder机制在AMS中注册监听
  • 广播发布者通过Binder机制向AMS发送广播
  • AMS根据发送者的需求,在已注册表中获取到合适的广播接受者(根据Intent-filter,Permission)
  • AMS将广播发送给合适的广播接受者的消息循环队列中
  • 广播接受者通过消息循环获取到广播并回调onReceive()

整个广播发送与接收过程中,发送者与接收者是异步的,发送者不需要知道是否有接受者,也不需要知道接受者何时收到广播。

LocalBroadcastManager特点

  • 本地广播只能在自身App内传播,不必担心泄漏隐私数据
  • 本地广播不允许其他App对你的App发送该广播,不必担心安全漏洞被利用
  • 本地广播比全局广播更高效

Broadcast Receiver注意事项

  • 动态注册的广播,在不需要使用时或者载体即将销毁时进行注销,即每一个registerReceiver需要有一个对应的unregisterReceiver。
  • 不要在广播接收器onReceive()中进行耗时操作,否则会引起ANR(10s)。
  • 不要在广播接收器onReceive()中开启异步任务,否则因为其生命周期的结束会出现空线程,导致任务丢失或者出现ANR等情况。
  • 耗时任务请开启service进行处理,且应当使用startService,而不应该使用bindService。
  • 应用内的广播尽量使用LocalBroadcas,因为其使用Handler,较Broadcast的Binder机制开销更小,且安全性更高。
  • 动态注册的广播优先度比静态注册高(当配置的优先级一致时),且可以控制其注册与注销,开销更小,所以能满足功能的情况下优先使用动态注册
  • 如果接受不到自己发送的广播,请注意是否是因为权限问题。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/784541
推荐阅读
相关标签
  

闽ICP备14008679号