Broadcasts

2017/3/9 7:55 上午 posted in  Android  

接收广播

在manifest文件中声明receiver

<receiver android:name=".MyBroadcastReceiver"  android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
        <action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
    </intent-filter>
</receiver>
public class MyBroadcastReceiver extends BroadcastReceiver {
    private static final String TAG = "MyBroadcastReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        StringBuilder sb = new StringBuilder();
        sb.append("Action: " + intent.getAction() + "\n");
        sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
        String log = sb.toString();
        Log.d(TAG, log);
        Toast.makeText(context, log, Toast.LENGTH_LONG).show();
    }
}

通过context注册receiver

receiver只在组件存活期间有效。
不需要receiver时要及时unregister。如在Activity中onCreate()中注册,则在onDestroy()中反注册;在onResume()中注册,则在onPause()中反注册。

BroadcastReceiver br = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
this.registerReceiver(br, filter);

对进程状态的影响

receiver所在的进程只在执行onReceive()方法时是前台进程,如果进程中没有运行其他组件,则进程在执行完onReceive()方法后很可能会被系统回收。

在onReceive()中执行异步操作,可以使用goAsync(),或者通过JobScheduler来安排一个JobService。

public class MyBroadcastReceiver extends BroadcastReceiver {
    private static final String TAG = "MyBroadcastReceiver";

    @Override
    public void onReceive(final Context context, final Intent intent) {
        final PendingResult pendingResult = goAsync();
        AsyncTask<String, Integer, String> asyncTask = new AsyncTask<String, Integer, String>() {
            @Override
            protected String doInBackground(String... params) {
                StringBuilder sb = new StringBuilder();
                sb.append("Action: " + intent.getAction() + "\n");
                sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
                Log.d(TAG, log);
                // Must call finish() so the BroadcastReceiver can be recycled.
                pendingResult.finish();
                return data;
            }
        };
        asyncTask.execute();
    }
}

发送广播

  • sendOrderedBroadcast(Intent, String)。串行有序地向receiver发送广播。
  • sendBroadcast(Intent)
  • LocalBroadcastManager.sendBroadcast

LocalBroadcastManager的主要方法:

  • registerReceiver()
  • unregisterReceiver()
  • sendBroadcast()

设置权限

发送广播时设置权限

可以设置系统已有的权限,也可以设置自定义权限(通过元素)。使用 sendBroadcast(Intent, String) 或者sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)

sendBroadcast(new Intent("com.example.NOTIFY"),
              Manifest.permission.SEND_SMS);

接收该广播时,接收方app需要设置以下权限:

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

接收广播时设置权限

只有设置了该权限的广播才能够向receiver发送广播。

<receiver android:name=".MyBroadcastReceiver"
          android:permission="android.permission.SEND_SMS">
    <intent-filter>
        <action android:name="android.intent.action.AIRPLANE_MODE"/>
    </intent-filter>
</receiver>


java
IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
registerReceiver(receiver, filter, Manifest.permission.SEND_SMS, null );

广播发送方需设置以下权限:

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

基于安全考虑的最佳实践

  1. 不需要向其他app发送广播时,使用Support Library中的LocalBroadcastManager。
  2. 优先使用context注册receiver。
  3. 限制可以接收到广播的receiver:
    • 广播设置权限;
    • Android 4.0及以上,可以对广播setPackage(String),只向相同包名的app发送广播;
    • 使用LocalBroadcastManager。
  4. 限制receiver能够接收到的广播:
    • receiver设置权限;
    • manifest中设置的receiver,可以设置android:exported为false;
    • 使用LocalBroadcastManager。
  5. Action的名称需全局唯一。
  6. 不要在onReveive()中做耗时操作,如有需要可以使用子线程或后台Service。
  7. 不要在onReceive()中start activities。

参考

Broadcasts
LocalBroadcastManager