Android中的广播机制十分灵活,因为Android中的每个应用程序都可以对自己感兴趣的广播进行注册。它提供了一套完整的API允许应用程序自由的发送和接受广播。
广播分为两类:1.标准广播,是一种完全异步执行的广播,在广播发出后,所有的接收器几乎在同一时间收到广播消息;2.有序广播,是一种同步执行的广播,在广播发出后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕了,广播才会继续传递,并且前面的广播接收器可以截断广播。
注册广播的方式有两种:1.动态注册即在代码中注册;2.静态注册即在AndroidManifest.xml中注册。
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中获取网络状态的权限。
android:name="android.permission.ACCESS_NETWORK_STATE"/>
运行程序,再次更改网络状态后如下图所示:
此时你可能发现,我们的广播必须在启动了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"?>xmlns:android="http://schemas.android.com/apk/res/android" package="company.testbroadcast"> android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> android:name=".MainActivity"> android:name="android.intent.action.MAIN"/> android:name="android.intent.category.LAUNCHER"/> android:name=".BootCompleteReceiver"> android:name="android.intent.action.BOOT_COMPLETED"/> android:name="android.permission.ACCESS_NETWORK_STATE"/> android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
在AndroidManifest中静态注册receiver,而不是在activity的onCreate中去动态注册。当然完了之后一样要拿到RECEIVE_BOOT_COMPLETED的权限。
运行程序,然后重启系统如下图所示:
刚才我们发送的广播都是通过接收器去接收系统的广播,接下来我们打算发送自己的定义的广播然后让指定的接收器接收。
新建一个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中添加注册:
android:name=".StandardBroadcastReceiver">
android:name="company.testbroadcast.MY_STANDARD_BROADCAST"/>
这样当我们点击按钮时,就会发送一个传入了intent的广播,而StandardBroadcastReceiver这个接收器已经静态注册了接收值跟intent一样的广播。因此就会弹出toast提示received standard broadcast。
在新建一个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(); } }
同样为其进行动态注册。
android:name=".AnotherBroadcast">
android:name="company.testbroadcast.MY_STANDARD_BROADCAST"/>
并将StandardBroadcastReceiver的优先级改为100。
android:name=".StandardBroadcastReceiver">
android:priority="100">
android:name="company.testbroadcast.MY_STANDARD_BROADCAST"/>
此时再次点击按钮时,就会先后弹出toast:"received standard broadcast", "received another broadcast"。说明我们通过sendOrderedBroadcast实现了发送有序广播。里面传入两个值:第一个为intent,第二个参数是一个与权限相关的字符串,这里传入null即可。
此时又出现了一个新的问题,到目前为止,我们发送的都是系统性的全局广播。发送的广播不仅可以被其他任何程序收到造成混乱,还有可能被其他应用程序拦截下来从而造成安全问题。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的常见用法已经介绍完毕了。