Android四大组件之 BroadcastReceiver

BroadcastReceiver,就是Android中的“广播接收者”,它是Android四大基本组件之一,这种组件本质上是一种全局的监听器,用于监听系统全局的广播消息。它可以接收来自系统和应用的的广播。
由于BroadcastReceiver是一种全局的监听器,因此它可以非常方便地实现系统不同组件之间的通信。比如Activity与通过startService()方法启动的Service之间通信,也可以作为Android中IPC的一种方式。如果你不需要跨进程发送广播,可以考虑使用这个类LocalBroadcastManager,这是一个更有效的实现,可以使你不用去考虑与其他应用程序接收与发送广播的任何安全问题。同样BroadcastReceiver的通信依赖 Binder 机制的,LocalBroadcastManager相对 BroadcastReceiver,它只能用于应用内通信,安全性更好,同时拥有更高的运行效率。也是需要发送应用内广播时的官方推荐。

BroadcastReceiver的生命周期

生命周期流程

一个BroadcastReceiver对象适用于调用Receive方法期间内。只要这个方法结束,系统会认为广播结束,不再是活跃的对象。另外,每次广播被接收后会重新创建BroadcastReceiver对象,并在onReceiver方法中执行完就销毁,如果BroadcastReceiver的onReceiver方法不能在10秒内执行完成,Android会出现ANR异常。所以不要在BroadcastReceiver的onReceiver方法中执行耗时的操作。如果需要在BroadcastReceiver中执行耗时的操作,可以通过Intent启动Service来完成。但不能绑定Service。你可能无法从一个BroadcastReceiver中显示一个对话框,那么可以使用NotificationManager的API来完成这个事情。如果在onReceiver方法中启动一个新Thread进程很可能被系统清理了,因为如果一个线程如果没有依托任何组件存在优先级比较低,这里涉及到了Android中进程的优先级和生命周期

进程的优先级

在Android中,有着5个进程等级,有着不同的优先级

1. 前台进程

前台进程是用户此时需要处理和显示的。下列的条件有任何一个成立,这个进程都被认为是在前台运行的。
1)进程中的某个Activity正在与用户进行交互(Activity的onResume()方法被调用)
2)绑定到与当前用户正在交互的activity的Service所在的进程
3)进程中的某个Service正运行在前台,即这个service的startForeground()方法被调用
4)进程中的某个Service正在执行生命周期回调方法(比如,onCreate(),onStart(),或者onDestory())
5)进程中的BroadcastReceiver正在执行onReceive()方法。
一般来说的话,在某特定时刻,也仅会有为数不多的几个前台进程。这些前台进程的重要性最高,当系统内存相当低,以致不能继续运行这些所有的进程的话,系统才会杀死这些进程。这种情况下,一般是指系统已经到了一个极限边缘了,所以为了让UI继续有反应的话,系统不得不杀死一些前台进程。

2. 可见进程

可视进程是指没有前台运行的组件,但仍然会对用户在屏幕看到的内容造成影响的进程。满足下面条件的进程都可以算作可视进程:
1)进程运行的Activity不在前台,但仍然是可见的(调用了onPause()方法)。这种情况可能是这样的,正在前台运行的Activity启动了一个对话框,这个对话框悬浮在这个activity之上。
2)进程中的Service绑定到了一个可视的activity(表示该activity已调用了onPause()方法)。
可视进程也是有着极高重要性的进程,只有在系统为了保持前台进程运行而不得不杀死可视进程的时候,才会杀死可视进程。

3. 服务进程

服务进程就是指除了上面两种进程外,如果一个进程已经通过startService()方法启动了一个service的进程。虽然这种service进程跟用户的看到的内容不相关,但它们所做的工作也是用户关心的(比如在后台播放音乐或者正在下载互联网上的资源),系统会一直保持服务进程,除非系统为了前台进程和可视进程的运行,而不得不杀死服务进程。

4. 后台进程

后台进程是指进程中的activity当前对用户来说不可见(这个activity调用了onStop()方法)。后台进程不会对用户的体验造成任何影响,并且系统可以在前台进程、可视进程、服务继承需要内存资源的时候会杀死后台进程。通常会有很多后台进程运行,并且这些后台进程保存在一个最近使用列表中,这样做的好处就是保证用户最近看到的进程最后被杀死。如果一个activity已经正确的实现了生命周期方法,并且保存了当前的状态,那么系统杀死这些后台进程对用户的可视效果来说的话,没有任何影响,因为当用户返回回来的时候,这个activity已经保存了所有的可视状态。

5. 空进程

一个空进程没有任何运行的程序组件。系统保持空进程存在的唯一原因就是为了缓存方面的考虑,这样做主要是为了提高组件的启动时间。系统经常会杀死这些空进程来保持整个系统资源和内核缓存之间的平衡。

Android根据进程中运行的最重要的组件进行划分进程的重要性,比如说,如果一个进程中即有一个可视的activity,又有一个service,那么这个进程应该属于可视进程而不是服务进程。 一个进程的级别可能会由于其它进程依赖于它而升高。一个为其它进程提供服务的进程级别永远高于使用它的进程。比如说,A进程为B进程的客户端提供服务,或进程A中的服务为进程B中的组件所绑定,则A进程最低也会被视为与B进程拥有同样的优先级。

这样其实说明了在广播的onReceive中如果单独启动一个线程而不依赖任何组件,那么在广播结束后这个线程可能也就不存在了,不能完成线程中的任务。

BroadcastReceiver的种类

普通广播

Normal broadcasts是完全异步的可以同一时间被所有的接收者接收到,消息的传递效率比较高。但缺点是接收者不能将接收的消息的处理信息传递给下一个接收者也不能停止消息的传播,可以通过Context.sendBroadcast()方法来发送。

有序广播

Ordered broadcasts的接收者按照一定的优先级进行消息的接收。如:A,B,C的优先级依次降低,那么消息先传递给A,在传递给B,最后传递给C。优先级别声明在中,取值为[-1000,1000]数值越大优先级别越高。优先级也可通过filter.setPriority(10)方式设置。 另外Ordered broadcasts的接收者可以通过abortBroadcast()的方式取消广播的传播,也可以通过setResultData和setResultExtras方法将处理的结果存入到Broadcast中,传递给下一个接收者。然后,下一个接收者通过getResultData()和getResultExtras(true)接收高优先级的接收者存入的数据,可以通过sendOrderedBroadcast()发送有序广播。

BroadcastReceiver本质上还是一个监听器,所以使用BroadcastReceiver的方法也是非常简单,只需要继承BroadcastReceiver,在其中重写onReceive(Context context,Intent intent)即可。一旦实现了BroadcastReceiver,并部署到系统中后,就可以在系统的任何位置,通过sendBroadcast、sendOrderedBroadcast方法发送Broadcast给这个BroadcastReceiver,来看一个例子
首先我们在清单文件中注册这个两个广播

 <receiver android:name=".MyBroadcastReceiver">
      <intent-filter android:priority="100">
            <action android:name="com.myTest.Receiver" />
      </intent-filter>
 </receiver>

 <receiver android:name=".MyReceiver">
      //优先级高于第一个receiver
      <intent-filter android:priority="300">
            <action android:name="com.myTest.Receiver" />
      </intent-filter>
 </receiver>

然后我们开一下这两个广播的内容

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle b = intent.getBundleExtra("data");
        String entity = b.getString("entity");
        Log.v("myTag","Second_receiver收到:" + entity + ":" + System.currentTimeMillis());
        //有序广播中向第二个广播再次传递
        Bundle d = new Bundle();
        d.putString("from","再次传递");
        setResultExtras(d);
    }
}
public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle b = intent.getBundleExtra("data");
        String entity = b.getString("entity");
        Log.v("myTag", "First_receiver收到:" + entity + ":" + System.currentTimeMillis());

        //接收第一个广播传递来的内容
        Bundle d = getResultExtras(true);
        String from = d.getString("from");
        Log.v("myTag", "First_receiver收到:" + from + System.currentTimeMillis());
    }
}

我们的Activity中是两个按钮,分别触发一个普通广播和一个有序广播,代码如下

public class MainActivity extends ActionBarActivity {

    private static final String ACTION = "com.myTest.Receiver";

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

        findViewById(R.id.button_first).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(ACTION);
                Bundle bundle = new Bundle();
                bundle.putString("entity", "我是普通广播");
                intent.putExtra("data", bundle);
                sendBroadcast(intent);

            }
        });
        findViewById(R.id.button_second).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(ACTION);
                Bundle bundle = new Bundle();
                bundle.putString("entity", "我是有序广播");
                intent.putExtra("data", bundle);
                sendOrderedBroadcast(intent, null);
            }
        });

    }
}

然后我分别点击这两个按钮看一下Log日志输出
Android四大组件之 BroadcastReceiver_第1张图片

可以看到有序广播具备二次传递的功能,无论是那种形式的广播都会按照优先级(-1000 ~ 1000)触发

BroadcastReceiver的注册方式

广播接收器注册一共有两种形式 : 静态注册和动态注册,两者及其接收广播的区别:

1.动态注册的广播永远要快于静态注册的广播,不管静态注册的优先级设置的多高,不管动态注册的优先级有多低

2.动态注册广播不是常驻型广播,也就是说广播跟随activity的生命周期。注意: 在activity结束前,移除广播接收器。 静态注册是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。

3.在同一个优先级下,谁先启动的快,谁将先接收到广播.


静态注册

静态注册就是在xml文件中进行广播的配置,比如如下

<receiver android:name=".comm.ConnectivityChangeBroadcastReceiver">
     <intent-filter>
           <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
     </intent-filter>
</receiver>
public class ConnectivityChangeBroadcastReceiver extends BroadcastReceiver {
    private static final String TAG = "ConnectivityChangeBroadcastReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, intent.getAction(), Toast.LENGTH_SHORT).show();
        if (isNetworkConnected(context)) {
            Toast.makeText(context, "网络连通了", Toast.LENGTH_SHORT).show();
        }
    }

    public boolean isNetworkConnected(Context context) {
        if (context != null) {
            ConnectivityManager mConnectivityManager = (ConnectivityManager) context
                    .getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
            if (mNetworkInfo != null) {
                return true;
            }
        }
        return false;
    }
}


动态注册

动态注册是直接在代码中进行广播的配置,直接在代码中通过调用Context的registerReceiver函数,可以在程序中动态注册BroadcastReceiver。

  1. registerReceiver(BroadcastReceiver receiver, IntentFilter filter)

  2. registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler)

public class MainActivity extends Activity {
    public static final String BROADCAST_ACTION = "com.example.myTest";
    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(BROADCAST_ACTION);
        registerReceiver(mBroadcastReceiver, intentFilter);
    }

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

}

LocalBroadcastManager

Android v4 兼容包提供android.support.v4.content.LocalBroadcastManager工具类,帮助大家在自己的进程内进行局部广播发送与注册,使用它比直接通过sendBroadcast(Intent)发送系统全局广播有以下几点好处。

1 因广播数据在本应用范围内传播,你不用担心隐私数据泄露的问题。

2 不用担心别的应用伪造广播,造成安全隐患。

3 相比在系统内发送全局广播,它更高效。

其使用方法也和正常注册广播类似,同样也是先自定义一个BroadcastReceiver子类

public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
       Log.v("myTag","ok");
    }
}

然后动态注册这个广播和接收器,并发送广播

public class MainActivity extends ActionBarActivity {

    private static final String ACTION = "com.myTest.Receiver";
    private LocalBroadcastManager mg;
    private MyBroadcastReceiver receiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        receiver = new MyBroadcastReceiver();
        mg = LocalBroadcastManager.getInstance(this);

        findViewById(R.id.button_first).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(ACTION);
                IntentFilter filter = new IntentFilter(ACTION);
                mg.registerReceiver(receiver,filter);
                mg.sendBroadcast(intent);
            }
        });
    }

    //和正常广播一样,也要在对应的生命周期中反注册掉: 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mg.unregisterReceiver(receiver);
    }
}

这样就完成了一个应用中的传递机制, LocalBroadcastManager 的核心实现实际还是 Handler,只是利用到了 IntentFilter 的 match 功能,至于 BroadcastReceiver 换成其他接口也无所谓,顺便利用了现成的类和概念而已。因为是 Handler 实现的应用内的通信,自然安全性更好,效率更高。

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