首页 > 技术文章 > 四大组件之BroadcastReceiver基础

joahyau 2017-04-15 16:25 原文

1. 系统广播

1.1 动态注册

  (1)创建自定义接收器类继承自BroadcaseReceiver,实现onReceive()方法,对接收到的广播的逻辑处理就是写在这个函数中的。

  (2)实例化IntentFilter对象,并通过addAction()方法加入需要接收的广播值。使用系统广播时可查阅官方文档,找到需要的Action。

  (3)实例化自定义接收器类的对象,并通过registerReceiver()方法注册接收器对象。

  (4)在AndroidManifest.xml文件中,通过uses=permission标签进行权限声明。若非系统的关键性信息,则无需这一步。

  (5)使用完后,动态注册的广播接收器一定要通过unregisterReceiver()方法取消注册。

范例: 监测当前网络状态,若断网,则提示用户当前网络不可用。

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.studying.myapplication">

    <!--声明权限-->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

    <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/AppTheme">
        <activity android:name=".BroadcaseActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

活动类:

public class BroadcaseActivity extends Activity {

    private IntentFilter mFilter;
    private NetworkChangeReceiver mReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broadcase);

        //实例化IntentFilter对象,并加入Action
        mFilter = new IntentFilter();
        mFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        mReceiver = new NetworkChangeReceiver();
        //注册接收器
        registerReceiver(mReceiver, mFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //动态注册的广播接收器一定要取消注册
        unregisterReceiver(mReceiver);
    }

    class NetworkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            //获取网络状态信息
            ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();

            //当网络信息对象为空或者网络不可用时,提示用户
            if (networkInfo == null || !networkInfo.isAvailable()) {
                Toast.makeText(context, "Network is unavailable.", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

1.2 静态注册

  相对动态注册而言,静态注册接收器非常简单,只需要自定义一个接收器类,而后实现onReceive()方法,在里面实现对广播的处理,然后在AndroidManifest.xml文件中通过receiver标签进行注册即可。需要注意,无论是静态注册还是动态注册,如果需要访问系统的关键性信息,都必须在配置文件中声明权限。

范例: 静态注册实现开机自启。

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.studying.myapplication">

    <!--声明权限-->
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

    <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/AppTheme">

        <!--通过receiver标签静态注册-->
        <receiver android:name=".BootCompleteReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

        <activity android:name=".BroadcaseActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

自定义接收器类:

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

2. 自定义广播

2.1 标准广播

  自定义广播的接收方法与系统广播的一样,也有静态注册和动态注册两种方法,这里就不再赘述。

  自定义广播的不同之处在于,它是手动发送的,方法是新建一个Intent对象并加入自定义的广播值,而后通过sendBroadcase()方法发送,此时发送出去的就是一条标准广播。

范例: 发送一条自定义标准广播并接收。

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.studying.myapplication">

    <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/AppTheme">

        <!--通过receiver标签静态注册-->
        <receiver android:name=".MyReceiver" >
            <intent-filter>
                <action android:name="com.studying.MyBroadcast" />
            </intent-filter>
        </receiver>

        <activity android:name=".BroadcaseActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

自定义接收器类:

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

布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.studying.myapplication.BroadcaseActivity">

    <Button
        android:id="@+id/send"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:text="Send MyBroadcase" />
</LinearLayout>

活动类:

public class BroadcaseActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broadcase);

        Button send = (Button) findViewById(R.id.send);
        send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //实例化一个Intent对象,并发送广播
                Intent intent = new Intent("com.studying.MyBroadcast");
                sendBroadcast(intent);
            }
        });
    }
}

2.2 有序广播

  与标准广播不同之处在于,标准广播是异步执行的,所有广播接收器会在同一时刻接收到;而有序广播是同步执行的,同一时刻只会有一个接收器收到,当这个接收器里面的逻辑执行完毕后,广播才会继续传播。

  因此,有序广播有两个要点,第一,优先级高的接收器可以先收到广播,优先级高低通过priority给定;第二,前面的接收器可以截断,让后面的接收器无法收到广播,这个则是通过abortBroadcase()方法实现。

  此外,有序广播是通过sendOrderedBroadcase()方法进行发送的。

范例: 创建两个自定义广播类,并且让MyReceiver1的优先级高于MyReceiver2,而后在MyReceiver1中调用abortBroadcase()方法截断广播的传递。

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.studying.myapplication">

    <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/AppTheme">

        <!--使MyReceiver1的优先级高于MyReceiver2的-->
        <receiver android:name=".MyReceiver1" >
            <intent-filter android:priority="100">
                <action android:name="com.studying.MyBroadcast" />
            </intent-filter>
        </receiver>
        <receiver android:name=".MyReceiver2" >
            <intent-filter android:priority="50">
                <action android:name="com.studying.MyBroadcast" />
            </intent-filter>
        </receiver>

        <activity android:name=".BroadcaseActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

布局文件与标准广播的范例一样:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.studying.myapplication.BroadcaseActivity">

    <Button
        android:id="@+id/send"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:text="Send MyBroadcase" />
</LinearLayout>

活动类:

public class BroadcaseActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broadcase);

        Button send = (Button) findViewById(R.id.send);
        send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //发送有序广播
                Intent intent = new Intent("com.studying.MyBroadcast");
                sendOrderedBroadcast(intent, null);
            }
        });
    }
}

MyReceiver1:

public class MyReceiver1 extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "Received in MyReceiver1.", Toast.LENGTH_SHORT).show();
        abortBroadcast();
    }
}

MyReceiver2:

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

  若把MyReceiver1中的abortBroadcase();这一句注释掉,点击发送则可一次看到两个接收器的提示。

3. 本地广播

  本地广播只能够在应用程序内部进行传递,广播接收器也只能接收本程序发出的广播,这样的机制解决了全局广播可能出现的安全问题。

  使用本地广播非常简单,与全局广播不一样的地方在于,使用了一个LocalBroadcastManager对广播进行管理,使用这个管理器发送的广播以及注册的接收器,即为本地广播和本地广播接收器。

  另外,由于发送本地广播时,程序必定处于启动状态,因而不需要并且也没有静态注册。

范例: 发送一条本地广播并接收。

活动类:

public class BroadcaseActivity extends Activity {

    private MyReceiver mReceiver;
    private LocalBroadcastManager mManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broadcase);

        //获取本地广播管理器
        mManager = LocalBroadcastManager.getInstance(this);

        Button send = (Button) findViewById(R.id.send);
        send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //通过本地广播管理器发送广播
                Intent intent = new Intent("com.studying.MyLocalBroadcast");
                mManager.sendBroadcast(intent);
            }
        });

        //通过本地广播管理器进行动态注册
        IntentFilter mFilter = new IntentFilter();
        mFilter.addAction("com.studying.MyLocalBroadcast");
        mReceiver = new MyReceiver();
        mManager.registerReceiver(mReceiver, mFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //通过本地广播管理器取消注册
        mManager.unregisterReceiver(mReceiver);
    }

    //自定义接收器类
    class MyReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "Received.", Toast.LENGTH_SHORT).show();
        }
    }
}

  最后,在使用广播接收器时要注意,不要在onReceive()方法中加任何耗时操作,广播接收器更多的是接收某些广播讯息,从而开启其它组件。在广播接收器中是不允许开启线程的,当onReceive()方法运行了长时间仍未结束时,程序就会报错。

推荐阅读