四大组件|Broadcast的基本用法

Android中每个应用进程都可以对自己感兴趣的广播进行注册,这些广播可以来自系统,也可以来自其他应用程序。接受广播可以轻松实现跨进程或应用内异步交流,比如App开机自启、App内强制下线。

  • 广播的种类
  • 接收广播
  • 发送自定义广播
  • 使用本地广播

广播的种类

  • 标准广播:

是一种完全异步执行的广播,广播发出后,所有注册该广播的接收器几乎在同一时刻接收到该广播,没有先后顺序。因为这种广播没有先后顺序,所以它的效率比较高,但也意味着它不能被截断。

  • 有序广播:

与标准广播相反,有序广播是一种同步的广播,广播发出后,同一时刻只有一个接收器接收到广播,且该接收器可以截断广播。此时广播是有先后顺序的。

接收广播

Android中有很多系统广播,我们可以在应用中监听这些广播获得系统的状态信息。如开机后发送一条信息,网络状态更改时发送一条信息等。

  • 动态注册:

在代码中注册,也就是需要打开应用程序才能注册接受器,可用于监听网络状态的变化等。

  1. 新建一个类继承BroadcastReceiver。
  2. 重写onReceive(),在方法中实现业务逻辑。
  3. 新建一个IntentFilter并添加action,action表示监听什么广播。
  4. 新建广播类实例。
  5. onCreate()注册,onDestroy()中注销。
    以下为监听网络状态实例:
public class BroadcastActivity extends AppCompatActivity {
    private NetworkReceive receive;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broadcast);
        //第三至第五步
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        receive = new NetworkReceive();
        registerReceiver(receive, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //第五步
        unregisterReceiver(receive);
    }
    
    //第一步
    class NetworkReceive extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            //第二步,由于该广播为监听网络状态,我们还要具体判断网络是否有连接
            ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = manager.getActiveNetworkInfo();
            if (networkInfo != null && networkInfo.isConnected()){
                Toast.makeText(context, "network is available", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

//AndroidManifest.xml中申请网络权限
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  • 静态注册

虽然动态注册可以自由控制广播的注册与注销,但它一个缺点就是需要应用启动时才能接受广播。而静态注册可以在应用未启动时接收广播,可实现开机自启这一类的需求。

  1. 使用Android Studio提供的快捷方式新建Broadcast Receiver
  2. 在AndroidManifest.xml中注册广播
  3. 重写onReceive实现业务逻辑
  4. 声明需要的权限
    以下实现开机自启:
 //第一步,新建Receiver...
 //第二、四步,AndroidManiefest中
    //声明权限
     <uses-permission
     android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    
    <application
        ...
        <receiver
            android:name=".broadcast.BootCompleteReceiver"
            android:enabled="true"
            android:exported="true">
            //静态注册
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>
    </application>
    
    //第三步
public class BootCompleteReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO: This method is called when the BroadcastReceiver is receiving
        // an Intent broadcast.
        //实现业务逻辑
        Toast.makeText(context, "Boot Complete", Toast.LENGTH_SHORT).show();
    }
}

两种注册均有优缺点,应结合使用。需要注册的是,不可在onRecevie()中添加过多的逻辑或者进行任何耗时操作。 因为广播接收器是不允许开启线程的。如果onReceive()执行了较长时间而没有结束,程序就会报错。因此,广播一般是一种打开程序其他组件的角色,如创建一条通知,启动一个服务等。

发送自定义广播

  • 发送标准广播:
    //activity中
    //构造函数传入action,接受广播需要这个action
    Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
    sendBroadcast(intent);

除此之外,还可以在Intent中携带一些数据传递到广播接收器中。

  • 发送有序广播
    //与标准广播类似
    Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
    sendOrderedBroadcast(intent, null);

第二个参数null表示与权限相关的字符串,这里传null就行。
有序广播的优先顺序,在AndroidManifest.xml中对应的receiver声明 android:priority即可,数值越大,优先级越高。

    <application
        ...
        <receiver
            android:name=".broadcast.BootCompleteReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter
                android:priority="100"
                >
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>
    </application>

有序广播还可以截断广播,在接收器onReceive()中调用abortBroadcast()即可。

public class BootCompleteReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO: This method is called when the BroadcastReceiver is receiving
        // an Intent broadcast.
        //实现业务逻辑
        Toast.makeText(context, "Boot Complete", Toast.LENGTH_SHORT).show();
        abortBroadcast();
    }
}

使用本地广播

以上提到的都是属于系统全局广播,发出的广播可被系统任何一个应用接收到,这就容易引起安全性问题。因此,在本应用内传递数据,我们可以使用本地广播,本地广播其他应用无法接收,只允许本应用接收。
发送本地广播:

  1. 新建LocalBroadcastManager实例
  2. 新建Intent,与全局广播类似
  3. 通过LocalBroadcastManager发送Intent
    接受本地广播:
  4. 与全局广播类似,新建一个类继承BroadcastReceiver
  5. 添加IntentFilter
  6. 通过LocalBroadcastManager动态注册与注销
public class BroadcastActivity extends AppCompatActivity {
    private LocalReceive receive;
    
    private LocalBroadcastManager localBroadcastManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broadcast);
        
        //第一步
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        //第二、三步
        Intent intent = new Intent("com.example.learnapplication.LOCAL_BROADCAST");
        localBroadcastManager.sendBroadcast(intent);
        
        //第五、六步
        receive = new LocalReceive();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("com.example.learnapplication.LOCAL_BROADCAST");
        localBroadcastManager.registerReceiver(receive, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        localBroadcastManager.unregisterReceiver(receive);
    }

    //第四步
    class LocalReceive extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            //处理具体逻辑
            Toast.makeText(context, "receive local broadcast", Toast.LENGTH_SHORT).show();
        }
    }
}

需要强调的是,本地广播接收器不能静态注册,且发送本地广播比发送全局广播更加高效。本地广播通常用于实现本应用全局通知,如强制下线、推送等。 结合之前Activity的BaseActivity用法,可实现应用内全局接收广播并做出响应。

参考:
《第一行代码》

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容