Android Broadcast Receiver 基础详解

来源:互联网 发布:mysql 字符串截取 编辑:程序博客网 时间:2024/05/17 08:54

Android中的广播机制十分灵活,因为Android中的每个应用程序都可以对自己感兴趣的广播进行注册。它提供了一套完整的API允许应用程序自由的发送和接受广播。

广播分为两类:1.标准广播,是一种完全异步执行的广播,在广播发出后,所有的接收器几乎在同一时间收到广播消息;2.有序广播,是一种同步执行的广播,在广播发出后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕了,广播才会继续传递,并且前面的广播接收器可以截断广播。

注册广播的方式有两种:1.动态注册即在代码中注册;2.静态注册即在AndroidManifest.xml中注册。



1.动态注册监听网络变化。

package company.testbroadcast;import android.app.Activity;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.os.Bundle;import android.widget.Toast;/** * Created by samyang on 2016/8/29. */public class MainActivity extends Activity {    private IntentFilter mIntentFilter;    private NetworkChangeReceiver mNetworkChangeReceiver;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mIntentFilter = new IntentFilter();        mIntentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");        mNetworkChangeReceiver = new NetworkChangeReceiver();        registerReceiver(mNetworkChangeReceiver, mIntentFilter);    }    @Override    protected void onDestroy() {        super.onDestroy();        unregisterReceiver(mNetworkChangeReceiver);    }    private class NetworkChangeReceiver extends BroadcastReceiver {        @Override        public void onReceive(Context context, Intent intent) {            Toast.makeText(context, "network changed", Toast.LENGTH_SHORT).show();        }    }}

首先建立一个NetworkChangeReceiver内部类,让它继承自BroadcastReceiver。并重写其中的onReceive方法。然后,声明并实例化mIntentFilter和mNetworkChangeReceiver,在mIntentFilter中添加一个addAction传入"android.net.conn.CONNECTIVITY_CHANGE"(当系统网络发生变化时,发出一条该值的广播),并注册接收器,传入两个参数:要动态注册的广播接收器实例,和添加了Action的IntentFilter实例。最后在onDestroy方法中取消注册

运行程序,然后按home键回到主界面,此时不能按back键,否则会调用onDestroy方法。然后在手机设置中更改网络状态,即会弹出Toast如下图所示。


此时我们只是在onReceive方法中加入了简单的逻辑。我们并不能知晓当前网络状态是变为有网络还是无网络。因此,我们更改代码,使用户准确的知晓当前网络状态。更改onReceive方法中的代码如下:

private 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 available", Toast.LENGTH_SHORT).show();        } else {            Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();        }    }}

借助getSystemService方法获取connectivityManager实例,然后通过调用connectivityManager的getActivityNetworkInfo方法获取networkInfo实例。这样我们就能够用network实例的isAvailable方法判断当前的网络状态,从而加入相应的逻辑。当然,我们还需要在AndroidManifest.xml中获取网络状态的权限。

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

运行程序,再次更改网络状态后如下图所示:




2.静态注册实现开机启动。

此时你可能发现,我们的广播必须在启动了app之后才会执行onCreate方法,这时写在其中的注册逻辑才能得到执行。但有的时候我们需要在开机后而app未启动前就能够收到广播。由于系统启动完成后会自动发出一条值为android.intent.action.BOOT_COMPLETED的广播,我们以此来做示例。

package company.testbroadcast;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.widget.Toast;/** * Created by samyang on 2016/8/29. */public class BootCompleteReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        Toast.makeText(context, "boot completed", Toast.LENGTH_SHORT).show();    }}

新建一个类,而不是内部类。

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"          package="company.testbroadcast">    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <activity android:name=".MainActivity">            <intent-filter >                <action android:name="android.intent.action.MAIN"/>                <category android:name="android.intent.category.LAUNCHER"/>            </intent-filter>        </activity>        <receiver android:name=".BootCompleteReceiver">            <intent-filter>                <action android:name="android.intent.action.BOOT_COMPLETED"/>            </intent-filter>        </receiver>    </application>    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/></manifest>

在AndroidManifest中静态注册receiver,而不是在activity的onCreate中去动态注册。当然完了之后一样要拿到RECEIVE_BOOT_COMPLETED的权限。

运行程序,然后重启系统如下图所示:




3.发送标准广播

刚才我们发送的广播都是通过接收器去接收系统的广播,接下来我们打算发送自己的定义的广播然后让指定的接收器接收。

新建一个StandardBroadcastReceiver类。

package company.testbroadcast;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.widget.Toast;/** * Created by samyang on 2016/8/29. */public class StandardBroadcastReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        Toast.makeText(context, "received standard broadcast", Toast.LENGTH_SHORT).show();    }}

在布局中添加一个按钮,然后在MainActivity中添加下面一段代码:

Button sendStandard = (Button) findViewById(R.id.send_standard_broadcast);sendStandard.setOnClickListener(new View.OnClickListener() {    @Override    public void onClick(View view) {        Intent intent = new Intent("company.testbroadcast.MY_STANDARD_BROADCAST");        sendBroadcast(intent);    }});

在AndroidManifest.xml中添加注册:

<receiver android:name=".StandardBroadcastReceiver">    <intent-filter>        <action android:name="company.testbroadcast.MY_STANDARD_BROADCAST"/>    </intent-filter></receiver>

这样当我们点击按钮时,就会发送一个传入了intent的广播,而StandardBroadcastReceiver这个接收器已经静态注册了接收值跟intent一样的广播。因此就会弹出toast提示received standard broadcast。




4.发送有序广播

在新建一个AnotherBroadcast类,在onReceive方法中写入弹出toast提示received another broadcast的逻辑。

package company.testbroadcast;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.widget.Toast;/** * Created by samyang on 2016/8/29. */public class AnotherBroadcast extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        Toast.makeText(context, "received another broadcast", Toast.LENGTH_SHORT).show();    }}

同样为其进行动态注册。

<receiver android:name=".AnotherBroadcast">    <intent-filter>        <action android:name="company.testbroadcast.MY_STANDARD_BROADCAST"/>    </intent-filter></receiver>

并将StandardBroadcastReceiver的优先级改为100。

<receiver android:name=".StandardBroadcastReceiver">    <intent-filter android:priority="100">        <action android:name="company.testbroadcast.MY_STANDARD_BROADCAST"/>    </intent-filter></receiver>

此时再次点击按钮时,就会先后弹出toast:"received standard broadcast", "received another broadcast"。说明我们通过sendOrderedBroadcast实现了发送有序广播。里面传入两个值:第一个为intent,第二个参数是一个与权限相关的字符串,这里传入null即可。



5.本地广播

此时又出现了一个新的问题,到目前为止,我们发送的都是系统性的全局广播。发送的广播不仅可以被其他任何程序收到造成混乱,还有可能被其他应用程序拦截下来从而造成安全问题。Android为了解决广播的安全性问题,引入了一套本地广播机制。主要就是通过LocalBroadcastManager来对广播进行管理

接下来我们就要通过一个例子来学习本地广播的使用。在这个例子中我们首先想要发送一条"company.testbroadcast.LOCAL_BROADCAST"的广播,在testbroadcast项目中去接收到这条广播并弹出toast,同时在新建立的testforlocalbroadcast项目中也能接收到这条广播并弹出toast。这样表示我们发出的广播是能被其他应用程序接收到的。然后更改testbroadcast中代码使用本地广播,此时再观察testforlocalbroadcast是否能收到。

首先新建了testforlocalbroadcast项目,在其中实现如下代码。

package company.testlocalbroadcast;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.widget.Toast;public class MainActivity extends AppCompatActivity {    private LocalBroadcastReceiver mLocalBroadcastReceiver;    private IntentFilter mIntentFilter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mIntentFilter = new IntentFilter();        mIntentFilter.addAction("company.testbroadcast.LOCAL_BROADCAST");        mLocalBroadcastReceiver = new LocalBroadcastReceiver();        registerReceiver(mLocalBroadcastReceiver, mIntentFilter);    }    private class LocalBroadcastReceiver extends BroadcastReceiver {        @Override        public void onReceive(Context context, Intent intent) {            Toast.makeText(context, "received local broadcast in 'company.testlocalbroadcast'", Toast.LENGTH_SHORT).show();        }    }    @Override    protected void onDestroy() {        super.onDestroy();        unregisterReceiver(mLocalBroadcastReceiver);    }}

然后修改testbroadcast项目,添加的代码后面都打了注释,我不想删除之前的功能示例可能导致代码有点乱,麻烦仔细看下定能理清。

package company.testbroadcast;import android.app.Activity;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.net.ConnectivityManager;import android.net.NetworkInfo;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.Toast;/** * Created by samyang on 2016/8/29. */public class MainActivity extends Activity {    private IntentFilter mIntentFilter;    private NetworkChangeReceiver mNetworkChangeReceiver;    private IntentFilter mlocalIntentFilter;                              //重新声明IntentFilter和LocalBroadcastReceiver    private LocalBroadcastReceiver mLocalBroadcastReceiver;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mIntentFilter = new IntentFilter();        mIntentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");        mNetworkChangeReceiver = new NetworkChangeReceiver();        registerReceiver(mNetworkChangeReceiver, mIntentFilter);        mlocalIntentFilter = new IntentFilter();                         //添加发送"company.testbroadcast.LOCAL_BROADCAST"的Action并动态注册接收器        mlocalIntentFilter.addAction("company.testbroadcast.LOCAL_BROADCAST");        mLocalBroadcastReceiver = new LocalBroadcastReceiver();        registerReceiver(mLocalBroadcastReceiver, mlocalIntentFilter);        Button sendStandard = (Button) findViewById(R.id.send_standard_broadcast);        sendStandard.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                Intent intent = new Intent("company.testbroadcast.MY_STANDARD_BROADCAST");                sendOrderedBroadcast(intent, null);            }        });        Button sendLocal = (Button) findViewById(R.id.send_local_broadcast);        //添加按钮,实现点击逻辑        sendLocal.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                Intent intent = new Intent("company.testbroadcast.LOCAL_BROADCAST");                sendBroadcast(intent);            }        });    }    @Override    protected void onDestroy() {        super.onDestroy();        unregisterReceiver(mNetworkChangeReceiver);        unregisterReceiver(mLocalBroadcastReceiver);    }    private 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 available", Toast.LENGTH_SHORT).show();            } else {                Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();            }        }    }    private class LocalBroadcastReceiver extends BroadcastReceiver {         //新建LocalBroadcastReceiver内部类        @Override        public void onReceive(Context context, Intent intent) {            Toast.makeText(context, "received local broadcast in 'company.testbroadcast'", Toast.LENGTH_SHORT).show();        }    }}

先后运行两个项目,然后切换到testbroadcast项目点击SEND LOCAL BROADCAST按钮可以得到如下图结果。


此时已经证明了我们发送的广播是能够被其他应用程序接收到的。然后我们修改testbroadcast项目,使用本地广播机制,来检验testlocalbroadcast是否还能接收到广播。

package company.testbroadcast;import android.app.Activity;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.net.ConnectivityManager;import android.net.NetworkInfo;import android.os.Bundle;import android.support.v4.content.LocalBroadcastManager;import android.view.View;import android.widget.Button;import android.widget.Toast;/** * Created by samyang on 2016/8/29. */public class MainActivity extends Activity {    private IntentFilter mIntentFilter;    private NetworkChangeReceiver mNetworkChangeReceiver;    private IntentFilter mlocalIntentFilter;    private LocalBroadcastReceiver mLocalBroadcastReceiver;    private LocalBroadcastManager localBroadcastManager;                //声明localBroadcastManager    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        localBroadcastManager = LocalBroadcastManager.getInstance(this); //获取实例        mIntentFilter = new IntentFilter();        mIntentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");        mNetworkChangeReceiver = new NetworkChangeReceiver();        registerReceiver(mNetworkChangeReceiver, mIntentFilter);        Button sendStandard = (Button) findViewById(R.id.send_standard_broadcast);        sendStandard.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                Intent intent = new Intent("company.testbroadcast.MY_STANDARD_BROADCAST");                sendOrderedBroadcast(intent, null);            }        });        Button sendLocal = (Button) findViewById(R.id.send_local_broadcast);        sendLocal.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                Intent intent = new Intent("company.testbroadcast.LOCAL_BROADCAST");                localBroadcastManager.sendBroadcast(intent);            //通过Manager来发送广播            }        });        mlocalIntentFilter = new IntentFilter();        mlocalIntentFilter.addAction("company.testbroadcast.LOCAL_BROADCAST");        mLocalBroadcastReceiver = new LocalBroadcastReceiver();        localBroadcastManager.registerReceiver(mLocalBroadcastReceiver,mlocalIntentFilter);    //通过Manager来注册广播    }    @Override    protected void onDestroy() {        super.onDestroy();        unregisterReceiver(mNetworkChangeReceiver);                                            //通过Manager来取消注册广播        localBroadcastManager.unregisterReceiver(mLocalBroadcastReceiver);    }    private 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 available", Toast.LENGTH_SHORT).show();            } else {                Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();            }        }    }    private class LocalBroadcastReceiver extends BroadcastReceiver {        @Override        public void onReceive(Context context, Intent intent) {            Toast.makeText(context, "received local broadcast in 'company.testbroadcast'", Toast.LENGTH_SHORT).show();        }    }}

本地广播主要是通过获取到一个localBroadcastManager实例然后通过该实例来进行发送广播、注册、取消注册等操作。(实现处已在上述代码中打了注释)

注意一点:刚才我在写好代码运行testbroadcast项目后,testlocalbroadcast仍能接收到我在testbroadcast中发出的本地广播。然后我将两个程序都卸载了重装,就正常了。无论如何点击SEND LOCAL BROADCAST按钮,都只会弹出"received local broadcast in 'company.testbroadcast'"这个toast。从而确认了本地广播的使用。



至此,Android四大组件中的Broadcast Receiver的常见用法已经介绍完毕了。


0 0
原创粉丝点击