Android广播机制——初体验

写在前面的话

文章为笔者自己理解

如有错误,欢迎指正


    《第一行代码》读书笔记

                 写于2017年10月16日17:00:40


(一). 安卓广播的分类

a.标准广播

    标准广播发出以后,所有的广播接收器,可以几乎在同一时刻同时接受到这条广播。

    优点:效率高

    缺点:不能被截断。

b.有序广播

    有序广播发出以后,同一时刻只能有一个广播接收器收到这条广播。优先级高的广播先接受到这条

广播。在当前广播接收器处理完自己的逻辑以后,可以执行两种动作:

    1.继续传递广播

    2.将广播截断

(二). 注册广播的方式

a. 动态注册

在代码中注册的广播,被称为动态注册。动态注册的广播,最后必须取消注册。这类广播,只有应用启

动了,才能接收到广播。

动态注册广播需要的东西

  1. 一个广播接收器类 —— 实质就是一个继承自BoradCastReceiver的类,只要继承这个类,就

    具体接收广播的能力了,但是能接受什么广播由下面的第三条决定。

  2. 重写父类的onReceive()方法 —— 接收到广播的时候,就会回调这个方法。因此,广播接收

    器的处理逻辑就写在这里

  3. 一个 IntentFilter 对象,广播接收器接收什么样的广播,由它的addAction()方法决定。

  4. 在代码中注册广播接收器,通过registerReceiver方法。方法接受两个参数,一个是广播接收

    实例,一个是IntentFilter实例。

  5. 取消注册广播 ,通过unregisterReceiver() 方法—— 在哪里取消注册无所谓,只要保证取

    消注册就OK

  6. 如果需要权限,则在AndroidManifest.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:orientation="vertical"
    >

    <Button
        android:text="检测网络"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:id="@+id/btn_isNetConnect" />

    <TextView
        android:textSize="30sp"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:id="@+id/txt_show" />


</LinearLayout>

这里写图片描述

我们写一个简单的判断当前网络是否可用的广播。监听网络是一个敏感的动作,因此,需要在xml中申明权限。
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>

java 代码

public class MainActivity extends AppCompatActivity {

//     决定广播接收器接收什么广播
    private IntentFilter intentFilter ;

    private NetworkChangeReceiver networkChangeReceiver ;
    private TextView txt_show ;
    private Button btn_isNetConnect ;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

//        获取控件实例
        txt_show = (TextView) findViewById(R.id.txt_show);
        btn_isNetConnect = (Button) findViewById(R.id.btn_isNetConnect);
//        在按钮监听器中注册广播
        btn_isNetConnect.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                intentFilter = new IntentFilter() ;
//       字符串 android.net.conn.CONNECTIVITY_CHANGE
//       表明广播接收器接收网络变化的广播
                intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
//        网络变化广播接收器实例
                networkChangeReceiver = new NetworkChangeReceiver() ;
//        注册广播
//        别忘了,申明权限
                registerReceiver(networkChangeReceiver,intentFilter) ;
            }
        });
    }

    /**
     * 动态注册的广播,必须在取消注册。
     * 我们在create(),方法创建的时候,注册了广播
     * 这里,我们在销毁活动中,取消注册,
     * 当然也可以在onPause()方法取消注册。
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(networkChangeReceiver);
    }

    /**
     * 用内部类实现一个网络变化广播接收器
     */
    class NetworkChangeReceiver extends BroadcastReceiver {

//        重写父类的onReceive()方法,接收到对应的广播的时候,就会回调这个方法
//        广播接收器的处理逻辑,就写此方法中
        @Override
        public void onReceive(Context context, Intent intent) {
//            获取网络管理服务类,就跟findViewById()方法一样
            ConnectivityManager connectivityManager =
                    (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
//            记住这样写。就好了。用到的时候,百度一下,边边角角,谁也记不清
            NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo() ;
//          判断网络状态
            if(networkInfo != null && networkInfo.isAvailable()){
                txt_show.setText("当前网络可用");
            }else {
                txt_show.setText("当前网络不可用");
            }

        }
    }
}

b.静态注册

在AndroidManifest.xml文件中注册的广播。这类广播,常驻广播,在应用未启动的时候,即可接收

到广播,处理相应的逻辑。

静态注册的广播的广播接收器中不能有UI组件。

静态广播需要的东西

相比于动态注册的广播,静态广播需要的东西,就很少了。
  1. 依然需要一个广播接收器类

  2. 继续需要重写父类的onReceive()方法

  3. 在AndroidManifest.xml文件中注册广播

  4. 如果需要权限,则在AndroidManifest.xml文件申明权限


talk is cheap,show me code ! Or shut up

代码:

/**
 * 这里写将广播写为内部类
 * 目的;记住一个知识点:
 * 静态注册的广播,广播接收器为内部类的时候,必须是pubic static

 * 实现开机自启动
 * Created by MaiBenBen on 2017/10/16.
 */
public class Outter {

// 必须是public static
    public static class BootCompleteReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
//        调启主活动,来打开应用程序
//        context参数,就是此时的上下文
            Intent intent1 = new Intent(context,MainActivity.class);
            context.startActivity(intent1);
        }
    }

}


<!--
在AndroidManifest.xml文件中注册广播
内部类广播接收器的名字,写全了
-->
 <receiver android:name=".Outter$BootCompleteReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
            <!--写明广播接收器接收的广播-->
                <action android:name="android.intent.action.BOOT_COMPLETED"></action>
            </intent-filter>
        </receiver>

监听开机广播 权限申明:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"></uses-permission>

至此,已经成功完成了开机自启动。


(三). 发送广播

总是接受广播,也没劲。下面,我们自己来发送一些广播。发送的广播是自定义的。

a. 发送标准广播

广播是使用Intent,进行传递的。因此,我们可以在Intent中携带一些数据传递给广播接收器。

注意:这里只是在活动中发送广播,在服务或者广播中发送广播,代码有所不同:需要在intent中加上

标志。
setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)。
该标志表明:首先寻找是否有与要启动的activity具有相同affinity的task。若没有,则生成一个新

的task,并将该activity放入其中;若有,则将该activity添加到该task的栈顶。
//        自定义广播内容
       String broadcast = "allbet.broad.cn.broadcasttest.MY_BROADCAST" ;
//        绑定到Intent中
       Intent intent = new Intent(broadcast) ;
//        发送广播,调用Context的sendBroadcast()方法
       sendBroadcast(intent);

b. 发送有序广播

有序广播与标准广播的发送方式,在代码中只有一句代码有差别,就是发送广播的方法不同。
//         自定义广播内容
      String broadcast = "allbet.broad.cn.broadcasttest.MY_BROADCAST" ;
//         绑定到Intent中
      Intent intent = new Intent(broadcast) ;
//         发送有序广播
//         第一个参数依然是Intent
//         第二个参数是代表权限的字符串,这里写null就好了
       sendOrderedBroadcast(intent,null);

有序广播发送完毕以后,优先级高的广播接收器会先收到

个广播。它们甚至可以将广播截,下面我们讲一下优先级的

问题


<☆> 广播接收器优先级

1.) 静态广播的优先级
  1. 跟目录的顺序有关,如下所示,目录越靠前,优先级越高,其中,第三方APP都在data/app

    录下:

      system/framework
      
      system/app

      vendor/app

      data/app

      drm/app-private

  2. 当处于同一目录下时:按照file.list()的返回顺序。因为在data/app下的应用都是用户安装的,

    并且都是以com.xxx.xxx-1.apk 的形式出现,它们的顺序,就是包名的顺序,因此,想要你的

    应用的静态广播优先级高,就需要好好研究包名了,让你的应用包名靠前。但是这个顺序不是

    绝对的,还依赖被扫描的先后顺序,先扫描的当然优先级高一点啦。即同一优先级的静态广播

    接收器,先扫描的优先级高。一般都是包名排在前面的先被扫描。但是有人也说,是按照应用

    安装的先后顺序。。。。有待考证

  3. 上面说的都是静态注册的时候,使用了相同优先级的情况。设置了优先级的,优先级越高,越

    先接收到广播。


2.) 动态广播的优先级
  1. 一大影响因素,动态广播的注册时间。同一优先级的动态广播,先注册的优先级高。

  2. 动态注册的广播接收器的优先级永远高于静态注册的广播接收器。


综上所述,做个总结:

  • 其实当接收广播的时候,广播接收器都会排队,无论是标准广播,还是有序广播,都是要排队

    的,只不过,有序广播可能会被截断。

  • 因此我们可以将,所有能接收该广播的广播接收器,理解为排队等着广播传递,排在前面的

    人,当然就先接收到广播啦。那么谁排在前面呢?毫无疑问,一定是动态注册的广播,动态注

    册的广播接收器们根据优先级排队,相同优先级的,就按照被扫描的先后顺序排队。排在队伍

    后面的就只剩下静态注册的广播接收器了,也先根据优先级排序,相同优先级的,按照被扫描

    的先后顺序排队。

  • 上述的情况是,所有的广播接收器都不缺席的情况,但是总有广播接收器,由于种种原因(比

    如,动态注册的广播,应用未启用)会缺席,缺席的话,那后面的广播接收器就自动补全空

    位。


(四).为广播接收器设定优先级

a.为静态广播接收器设定优先级

在xml文件中设定
 <receiver android:name=".Outter$BootCompleteReceiver"
            android:enabled="true"
            android:exported="true">
            <!--在这里添加属性-->
            <intent-filter android:priority="100">
                <action android:name="android.intent.action.BOOT_COMPLETED"></action>
            </intent-filter>
        </receiver>

b.为动态广播设置优先级

在intentFilter对象上设置
        intentFilter = new IntentFilter() ;
//       字符串 android.net.conn.CONNECTIVITY_CHANGE
//       表明广播接收器接收网络变化的广播
                intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
//         设置优先级
                intentFilter.setPriority(1000);
//        网络变化广播接收器实例
                networkChangeReceiver = new NetworkChangeReceiver() ;
//        注册广播
//        别忘了,申明权限
                registerReceiver(networkChangeReceiver,intentFilter) ;

(五).截断有序广播

有序广播在传送中,是可以被截断,甚至可以修改广播携带的数据。

截断广播,就是在广播接收器的onReceive()方法中调用abortBroadcast();
public void onReceive(Context context, Intent intent) {
/*
    处理逻辑
*/
//     逻辑处理结束,截断广播。
    abortBroadcast();
}

(六) 本地广播

之前我们发送的广播,都是系统全局广播,即我们发送的广播,能被系统的其他程序也接收到。这样是

很不安全的,因为,广播上说不定会携带着关键数据。或者,其他程序,不停的向我们发生垃圾广播。

本地广播,是无法再通过静态注册的广播接收器收到的。很好理解,静态注册,是为了程序未启动的

时候接收广播,而本地广播,程序已然启动可了。当然,也不能注册本地广播。

因此,我们需要本地广播,顾名思义,广播,只能在本程序中发送和接收。


a.本地广播需要的东西

  1. 对比全局广播,本地广播需要一个LocalBroadcastManger对象,来对广播进行管理。

//       获取本地广播管理 实例
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
 intentFilter = new IntentFilter("com.yaz.QQQ") ;
        myLocalBr  = new MyLocalBr() ;
        localBroadcastManager.registerReceiver(myLocalBr,intentFilter);
        break;

这里需要注意的事:本地广播是不能静态注册,主要是为了别的程序申明和intent-filter一样的广播。

last but not least:一些小技巧

  1. 如果程序需要在某个时候,接受到一个广播,这个广播中需要弹出一些UI组件。
  2. 由于时间的不确定性,有可能在任何一个活动上,接收到这个广播。
  3. 因此,第一反应,就是注册静态广播,毕竟,动态广播,跟随活动。
  4. 但是,由于有UI组件需要弹出,静态广播是不能注册了。
  5. 完犊子,只有为活动一个一个注册广播接收器了。
  6. 上述方法,多呆哦!

        思路:为了解决这个问题,我们可以写一个活动基类,让它继承活动的上帝类Activity;再让其他的活动类继承这个活动基类。聪明的你,可能已经想到了java的继承特性了。对!我们,就在这个活动基类中的onResume()方法中注册广播,这样后面所有活动类,启动完毕以后,处于栈顶的是,就会执行这个方法,完成广播的注册。那么广播的取消注册呢?当然同样放在活动基类中,至于放在哪一个方法中呢?我们放在,onStop()方法中,方法不在栈顶,就取消注册。这个方法,视情况而定。
    
        就这样,我们完成了动态广播的重复注册。
    

Allbet

写于2017年10月17日20:23:14

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值