BroadcastReceiver 基础知识

BroadcastReceiver 四大组件之一,主要用于监听手机状态的变以及不同组件和应用间的通信。

一.注册广播

1.静态注册:

  1. 在AndroidMainfest中添加receiver

     <receiver android:name=".MyReceiver">
         <intent-filter>
             <action android:name="android.intent.action.myreceiver" />
         </intent-filter>
     </receiver>
    
  2. 创建类继承BroadcastReceiver的,并且复写onReceive()方法

     public class MyReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             Log.i(TAG, "receive action ");
         }
     }
    

2. 动态注册

  1. 创建广播类和实例

  2. 创建intentFilter并且添加需要接收的action

  3. 调用registerReceiver方法注册

  4. 可以通过unregisterReceiver取消

     public class MainActivity extends Activity {
         private BroadcastReceiver mBroadcastReceiver;
     
         @Override
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             setContentView(R.layout.activity_main);
     
             mBroadcastReceiver = new MyBroadcastReceiver();
             IntentFilter intentFilter = new IntentFilter();
             intentFilter.addAction("MyIntent");
             registerReceiver(mBroadcastReceiver, intentFilter);
         }
         @Override
         protected void onDestroy() {
             super.onDestroy();
             unregisterReceiver(mBroadcastReceiver);
         }
     }
    

Tips:

  1. action的注册和解注册成对出现在context对应的生命周期中,例如onCreate和onDestroy,以及onResume和onPause
  2. 同一个receiver可以同时接收动态注册和静态注册的广播
  3. 动态广播在应用没有启动时,是无法接收到的,即使加了Intent.FLAG_INCLUDE_STOPPED_PACKAGES(测试失败)

二、广播的类型

1. 系统广播

系统广播,当手机状态发生变化时,都会发出相应的系统广播。如:网络状态,解锁等。注意:有些系统广播必须态注册才有效:SCREEN_ON,SCREEN_OFF

2. 普通广播

自定义的广播,通常用于应用内或应用间通信。

3. 有序广播

  1. 多个具当前已经注册且有效的BroadcastReceiver接收有序广播时,是按照先后顺序接收的,先后顺序判定标准为:将当前系统中所有有效的动态注册和静态注册的BroadcastReceiver按照priority属性值从大到小排序,对于具有相同的priority的动态广播和静态广播,动态广播会排在前面。

     <receiver android:name=".OrderedReceiver1">
         <intent-filter android:priority="100">
             <action android:name="com.sven.action.my.receiver.orderd.receiver" />
         </intent-filter>
     </receiver>
     <receiver android:name=".OrderedReceiver2">
         <intent-filter android:priority="1000">
             <action android:name="com.sven.action.my.receiver.orderd.receiver" />
         </intent-filter>
     </receiver>
    

当然动态注册时也可以设置优先级:

    intentFilter.setPriority(1000);
  1. 先接收的BroadcastReceiver可以对此有序广播进行截断,使后面的BroadcastReceiver不再接收到此广播,

     public class OrderedReceiver2 extends BroadcastReceiver {
         private static final String TAG = "OrderedReceiver2";
         @Override
         public void onReceive(Context context, Intent intent) {
             Log.i(TAG, "action = "+intent.getAction());
             abortBroadcast();
         }
     }
    
  2. receiver之间的通信
    优先接收到Broadcast的Receiver可通过setResultExtras(Bundle)方法将处理结果存入Broadcast中,下一个Receiver 可通过getResultExtras(true)方法获取上一个 Receiver传来的数据。

     //修改
     public class OrderedReceiver2 extends BroadcastReceiver {
         private static final String TAG = "OrderedReceiver2";
         @Override
         public void onReceive(Context context, Intent intent) {
             Log.i(TAG, "action = "+intent.getAction());
             Bundle b = new Bundle();
             b.putInt("value", 101);
             setResultExtras(b);
         }
     }
     
     //接收    
     public class OrderedReceiver1 extends BroadcastReceiver {
         private static final String TAG = "OrderedReceiver1";
         @Override
         public void onReceive(Context context, Intent intent) {
             Log.i(TAG, "action = "+intent.getAction());
             Bundle b = getResultExtras(true);
             Log.i(TAG,"value = "+b.get("value"));
         }
     }
    

三、广播和权限

1. 谁有权限接收 (和安装的顺序有关 需要先装发送者)

  1. 发送者的manifest声明权限

     <permission android:name="com.sven.permission.my.receiver.RECEIVE"/>
    
  2. 发送时添加权限

     public void sendBroadcast(View view) {
         sendBroadcast(new Intent("com.sven.action.my.receiver"),
                 "com.sven.permission.my.receiver.RECEIVE");
     }
    
  3. 接收app的manifest要添加对应的权限

     <uses-permission android:name="com.sven.permission.my.receiver.RECEIVE"/>
    

2. 谁有权限发送 (和安装顺序有关,先安装接收者)

  1. 接收者的manifest文件中声明权限

        <permission android:name="com.sven.permission.my.receiver.SEND"/>
    
  2. 接收者的receiver节点中添加

         <receiver
             android:name=".MyReceiverWithPermission"
             android:permission="com.sven.permission.my.receiver.SEND">
             <intent-filter>
             ....
             </intent-filter>
         </receiver>
    
  3. 发送者的manifest文件中使用

     <uses-permission android:name="com.sven.permission.my.receiver.SEND"/>
    

Tips:

自定义的权限最好在2个app中同时声明,(测试出现了比较奇怪的情况,声明权限的app,必须先安装,否则还是会报权限问题),使用系统权限不会不存在以上问题

四、安全高效地使用广播的一些原则:

  1. 如果不需要发送到应用外,同一个应用内的广播尽量使用LocalBroadcastManager

  2. 尽量使用动态注册的广播,而且有些系统广播,比如说 CONNECTIVITY_ACTION 在7.0之后只能通过动态注册接收

  3. 发送广播时明确广播的接受者:
    (1)发送时添加权限
    (2)通过setPackage 指定应用
    (3)使用LocalBroadcastManager

  4. 广播的命名尽量保证唯一

  5. 注册一个广播时,限制广播的接收者:

    (1)添加一个权限, mainfest 中指定,动态注册可以使用该接口添加权限

     public abstract Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler);
    

    (2)仅仅只是应用内使用时,可以使用设置 android:exported="false"。这样就不会接收应用外的广播了
    (3)使用LocalBroadcastManager

  6. onReceive方法运行在主线程中,所以不能执行耗时任务

  7. 不要通过广播启动activity,可以使用通知替代

五、onReceive
系统执行onReceive方法时,receiver会被当成前台进程,不会被杀,但是当onReceive()方法返回后,会被当成一个低优先级的程序,很容易被系统杀掉,在onReceive执行耗时异步任务时,最好通过goAsync防止执行过程中被系统杀掉。
onReceive 的context是应用的context

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(); 
}

}

Tips:

可以在通过一下这种方法让onReceive执行在handlerThread中。

private Handler handler; // Handler for the separate Thread
HandlerThread handlerThread = new HandlerThread("MyNewThread");
handlerThread.start();
// Now get the Looper from the HandlerThread so that we can create a Handler that is  attached to the HandlerThread
// NOTE: This call will block until the HandlerThread gets control and initializes its Looper
Looper looper = handlerThread.getLooper();
// Create a handler for the service
handler = new Handler(looper);
// Register the broadcast receiver to run on the separate Thread
registerReceiver (myReceiver, intentFilter, broadcastPermission, handler);

https://stackoverflow.com/questions/10682241/register-a-broadcast-receiver-from-a-service-in-a-new-thread

问题:

  1. 有哪些静态广播可以启动进程,是否有其他要求?
  2. 上文提到的发送及接收时permission的问题?
  3. BroadcastReceiver 耗时任务通过开启service执行和异步执行ge有什么应用场景?

参考资料:
https://developer.android.com/guide/components/broadcasts.html#security_considerations_and_best_practices
http://www.cnblogs.com/lwbqqyumidi/p/4168017.html

Demo:
http://download.csdn.net/detail/time_traveller14/9892205
http://download.csdn.net/detail/time_traveller14/9892207

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容