浅谈Android四大组件之BroadcastReceiver

1、介绍

Android中定件义了很多很多的事件如,电话打出去了,电量变化了,信号变化了,应用安装了,屏幕开启了关闭了,当这些事件到来的时候;android就会发送广播如果想到监听到这些事件,就需要注册广播接收者,主要为了方便开发者进行开发。

内部实现机制:

  • 1、自定义广播接收者BroadcastReceiver,并复写onReceive()方法;
  • 2、通过Binder机制向AMS(Activity Manager Service)进行注册;
  • 3、广播发送者通过Binder机制向AMS发送广播;
  • 4、AMS查找符合相应条件(IntentFilter / Permission等)的BrocastReceiver,将广播发送到BrocastReceiver
  • (一般情况下为Activity)相应的消息循环队列中;
  • 5、消息循环队列拿到此广播,回调BrocastReceiver中的onReceiver()方法。

2、简单案列-sd卡状态监听(系统广播)

广播接收者的用法

public class SdcradReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
    //         <action android:name="android.intent.action.MEDIA_MOUNTED" />
    //           <action android:name="android.intent.action.MEDIA_UNMOUNTED" />

            //一个广播接收者  可以同时注册多个广播的接受事件
            //通过action去区分
            //获取action  通过intent
            String action = intent.getAction();
            if (action.equals("android.intent.action.MEDIA_MOUNTED")) {
                System.out.println("sd卡 安装成功");
            }
            if (action.equals("android.intent.action.MEDIA_UNMOUNTED")) {
                System.out.println("sd卡 卸载了");
            }

            Uri data = intent.getData();
            System.out.println(data);

        }

    }

清单代码:

  <receiver android:name="cn.test.sdcard.SdcradReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_MOUNTED" />
                <action android:name="android.intent.action.MEDIA_UNMOUNTED" />
                <data android:scheme="file" />
            </intent-filter>
    </receiver>

3、简单案列-无序广播(开发者自己手动发广播)

无序广播:相当于新闻联播7点钟准时开始放不能配置优先权,广播不能被中断,系统广播一般是无序广播

实现广播接收类:

public class NormalReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        String data = intent.getStringExtra("news");
        Toast.makeText(context, data, 0).show();

    }

}

清单文件注册广播接收:

      <receiver android:name="cn.test.receivenormal.NormalReceiver">
            <intent-filter >
                <action android:name="cn.test.news"/>
            </intent-filter>
        </receiver>

发送广播:

        Intent intent=new Intent();
        //为了让广播接收者  有接收到的action
        intent.setAction("cn.test.news");
        intent.putExtra("news", "新闻联播每天晚上7点准时与您相约,不见不散哦");
        sendBroadcast(intent);

4、简单案列-有序广播

有序广播:红头文件 可以配置优先权.广播是可以中断的

背景介绍:
发送一个有序广播,有多个广播接收者,分别为 CityReceiver TownReceiver FarmerReceiver 以及最终的广播接收者ResultReceiver(返回有序广播的接受情况)

申明多个广播接受着:

public class CityReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub
        String data = getResultData();
        Toast.makeText(context, "市政府收到的信息:"+data, 0).show();

        setResultData("皇上给灾民赈灾粮20万石");
    }

}
public class TownReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub
        String data = getResultData();
        Toast.makeText(context, "镇政府收到的信息:"+data, 0).show();
        setResultData("皇上给灾民赈灾粮10万石");
    }

}
public class FarmerReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub
        String data = getResultData();
        Toast.makeText(context, "灾民收到的信息:" + data, 0).show();
    }

}

再定义这三个广播接收者的清单:

<receiver android:name="cn.test.receiveorder.CityReceiver">
            //优先级 0-1000
            <intent-filter android:priority="1000"> 
                <action android:name="cn.daqing.rice"/>
            </intent-filter>
        </receiver>

<receiver android:name="cn.test.receiveorder.TownReceiver">
            <intent-filter android:priority="666">
                <action android:name="cn.daqing.rice"/>
            </intent-filter>
        </receiver>

<receiver android:name="cn.test.receiveorder.FarmerReceiver">
            <intent-filter android:priority="110">
                <action android:name="cn.daqing.rice"/>
            </intent-filter>
        </receiver>

定义最终的广播接收者,自己的广播接收者,该接受者不需要在清单文件中配置,主要由于向发送广播者反应发送的广播最终情况,这个可以根据自己发送有序广播参数选择是否添加:

public class ResultReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub
        String data = getResultData();
        Toast.makeText(context, "报告皇上灾民收到信息是:"+data, 0).show();

    }

}

发送有序广播

      Intent intent=new Intent();
        intent.setAction("cn.daqing.rice");

        ResultReceiver resultReceiver = new ResultReceiver();

        //receiverPermission  接受的权限
        //resultReceiver  结果接收者  最终的广播接收者  自己的广播接收者  不需要在清单文件中配置
        //广播被优先级高的 中断  依然可以收到广播   最后收到广播
        //scheduler handler  
        //initialData  初始化的数据
        //initialExtras  额外的数据
        // sendOrderedBroadcast(Intent intent,
         //                                 String receiverPermission,
         //                                 BroadcastReceiver resultReceiver,
         //                                 Handler scheduler,
         //                                 int initialCode,
         //                                 String initialData,
         //                                 Bundle initialExtras)
        sendOrderedBroadcast(intent, null, resultReceiver, null, RESULT_OK, "皇上给灾民赈灾粮50万石",
                null);

注意:abortBroadcast(),进行广播拦截的时候 是不能拦截最终的广播(resultReceiver )。
- 优先级相同
不同应用 谁先在手机中安装 谁先接受到信息
同一应用 谁先在清单文件中注册 谁先接收到信息
- 优先级不同
不管应用是否相同 都是 按优先级的顺序接受广播

5、动态注册广播

以上广播注册都是在清单文件中进行注册(静态注册)的,而操作非常频繁广播,比如屏幕的关闭和开启电池电量的变化不能直接在清单文件中注册
,这时需要动态的在代码中注册一般在 onCreate()方法中注册,并把receiver声明成全局变量,最后在onDestory()中注销
即:

public class MainActivity extends Activity {
    private ScreenReceiver receiver;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //代码  动态的注册广播接收者
//      /receiver  要注册  广播接收者
        //filter  过滤器
        receiver = new ScreenReceiver();
        IntentFilter filter=new IntentFilter();
        filter.addAction("android.intent.action.SCREEN_ON");
        filter.addAction("android.intent.action.SCREEN_OFF");
        registerReceiver(receiver, filter);
    }
    //当activity销毁的时候调用
    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        unregisterReceiver(receiver);
    }

}

广播接收者:

public class ScreenReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equals("android.intent.action.SCREEN_ON")) {
            System.out.println("屏幕亮了");
        }
        if (action.equals("android.intent.action.SCREEN_OFF")) {
            System.out.println("屏幕灭了");
        }
    }

}

6 LocalBroadcastManager

LocalBroadcastManager是Android Support包提供了一个工具,用于在同一个应用内的不同组件间发送Broadcast。LocalBroadcastManager也称为局部通知管理器,这种通知的好处是安全性高,效率也高,适合局部通信,可以用来代替Handler更新UI

好处:

    1、因广播数据在本应用范围内传播,你不用担心隐私数据泄露的问题。
    2、不用担心别的应用伪造广播,造成安全隐患。
    3、相比在系统内发送全局广播,它更高效。
  • LocalBroadcastManager用法

    1、LocalBroadcastManager对象的创建

    LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance( this ) ;

2、注册广播接收器

    LocalBroadcastManager.registerReceiver( broadcastReceiver , intentFilter );

3 、发送广播

     LocalBroadcastManager.sendBroadcast( intent ) ;

4、取消注册广播接收器

      LocalBroadcastManager.unregisterReceiver( broadcastReceiver );
  • LocalBroadcastManager部分源码解析
  private static LocalBroadcastManager mInstance;

    public static LocalBroadcastManager getInstance(Context context) {
        synchronized (mLock) {
            if (mInstance == null) {
                mInstance = new LocalBroadcastManager(context.getApplicationContext());
            }
            return mInstance;
        }
    }

    private LocalBroadcastManager(Context context) {
        mAppContext = context;
        mHandler = new Handler(context.getMainLooper()) {

            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_EXEC_PENDING_BROADCASTS:
                        executePendingBroadcasts();
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
        };
    } 

从这个部分源码可以看出两点:

1、在获取LocalBroadcastManager对象实例的时候,这里用了单例模式。并且把外部传进来的Context 转化成了ApplicationContext,有效的避免了当前Context的内存泄漏的问题。这一点我们在设计单例模式框架的时候是值得学习的,看源码可以学习到很多东西。

2、在LocalBroadcastManager构造函数中创建了一个Handler.可见 LocalBroadcastManager 的本质上是通过Handler机制发送和接收消息的。

3、在创建Handler的时候,用了 context.getMainLooper() , 说明这个Handler是在Android 主线程中创建的,广播接收器的接收消息的时候会在Android 主线程,所以我们决不能在广播接收器里面做耗时操作,以免阻塞UI。

  • 案例:
public class MainActivity extends AppCompatActivity {

    private LocalBroadcastManager localBroadcastManager ;
    private MyBroadcastReceiver broadcastReceiver ;
    private IntentFilter intentFilter ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //注册广播接收器
        localBroadcastManager = LocalBroadcastManager.getInstance( this ) ;
        broadcastReceiver = new MyBroadcastReceiver() ;
        intentFilter = new IntentFilter( "myaction") ;
        localBroadcastManager.registerReceiver( broadcastReceiver , intentFilter );

        //在主线程发送广播
        Intent intent = new Intent( "myaction" ) ;
        intent.putExtra( "data" , "主线程发过来的消息" ) ;
        localBroadcastManager.sendBroadcast( intent ) ;

        new Thread(new Runnable() {
            @Override
            public void run() {
                //在子线程发送广播
                Intent intent = new Intent( "myaction" ) ;
                intent.putExtra( "data" , "子线程发过来的消息" ) ;
                localBroadcastManager.sendBroadcast( intent ) ;
            }
        }).start(); ;

    }

    private class MyBroadcastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction() ;
            if ( "myaction".equals( action )){
                Log.d( "tttt 消息:" + intent.getStringExtra( "data" )  , "线程: " + Thread.currentThread().getName() ) ;
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        //取消注册广播,防止内存泄漏
        localBroadcastManager.unregisterReceiver( broadcastReceiver );
    }
}

运行结果

D/tttt 消息:主线程发过来的消息: 线程: main
D/tttt 消息:子线程发过来的消息: 线程: main

可以看出,广播接收器的onReceive方法运行在主线程。

注意事项

虽然LocalBroadcastManager也通过BroadcastReceiver来接收消息,但是他们两个之间还是有很多区别的。

  • LocalBroadcastManager注册广播只能通过代码注册的方式。传统的广播可以通过代码和xml两种方式注册。
    • LocalBroadcastManager注册广播后,一定要记得取消监听。这一步可以有效的解决内存泄漏的问题。
  • LocalBroadcastManager注册的广播,您在发送广播的时候务必使用LocalBroadcastManager.sendBroadcast(intent); 否则您接收不到广播。传统的发送广播的方法:context.sendBroadcast( intent );

7、注意

原因:随着android版本的提高,android的安全性,隐私性不断增强。

  • 4.0以上系统,在广播接收者第一安装的时候必须要有界面,没有界面,广播接收者不起作用,如果第一次安装有界面,第二次安装没有界面,起作用;
  • 2.3系统,不管安装有没有界面 广播接收者都会起作用
  • 4.0以上 如果 应用程序被 apps===>app 点击强行停止 广播接收者不在起作用
  • 2.3系统 点击强行停止以后仍然起作用

相同点: 如果进程被系统杀掉 广播接收者 在广播到来的时候依然起作用 会重启进程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值