android的广播broadcast和receiver && 广播接收器优先级的深入分析

来源:互联网 发布:淘宝客户群体分析 编辑:程序博客网 时间:2024/05/16 05:51

(1)基础概念

       广播发送者:通常广播发送方就是调用Context.sendBroadcast()的程序,而广播接收者就是继承BroadcastReceiver的程序。广播发送分两种:

A,无序广播:发送方发出后,几乎同时到达多个广播接收者处,并且无法终止广播继续传播,使用Context.sendBroadcast(intent);

B,有序广播:广播接收者需要提前设置优先级,优先级高的先接收到广播,而且能终止广播(abortBroadcast());使用Context.sendOrderedBroadcast(intent);

         BroadcastReceiver有一个特性:即使应用程序不在运行,也可以用recevier接收到特定 的广播信息。比如多个短信应用可能会响应同一条信息。

(2)发送接收模型核心代码
        A,无序广播发送方:

Intent intent = new Intent();   
intent.setAction("...");   
Context.sendBroadcast(intent);  
        B,有序广播发送方:

Intent intent = new Intent();   
intent.setAction("...");   
Context.sendOrderedBroadcast(intent,null);  
        C,广播接收者核心代码:

public class Receiver extends BroadcastReceiver{   
    public void onReceive(Context context, Intent intent) {   
        Bundle bundle = intent.getExtras();   
        ...   
    }   

关于Receiver的Intent filter的方式有两种:在注册receiver时用addAction,或者在androidmenifest.xml中加上。

(3) 普通广播实例,通过按钮发送一个广播,receiver中接收到后将广播中附带的信息在log中显示。

       activity代码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public void onCreate(Bundle savedInstanceState) {  
  2.     super.onCreate(savedInstanceState);  
  3.     setContentView(R.layout.activity_main);          
  4.     Button btn =(Button)findViewById(R.id.callphone);        
  5.       
  6.     btn.setOnClickListener(new Button.OnClickListener(){  
  7.         public void onClick(View v){  
  8.             Intent intent = new Intent();  
  9.             intent.setAction("comzhang");  //intent filter的名字  
  10.             intent.putExtra("name""cheng");   //Intent中可以不用bundle,直接用“键-值”的格式带数据  
  11.             MainActivity.this.sendBroadcast(intent);  
  12.             Toast.makeText(getApplicationContext(), "send broadcast success", Toast.LENGTH_LONG).show(); //toast方式显示结果  
  13.         }  
  14.     });        
  15. }  

       receiver代码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class Receiver extends BroadcastReceiver {  
  2.   
  3.     @Override  
  4.     public void onReceive(Context context, Intent intent) {  
  5.         // TODO Auto-generated method stub  
  6.         String name = intent.getExtras().getString("name");  //从intent中取出要带的参数,getString去处键对应的值  
  7.         Log.i("zhangcheng","get :"+name);  
  8.     }  
  9. }  

       andoid menifest的添加内容:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <receiver android:name=".Receiver"      //receiver文件名  
  2.     android:exported="false">     
  3.     <intent-filter>     
  4.         <action android:name="comzhang"/>    //匹配的Intent action名    
  5.     </intent-filter>     
  6. eceiver>    

执行它就可以在LOG中看到:get :cheng
(4)补充说明
       A,在一个Service或者receiver中发送广播等Intent的话,与在activity中有差别,需要在intent中加上标志setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)。该标志表明:首先寻找是否有与要启动的activity具有相同affinity的task。若没有,则生成一个新的task,并将该activity放入其中;若有,则将该activity添加到该task的栈顶。

       B,以上实例是XML加Intent过滤广播的方式,也可以在程序中自定义广播的接收模式:

发送:

Intent startCameraIntent = new Intent("android.intent.action.HTC_START_CAMERA");
mContext.sendBroadcast(startCameraIntent);

接收:

ScreenFilter.addAction("android.intent.action.HTC_START_CAMERA")  //字符串匹配就行
registerReceiver(mScreenFilterReceiver, ScreenFilter);
....................
mScreenFilterReceiver{
    onReceiver(){
          if(intent.getAction().equals("android.intent.action.HTC_START_CAMERA"))  {
          } 
      }
}

(5) 关于有序广播的说明

        有序广播可以设定接收的优先级,并在接收后删掉这个广播传送。在XML中设定receiver的优先级:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <receiver android:name=".smsReceiver">  
  2.      <intent-filter android:priority="1000">  
  3.      <action android:name="android.provider.Telephony.SMS_RECEIVED"/>  
  4.      </intent-filter>  
  5. </receiver>  

 优先级别声明在intent-filter 元素的 android:priority 属性中,数越大优先级别越高,最大值是2147483647;优先级别也可以调用IntentFilter对象的setPriority()进行设置。
         有序广播的接收者可以终止广播Intent的传播,广播Intent的传播一旦终止,后面的接收者就无法接收到广播,使用abortBroadcast();即可。这种用法可以用在短信拦截或者位置跟踪应用中,比如收到某个特定SMS信息,处理它之后,就把广播终止,防止其他应用收到。

     总体来说:对于接收同一个广播,在相同优先级的情况下,动态注册优先级别高于静态注册。在动态注册中,最早动态注册优先级别最高;在静态注册中,最早安装的程序,静态注册优先级别最高(安装APK会解析manifest.xml,把其加入队列)。

============================================================================================================

        我们以如何抢先开机启动为例,来说明接收无序广播静态广播接收器的接收顺序。首先我们要明确两个问题:
A,接收无序广播的接收器接收到广播的顺序是有序的,
B,接收无序广播的接收器也一样可以设置优先级的。

        我们以开机时候发出的广播android.intent.action.BOOT_COMPLETED为例,这是个无序广播。如果应用想要开启自启动,那么就要监听这个广播,程序启动之前,动态广播接收器肯定是无法使用的,只能在XML中静态注册。大家都知道,第三方应用是存放在/data/app目录下,当安装完毕之后,你会找到一个文件,他的名字是以与安装的应用包名开始的,然后可能会跟着"-数字.apk",比如:com.android.test-1.apk。接收的顺序与这个名字是有关的!那么关系是怎样的呢?

(1)系统在开机的时候,会按着一个顺序解析apk
1,首先,会解析手机中的/system/framework这个目录,原生系统中,这下面就一个apk - framework-res.apk,当然各个厂商也会加入自己的内容
2,然后受到重视的文件夹按顺序分别为:
/system/app
/vendor/app
/data/app
/drm/app-private

那么每个文件夹下解析的顺序是怎样的呢?我们先只看/data/app,也就是用户安装的第三方应用的存放位置,这与下面代码返回结果的顺序是一致的

File file = new File("/data/app/");
String[] files = file.list();

也就是说,我们按顺序打印这个数组,就能知道哪个接收器会先接收到这个广播,哪个会后接收到

(2)网上的CODE  DEMO

        做了一个实验,我写了几个只有receiver的应用,把他们的包名分别设置为大家常用的、关系的应用包名

飞信:cn.com.fetion
LBE隐私卫士:com.lbe.security.lite
Handsent:com.handsent.nextsms
金山手机卫士:com.ijinshan.mguard
360手机卫士:com.qihoo360.mobilesafe
QQ手机管家:com.tencent.qqpimsecure
一个测试应用:com.example.boottest

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. File file = new File("/data/app/");  
  2. String[] files = file.list();  
  3. for (int i = 0; i < files.length; i++){  
  4.     System.out.println("/data/app/:files["+(i+1)+"]:" + files[i]); //在打印语句中(i+1)的值可以自动转成字符  
  5. }  
结果为:
/data/app/:files[8]:com.tencent.qqpimsecure-1.apk
/data/app/:files[9]:com.qihoo360.mobilesafe-1.apk
/data/app/:files[10]:com.ijinshan.mguard-1.apk
/data/app/:files[11]:cn.com.fetion-1.apk
/data/app/:files[12]:com.lbe.security.lite-1.apk
/data/app/:files[13]:com.handsent.nextsms-1.apk
/data/app/:files[14]:com.example.boottest-1.apk

如果其中一个优先级较高,比如cn.com.fetion,那么实际的接收顺序为
getPackageName:cn.com.fetion
getPackageName:com.tencent.qqpimsecure
getPackageName:com.qihoo360.mobilesafe
getPackageName:com.ijinshan.mguard
getPackageName:com.lbe.security.lite
getPackageName:com.handsent.nextsms
getPackageName:com.example.boottest
所以如果同优先级的静态接收器想先接收某个广播,就要在包名上修改已让它靠前显示。

(3)自己的CODE实验,在一个安卓应用中加入了如下几行测试

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. File file = new File("/data/app/");  
  2. if (file == null)  
  3.     return;  
  4. String[] files = file.list();  
  5. if (files == null)  
  6.     return;  
  7. for (int i = 0; i < files.length; i++){  
  8.     Log.i("zhangcheng","/data/app/:files["+String.valueOf(i+1)+"]:" + files[i]);  
  9. }  
需要注意的【1】:目录权限一定要有,除了ROOT外,我还设置了777属性,否则file和files都可能是空指针,应用运行报错。【2】LOG的输出必须全都是字符,不是字符的要手工转成字符。我的输出如下:

/data/app/:files[1]:.restore_list                 
/data/app/:files[2]:HN_Facebook.apk               
/data/app/:files[3]:HN_IndonesiaCanggih.apk       
/data/app/:files[4]:HN_KingsoftOffice.apk         
/data/app/:files[5]:HN_Messenger.apk              
/data/app/:files[6]:HN_Twitter.apk                
/data/app/:files[7]:com.qihoo.appstore-1.apk      
/data/app/:files[8]:com.qihoo360.contacts-1.apk   
/data/app/:files[9]:com.example.app-1.apk         
/data/app/:files[10]:com.example.test-2.apk       
(4)以上讲述的是静态接收器,那动态接收器的优先级呢?

        android系统在收到短信息的时候会发送广播,但是此广播是有序广播,也就是说:先接收到广播的人,如果心情不好,它就不会向后传递此广播,后面的人就不会知道有短信到来。这与无序广播不同,无序广播并不是真的没有顺序,无序广播的接收者也是排队等待广播,只不过是在传递过程中,大家必须遵守规则一直把消息传递给最后一个。

        以大家关心的接收短消息为例,想要在程序中接收短信,就要接收如下广播android.provider.Telephony.SMS_RECEIVED。系统把它作为有序广播进行发送,那么,谁第一个接收到短信将变得至关重要。

        上一节说过静态接收器的接收顺序,那么动态接收器和静态接收器相比呢?答案是静态接收器优先级低于动态接收器,也就是说,无论多么高级别的静态接收器和多么低级别的动态接收器都接收同一广播,永远都是动态接收器先接收到!同等优先级的动态接收器,先注册的先接收。

(5)有些广播必须是动态注册的receiver才能收到

         比如ACTION_SCREEN_ON,当屏幕被点亮的时候系统发送此广播,如果你尝试在manifest中注册receiver来接收,那么会失败,这是为什么呢?他们在Intent中都设置了Intent.FLAG_RECEIVER_REGISTERED_ONLY,所以如果要接收,必须动态注册广播接收器,ACTION_SCREEN_OFF也是如此。





参考原文:http://www.eoeandroid.com/thread-14095-1-1.html

参考原文:http://www.linuxidc.com/Linux/2012-07/65943.htm

参考原文:http://blog.csdn.net/prince58/article/details/6237792

参考原文:http://blog.csdn.net/ivan_software/article/details/20314563

参考原文:http://blog.csdn.net/luck_apple/article/details/7365176

参考原文:http://blog.csdn.net/su1216/article/details/8274830

参考原文:http://blog.csdn.net/su1216/article/details/8294850

0 0
原创粉丝点击