【第一行代码】Android的广播机制

时间:2021-03-13 19:00:26

Android的广播机制

一、广播的类型

         标准广播:完全异步执行的广播,发出后所有广播接收器都同一时刻接收到这条广播信息,没有先后顺序,效率较高,但也无法截断。

        有序广播:同步执行的广播,发出后同一时刻只有一个广播接收器能够收到这条广播消息,当这个接收器的逻辑执行完毕后才会继续传递,所以接收器是有先后顺序的,优先级高的可以先收到并且可以截断广播。

 

二、广播接收器

        广播接收器可以*地对自己感兴趣的广播进行注册,这样当有相应的广播发出时,广播接收器就能够收到该广播,并在内部处理相应的逻辑。注册广播的方式一般有两种,在代码中注册和在AndroidManifest.xml中注册,其中前者也被称为动态注册,后者也被称为静态注册。新建一个类,让它继承自BroadcastReceiver,并重写父类的onReceive()方法就行了。这样当有广播到来时,onReceive()方法就会得到执行,具体的逻辑就可以在这个方法中处理。 

 

三、接收系统广播

        可以通过监听Android内置的系统级别的广播来得到各种系统状态信息。

        1.动态注册监听网络变化

       

@Override 

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

intentFilter = new IntentFilter();

//当网络状态发生变化时,系统发出的正是一条值为android.net.conn.CONNECTIVITY_ CHANGE的广播,也就是说我们的广播接收器想要监听什么广播,就在这里添加相应的action就行了

intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");

networkChangeReceiver = new NetworkChangeReceiver();

//动态注册广播接收器

registerReceiver(networkChangeReceiver, intentFilter);

}



class NetworkChangeReceiver extends BroadcastReceiver {

@Override

public void onReceive(Context context, Intent intent) {

//ConnectivityManager是个系统服务类,专门用于管理网络连接的

ConnectivityManager connectionManager = (ConnectivityManager)

getSystemService(Context.CONNECTIVITY_SERVICE);

//查询系统的网络状态需要声明权限<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

NetworkInfo networkInfo = connectionManager.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();

}

}

}

@Override

protected void onDestroy() {

super.onDestroy();

//动态注册的广播接收器一定都要取消注册

unregisterReceiver(networkChangeReceiver);

}


        2.静态注册实现开机启动

        使用静态注册让程序在未启动的情况下就能接收到广播

public class BootCompleteReceiver extends BroadcastReceiver { 

@Override

public void onReceive(Context context, Intent intent) {

Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();

}

}


监听系统开机广播也是需要声明权限的

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

<!-- 由于Android系统启动完成后会发出一条值为android.intent.action.BOOT_COMPLETED的广播,因此我们在这里添加了相应的action。 -->

<receiver android:name=".BootCompleteReceiver" >

<intent-filter>

<action android:name="android.intent.action.BOOT_COMPLETED" />

</intent-filter>

</receiver>


        注意:不要在onReceive()方法中添加过多的逻辑或者进行任何的耗时操作,因为在广播接收器中是不允许开启线程的,当onReceive()方法运行了较长时间而没有结束时,程序就会报错。因此广播接收器更多的是扮演一种打开程序其他组件的角色,比如创建一条状态栏通知,或者启动一个服务等

 

四、发送自定义广播

        1.自定义接收器和注册(其他程序接收同理)

public class MyBroadcastReceiver extends BroadcastReceiver { 

@Override

public void onReceive(Context context, Intent intent) {

Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();

}

}


<receiver android:name=".MyBroadcastReceiver"> 

<intent-filter>

<action android:name="com.example.broadcasttest. MY_BROADCAST"/>

</intent-filter>

</receiver>


        2.修改按钮的点击事件发送自定义广播

public void onClick(View v) { 

Intent intent = new Intent("com.example.broadcasttest. MY_BROADCAST");

sendBroadcast(intent);

}


        五、发送有序广播

        方法:

sendOrderedBroadcast(intent, null); //第二个参数是一个与权限相关的字符串

        优先级设置:

<intent-filter android:priority="100" > 

<action android:name="com.example.broadcasttest.MY_BROADCAST"/>

</intent-filter>


        截断广播:

abortBroadcast(); 

 

六、使用本地广播

        使用本地广播机制发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播。本地广播无法静态注册。

        优势:

        1. 可以明确地知道正在发送的广播不会离开我们的程序,因此不需要担心机密数据泄漏的问题。 

        2. 其他的程序无法将广播发送到我们程序的内部,因此不需要担心会有安全漏洞的隐患。 

        3. 发送本地广播比起发送系统全局广播将会更加高效。 

 

private LocalReceiver localReceiver; 

private LocalBroadcastManager localBroadcastManager;

localBroadcastManager = LocalBroadcastManager.getInstance(this); // 获取实例

localBroadcastManager.sendBroadcast(intent); // 发送本地广播

intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");

localReceiver = new LocalReceiver();

localBroadcastManager.registerReceiver(localReceiver, intentFilter); // 注册本地广播监听器

记得在onDestroy()里localBroadcastManager.unregisterReceiver(localReceiver);

class LocalReceiver extends BroadcastReceiver {

@Override

public void onReceive(Context context, Intent intent) {

Toast.makeText(context, "received local broadcast", Toast.LENGTH_SHORT).show();

}

}


七、实践——强制下线

        实现思路:用ActivityController管理全部活动。在需要强制下线的时候发送一个广播,在自定义的接收器的onReceive()中显示对话框,确认后调用ActivityController的finishAll()方法关闭所有活动并回到登录界面。

ActivityController代码:

package com.linh.forceoffline;

import java.util.ArrayList;

import java.util.List;

import android.app.Activity;

public class ActivityCollector {

public static List<Activity> activities = new ArrayList<Activity>();

public static void addActivity(Activity activity) {

activities.add(activity);

}

public static void removeActivity(Activity activity) {

activities.remove(activity);

}

//遍历activities中每个activity,关闭他们

public static void finishAll()

{

for(Activity activity:activities)

{

if(!activity.isFinishing())

{

activity.finish();

}

}

}



}


BaseActivity作为所有activity的父类:

package com.linh.forceoffline;



import android.app.Activity;

import android.os.Bundle;



public class BaseActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

ActivityCollector.addActivity(this);

}

@Override

protected void onDestroy() {

super.onDestroy();

ActivityCollector.removeActivity(this);

}

}


LoginActivity实现简单的登录功能,登录后进入MainActivity

MainActivity中点击按钮则发送一条广播。

自定义广播接收器ForceOfflineReceiver 

package com.linh.forceoffline;

import android.app.AlertDialog;

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.DialogInterface;

import android.content.DialogInterface.OnClickListener;

import android.content.Intent;

import android.view.WindowManager;

public class ForceOfflineReceiver extends BroadcastReceiver {

@Override

public void onReceive(final Context context, Intent intent) {

//构建一个对话框

AlertDialog.Builder dialogBuilder=new AlertDialog.Builder(context);

dialogBuilder.setTitle("警告");

dialogBuilder.setMessage("你被强制下线,请重新登录");

//一定要不可取消

dialogBuilder.setCancelable(false);

//注册确认按钮

dialogBuilder.setPositiveButton("确认", new OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int which) {

ActivityCollector.finishAll();//销毁所有活动

Intent intent=new Intent(context,LoginActivity.class);

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

//重新启动LoginActivity

context.startActivity(intent);

}

});

AlertDialog alertDialog=dialogBuilder.create();

//需要设置AlertDialog的类型,保证在广播接收器中可以正常弹出

alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

alertDialog.show();

}

}


最后在AndroidManifest.xml配置:

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

<application

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme" >

<activity

android:name=".LoginActivity"

android:label="@string/app_name" >

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

<activity android:name=".MainActivity" >

</activity>

<receiver android:name=".ForceOfflineReceiver" >

<intent-filter>

<action android:name="com.example.broadcastbestpractice. FORCE_OFFLINE" />

</intent-filter>

</receiver>

</application>