广播——broadcast

    • 广播机制简介
      • 广播分类
      • 标准广播
      • 有序广播
    • 接收系统广播
      • 动态注册监听网络变化
      • 静态注册实现开机启动
      • 注意耗时操作
    • 发送自定义广播
    • 发送有序广播
      • 发送有序广播步骤
      • 优先级
    • 使用本地广播
      • 出现原因
      • 本地广播使用
      • 优点
    • 广播优先级
      • 基本原则
      • ordered 广播
      • 非 ordered 广播
    • 只能动态接受广播源码分析
      • 目的
      • 参考httpblogcsdnnetgemmemarticledetails8859493
    • 广播注册过程分析
      • 静态注册 Receiver 的流程
      • 动态注册 Receiver 的流程
    • 广播发送过程分析
      • 分析
      • 总结
      • 参考
    • 自己动手

广播机制简介

广播是一种可以跨进程的通信方式(比如:接收系统广播)。

Android 广播不关心接收者是否收到处理或者如何处理广播,可以说是一种单向的通知。Android 通过 BraodcastReceiver 来监听系统发出的广播,不同的 BraodcastReceiver 通过设置不同的 fliter 来区分监听广播的类型。有些广播的监听需要相应的权限。

广播分类

  1. 标准广播(Normal Broadcast)
  2. 有序广播(Ordered Broadcast)

标准广播

标准广播:完全异步执行的广播,在广播发生之后,所有广播同一时间接收到广播消息,无任何先后顺序,效率高,但无法被截断。
广播——broadcast_第1张图片

有序广播

有序广播:同步执行的广播,广播发出后,同一时刻只有一个广播接收器能收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递,广播接收器有先后顺序,优先级高的先收到,且可以截断,后面的无法接收。
广播——broadcast_第2张图片

接收系统广播

BraodcastReceiver 必须经过注册才能具有监听功能,注册的方式有两种:
广播注册方式:在代码中注册,又称动态注册。以及在AndroidManifest.xml清单文件中注册。

动态注册监听网络变化

(1)创建广播接收器
新建一个内部类,让他继承BroadcastReceiver,并重写父类的onReceive()方法。
(2)代码注册广播。
(3)代码举例

public class MainActivity extends Activity {

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

        intentFilter = new IntentFilter();
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        myReceiver = new MyBroadcastReceiver();
        //注册广播
        registerReceiver(myReceiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(myReceiver);
    }

    class MyBroadcastReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = cm.getActiveNetworkInfo();
            Toast.makeText(MainActivity.this, "i am change" + networkInfo.getState(), Toast.LENGTH_LONG).show();
        }
    }
}

添加获取网络状态的权限。

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

静态注册实现开机启动

(1)动态注册的缺点:必须在程序开启时才能接收广播。
(2)所以,静态注册解决了这个问题。
静态注册步骤:

  • 新建一个类继承BroadcastReceiver,实现onReceiver(),不再是内部类。
  • 在AndroidManifest.xml中注册,此时是作为四大组件,用标签,加上等,注意也要加上相关需要的权限。

BootCompleteReceiver .java

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" />
注册
<receiver android:name=".BootCompleteReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

注意耗时操作

不要在onReceive()中添加过多的逻辑或者进行耗时操作。因为广播接收器不允许开启线程,当onReceive()运行较长时间而没结束时,程序会报错。8s左右即可,超过就要考虑转移处理。(比如在这里面启动一个服务,在服务中进行操作?)。

发送自定义广播

  1. 在清单文件中注册及对应的action。
  2. 在使用的地方sendBroadcast(intent)
    intent构造函数传入action作为参数(String)。同时,用intent还可以携带数据。
Intent intent = new Intent(
                        "com.example.broadcasttest.MY_BROADCAS");
sendBroadcast(intent);
public class MyBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "received in MyBroadcastReceive",
                Toast.LENGTH_SHORT).show();
    }

}
//这里的action是自定义的,不是android自带
<receiver android:name=".MyBroadcastReceiver">
            <intent-filter>
                <action android:name="com.example.broadcasttest.MY_BROADCAST"/>
            </intent-filter>
        </receiver>

注意:在这之前的广播都属于标准广播

发送有序广播

发送有序广播步骤

  • sendOrderBroadcast(intent, null)
  • 注册时,进行设置优先级。或者通过调用 IntentFilter 对象的 setPriority()进行设置。
  • 截断:优先级高的接收器中调用abortBroadcast(),后面的接收器就接收不到了。

优先级

  • 范围:-1000~1000,但是实际最大值是可以达到int的最大值2147483647。

使用本地广播

出现原因

前面发送和接收的广播均属于系统全局广播,即发出的广播可以被其他任何程序接收到,且我们也可以接收其他程序的广播。这就导致了安全性问题(比如关键数据或者是垃圾广播)。
所以,引入本地广播机制,只能在应用程序内部进行传递,本地广播机制中的广播接收器也只能接收来自本应用发出的广播。

本地广播使用

  1. LocalBroadcastManager来对广播进行管理。
  2. LocalBroadcastManager localBroadcastManager;
    localBroadcastManager = LocalBroadcastManager.getInstance(this);
  3. localBroadcastManager.sendBroadcast(intent);
  4. //注册本地广播监听器localBroadcastManager.registerReceiver(localReceiver, intentFilter);
  5. localBroadcastManager.unregisterReceiver(localReceiver);
    代码:
public class MainActivity extends Activity {

    private IntentFilter intentFilter;

    private LocalReceiver localReceiver;

    private LocalBroadcastManager localBroadcastManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(
                        "com.example.broadcasttest.LOCAL_BROADCAST");
                localBroadcastManager.sendBroadcast(intent);
            }
        });
        intentFilter = new IntentFilter();
        intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
        localReceiver = new LocalReceiver();
        localBroadcastManager.registerReceiver(localReceiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.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();
        }

    }

}

优点

本地广播是无法通过静态注册的方式来接收的。其实这也完全
可以理解,因为静态注册主要就是为了让程序在未启动的情况下也能收到广播,而发送本地
广播时,我们的程序肯定是已经启动了,因此也完全不需要使用静态注册的功能。
最后我们再来盘点一下使用本地广播的几点优势吧。
1. 可以明确地知道正在发送的广播不会离开我们的程序,因此不需要担心机密数据泄
漏的问题。
2. 其他的程序无法将广播发送到我们程序的内部,因此不需要担心会有安全漏洞的隐
患。
3. 发送本地广播比起发送系统全局广播将会更加高效。

广播优先级

基本原则

接收无序广播的接收器接收到广播的顺序是有序的(由优先级决定顺序)
接收无序广播的接收器也一样可以设置优先级的
动态注册广播优先级高于静态注册广播
同等优先级的动态接收器,先注册的先接收
同等优先级的静态接收器,接收广播的顺序与 String[] java.io.File.list()顺序一致
Ps:这里有一点需要注意的是,同等优先级的静态接收器的接收顺序具有不确定性,原因就是File.list()的方法返回的顺序具有不确定性,如果需要查看某接收器的接收顺序,最好是试验大量的 apk 名。

ordered 广播

假设有如下优先级的 5 个接收器
1.动态 A(优先级=1)
2.动态 B(优先级=2)
3.动态 C(优先级=2)
4.静态 D(优先级=1)
5.静态 E(优先级=2)
并且 B 先于 C 注册
那么实际接收顺序应为
B C E A D
也就是说,如果静态接收器的优先级高于动态接收器的优先级,那么还是静态接收器先接收到广播(比如接收 SMS 广播)

非 ordered 广播

动态接收器高优先级>动态接收器低优先级>静态接收器高优先级>静态接收器低优先级

只能动态接受广播源码分析

有些广播,我们无法用静态接收器接收,比如 ACTION_SCREEN_ON,当屏幕被点亮的时候系统发送此广播。

void com.android.server.PowerManagerService.initInThread()
Java 代码
void initInThread() {
??
mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
mScreenOnIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
mScreenOffIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
??
}

Intent 中都设置了 Intent.FLAG_RECEIVER_REGISTERED_ONLY,所以,如果要接收,必须动态注册广播接收器 ACTION_SCREEN_OFF 也是如此。类似的还有:ACTION_TIME_TICK、ACTION_BATTERY_CHANGED。

目的

很多病毒程序为了保证自己被终止后能够再次运行,都会在xml中注册一些系统广播,企图利用这些系统高频广播来实现自启动。
比如在老版本的android系统中,病毒程序可以通过监听TIME_TICK来启动自己的service后台运行,做一些隐秘的工作,而且就算自己被kill掉了,也能很快重新启动。
而一旦这些系统广播加了flag FLAG_RECEIVER_REGISTERED_ONLY,这些病毒程序就没辙了。google的这一改动无疑提升了android系统的安全性。

参考:http://blog.csdn.net/gemmem/article/details/8859493

广播注册过程分析

静态注册 Receiver 的流程

静态 receiver 的注册是由 PackageManagerService 开机的时候负责初始 PMS 在开机的时候会对系统一些目录逐个扫描,解析 apk 文件。静态广播接收器就是在 PMS 做这件事情的时候顺便处理的。
PMS 会解析 apk 的 manifest 文件,查找这里注册的 receiver,然后加载到内存中。
PMS 初始化扫描目录的顺序:
system/framework
system/app
vendor/app
data/appd
rm/app-private
我们看到了 PMS 如何在初始化的时候如何解析 manifest 并把其中的 element 存放到内存中的其中receiver 保存到了 owner 的成员变量 receivers 中,owner 的类型是
android.content.pm.PackageParser.Package 也就是说 scanPackageLI 返回结果就是已经包含了manifest 信息的 Package 对象。

动态注册 Receiver 的流程

动态注册最终会调用到 AMS 中的 registerReceiver 函数,最终所有动态注册的 receiver 都保存到 AMS 的成员变量 mReceiverResolver 中。静态广播和动态广播如何注册的,我们已经全部分析完了。静态广播是 PackageManagerService负责,保存到其成员变量 mReceivers 中,动态广播是 ActivityManagerService 负责,保存到其成员变量 mReceiverResolver 中。

广播发送过程分析

分析

Context 中的 sendBroadCast 函数的实现是在 ContextImpl 中,和发送广播相关的有如下六个函数:
void android.app.ContextImpl.sendBroadcast(Intent intent)
void android.app.ContextImpl.sendBroadcast(Intent intent, String receiverPermission)
void android.app.ContextImpl.sendOrderedBroadcast(Intent intent, String receiverPermission)
void android.app.ContextImpl.sendOrderedBroadcast(Intent intent, String receiverPermission,
BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData,
Bundle initialExtras)
void android.app.ContextImpl.sendStickyBroadcast(Intent intent)
void android.app.ContextImpl.sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver
resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle
initialExtras)

可以分为 3 组:1 普通广播;2 Ordered 广播;3 Sticky 广播。不论哪种,最后都会由 AMS 处, 如果是非 ordered 广播,那么 mParallelBroadcasts 将存储所有动态接收器,然后合并的时候,mParallelBroadcasts 设置为 null,所以不会合并到 receivers 中,如果是 ordered 广播,那么mParallelBroadcasts 将合并到 receivers 中,然后,不管是哪种广播,最后都是调用scheduleBroadcastsLocked 继续处理,最终到 processNextBroadcast 函数上。广播是否有序,即通过 Boolean 变量 ordered 进行设置。

总结

发送过程情况分为两种(scheduleBroadcastsLocked),ordered 广播和非 ordered 广播、非ordered 广播。先处理动接收器,然后处理静态接收器 ordered 广播同时处理动态接收器和静态接收器先将动态接收器与静态接收器合并,保持着与优先级相同的顺序,优先级高的在前面,否则顺序不变。静态接收器与动态接收器优先级相同的话,动态接收器在前。

参考

http://www.2cto.com/kf/201408/325326.html

自己动手

控制activity

public class ActivityControl {
    public static List<Activity> activityList =  new ArrayList<Activity>();

    public static void addActivity(Activity activity){
        activityList.add(activity);
    }

    public static void rmActivity(Activity activity){
        activityList.remove(activity);
    }

    public static void finishAllActivity(){
        int length = activityList.size();
        for(int i = 0; i < length; i++ ){
            activityList.get(i).finish();
        }
    }
}

强制下线(静态注册)

offBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(DownActivity.this, OffLineBroadcast.class);
                sendBroadcast(intent);
            }
        });
//注意,在activity外部跳转activity注意加上FLAG:Intent.FLAG_ACTIVITY_NEW_TASK
public class OffLineBroadcast extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        ActivityControl.finishAllActivity();
        Intent i = new Intent(context, MainActivity.class);
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(i);
    }
}
清单文件注册
<receiver android:name=".OffLineBroadcast"></receiver>

或者在启动广播的时候用intent(action)

Intent intent = new Intent("com.zzz.offLine");
                sendBroadcast(intent);

这种情况下清单文件注册注意加上action

<receiver android:name=".OffLineBroadcast">
            <intent-filter>
                <action android:name="com.zzz.offLine"></action>
            </intent-filter>
</receiver>

增加对话框提示

public class OffLineBroadcast extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        final Context theContext = context;
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle("Warning");
        builder.setMessage("You are forced to be offline, please try to login again.");
        builder.setCancelable(false);
        builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                ActivityControl.finishAllActivity();
                Intent i = new Intent(theContext, MainActivity.class);
                i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                theContext.startActivity(i);
            }
        });

        AlertDialog alertDialog = builder.create();
        //需要设置AlertDialog的类型,保证在广播接收器中可以正常弹出。
        alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

        alertDialog.show();;

    }
}

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

强制下线(动态注册)
动态注册:广播是内部类,并且要注意广播的注册和解除,触发广播用intent(action),系统广播的触发intent不需要我们写。

public class DownActivity extends BaseActivity {

    private Button offBtn;
    private DyOffBroadcast broadcast;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_down);
        initView();

        broadcast = new DyOffBroadcast();
        IntentFilter filter = new IntentFilter();
        filter.addAction("com.zzz.offLine");
        registerReceiver(broadcast, filter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(broadcast);
    }

    private void initView(){
        offBtn = (Button)findViewById(R.id.offLine);
        offBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.zzz.offLine");
                sendBroadcast(intent);
            }
        });
    }



    public class DyOffBroadcast extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            ActivityControl.finishAllActivity();
            Intent i = new Intent(context, MainActivity.class);
            context.startActivity(i);
        }
    }
}

//多加一个对话框提示
(为什么用AlertDialog.Builder而不是AlertDialog呢?AlertDialog.Builder是AlertDialog的一个公开的静态内部类,每个方法返回值都是builder,可以连续性设置,方便,参考设计模式——建造者模式。)
AlertDialog.Builder builder = new AlertDialog.Builder(context)
            .setTitle("Warning")
            .setMessage("You are forced to be offline, please try to login again.")
            .setCancelable(false)
            .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    ActivityControl.finishAllActivity();
                    Intent i = new Intent(theContext, MainActivity.class);
                    theContext.startActivity(i);
                }
            });
            builder.create().show();

你可能感兴趣的:(android,Broadcast)