Android四大组件--Broadcast Receiver具体解释

本文主要讲述了:

一、BroadcastReceiver概述:

二、BroadcastReceiver事件分类

三、BroadcastReceiver事件的编程流程

四、两类BroadcastReceiver

五、普通广播和有序广播

六、Service与BroadcastReceiver怎样交互?

七、开机自己主动执行service

八、BroadcastReceiver的生命周期


一、BroadcastReceiver概述:

1、广播接收器是一个专注于接收广播通知信息,并做出相应处理的组件。

非常多广播是源自于系统代码的──比方,通知时区改变、电池电量低、拍摄了一张照片或者用户改变了语言选项。

应用程序也能够进行广播──比方说,通知其他应用程序一些数据下载完毕并处于可用状态。
2、应用程序能够拥有随意数量的广播接收器以对全部它感兴趣的通知信息予以响应。全部的接收器均继承自BroadcastReceiver基类。


2、广播接收器没实用户界面。

然而,它们能够启动一个activity来响应它们收到的信息,或者用NotificationManager来通知用户。通知能够用非常多种方式来吸引用户的注意力──闪动背灯、震动、播放声音等等。

一般来说是在状态栏上放一个持久的图标,用户能够打开它并获取消息。


二、BroadcastReceiver事件分类

1、系统广播事件,比方:ACTION_BOOT_COMPLETED(系统启动完毕后触发)。ACTION_TIME_CHANGED(系统时间改变时触发),ACTION_BATTERY_LOW(电量低时触发)等等。

2、用户自己定义的广播事件。


三、BroadcastReceiver事件的编程流程

1、注冊广播事件:注冊方式有两种,

一种是静态注冊,就是在 AndroidManifest.xml文件里定义,注冊的广播接收器必需要继承BroadcastReceiver类;

在AndroidManifest.xml中用标签生命注冊,并在标签内用标签设置过滤器。

<receiver android:name="myRecevice">    //继承BroadcastReceiver。重写onReceiver方法


    <intent-filter>    


      <action android:name="com.dragon.net"></action> //使用过滤器。接收指定action广播


      </intent-filter>


  </receiver>

还有一种是动态注冊,是在程序中使用 Context.registerReceiver注冊,注冊的广播接收器相当于一个匿名类。两种方式都须要IntentFIlter。

IntentFilter intentFilter = new IntentFilter();


intentFilter.addAction(String);   //为BroadcastReceiver指定action。使之用于接收同action的广播


registerReceiver(BroadcastReceiver,intentFilter);

  一般:在onStart中注冊,onStop中取消unregisterReceiver

  指定广播目标Action:Intent intent = new Intent(actionString);

  而且可通过Intent携带消息 :intent.putExtra("msg", "hello,我通过广播发送消息了");

  发送广播消息:Context.sendBroadcast(intent )


2、发送广播事件:通过Context.sendBroadcast来发送,由Intent来传递注冊时用到的Action。

3、 接收广播事件:当发送的广播被接收器监听到后。会调用它的onReceive()方法,并将包括消息的Intent对象传给它。onReceive中代码的运行时间不要超过5s,否则Android会弹出超时dialog。

四、两类BroadcastReceiver

1、正常广播 Normal broadcasts(用 Context.sendBroadcast()发送)是全然异步的。它们都执行在一个没有定义的顺序,一般是在同一时间。这样会更有效,但意味着receiver不能包括所要使用的结果或中止的API。  
2、有序广播 Ordered broadcasts(用 Context.sendOrderedBroadcast()发送)每次被发送到一个receiver。所谓有序,就是每一个receiver执行后能够传播到下一个receiver,也能够全然中止传播——不传播给其它receiver。

而receiver执行的顺序能够通过matched intent-filter 里面的android:priority来控制。当priority优先级同样的时候。Receiver以随意的顺序执行。


PS:

以下举例说明了4种情况的广播事件:静态注冊的系统广播事件、静态注冊的用户自己定义广播事件、动态注冊的系统广播事件和动态注冊的用户自己定义广播事件。

1、创建广播接受者

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class MyReceiver extends BroadcastReceiver {
	
	private static final String TAG = "MyReceiver";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		String msg = intent.getStringExtra("msg");
		Log.i(TAG, msg);
	}

}
2、广播注冊

1)静态注冊
静态注冊是在AndroidManifest.xml文件里配置的。我们就来为MyReceiver注冊一个广播地址:

<receiver android:name=".MyReceiver">
        	<intent-filter>
        		<action android:name="android.intent.action.MY_BROADCAST"/>
        		<category android:name="android.intent.category.DEFAULT" />
        	</intent-filter>
        </receiver>
配置了以上信息之后,仅仅要是android.intent.action.MY_BROADCAST这个地址的广播,MyReceiver都可以接收的到。


2)动态注冊
动态注冊须要在代码中动态的指定广播地址并注冊。通常我们是在Activity或Service注冊一个广播。以下我们就来看一下注冊的代码:

MyReceiver receiver = new MyReceiver();
        
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.MY_BROADCAST");
        
registerReceiver(receiver, filter);

3)发送广播

    public void send(View view) {
    	Intent intent = new Intent("android.intent.action.MY_BROADCAST");
    	intent.putExtra("msg", "hello receiver.");
    	sendBroadcast(intent);
    }

PS:

以下简介下系统广播

以下是android系统中定义了非常多标准的Broadcast Action来响应系统的广播事件(仅仅列出一部分)

①ACTION_TIME_CHANGED(时间改变时触发)
②ACTION_BOOT_COMPLETED(系统启动完毕后触发)--比方有些程序开机后启动就是用这样的方式来实现的
③ACTION_PACKAGE_ADDED(加入包时触发)
④ACTION_BATTERY_CHANGED(电量低时触发)

/**
 * 系统静态注冊广播消息接收器
 * 
 * @author zuolongsnail
 * 
 */
public class SystemReceiver extends BroadcastReceiver {

	@Override
	public void onReceive(Context context, Intent intent) {
		if (intent.getAction().equals(Intent.ACTION_BATTERY_LOW)) {
			Log.e("SystemReceiver", "电量低提示");
			Toast.makeText(context, "您的手机电量偏低,请及时充电", Toast.LENGTH_SHORT).show();
		}
	}
}

<?xml version="1.0" encoding="utf-8"?

> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.byread" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- 注冊系统静态广播接收器 --> <receiver android:name=".SystemReceiver"> <intent-filter> <action android:name="android.intent.action.BATTERY_LOW" /> </intent-filter> </receiver> </application> </manifest>


五、普通广播和有序广播

上面的样例仅仅是一个接收者来接收广播。假设有多个接收者都注冊了同样的广播地址,又会是什么情况呢。能同一时候接收到同一条广播吗,相互之间会不会有干扰呢?

这就涉及到普通广播和有序广播的概念了。

1、Normal Broadcast(普通广播):Normal Broadcast是全然异步的。能够在同一时刻(逻辑上)被全部接收者接收到,消息传递的效率比較高。

但缺点是接受者不能将处理结果传递给下一个接收者,而且无法终止Broadcast Intent的广播。
 
 2、Ordered Broadcast(有序广播):Ordered Broadcast的接收者将按预先声明的优先级依次接受Broadcast。如:A的级别高于B、B的级别高于C,那么Broadcast先传给A,再传给B,最后传给C。

优先级别声明在<intent-filter.../>元素的android:priority属性中,数越大优先级别越高。取值范围为-1000-1000,优先级别也能够调用IntentFilter对象的setPriority()进行设置。OrderedBroadcast接收者能够终止Broadcast Intent的传播,BroadcastIntent的传播一旦终止,后面的接收者就无法接收到Broadcast。

另外,OrderedBroadcast的接收者能够将数据传递给下一个接收者。

如:A得到Broadcast后,能够往它的结果对象中存入数据,当Broadcast传给B时,B能够从A的结果对象中得到A存入的数据。


3、context提供的例如以下两个方法用于发送广播:
  sendBroadcast():发送Normal Broadcast
    sendOrderedBroadcast():发送OrderedBroadcast。
 
4、对于OrderedBroadcast而言,系统会依据接收者生命的优先级别顺序逐个运行接收者。优先接收到Broadcast的接收者能够终止Broadcast,调用BroadcastReceiver的abortBroadcast()方法就可以终止Broadcast。假设Broadcast被前面的接收者终止。后面的接收者就再也无法获取到Broadcast。


 
5、不仅如此,对于OrderBroadcast而言。优先接收到Broadcast的接收者能够通过setResultExtras(Bundle)方法将处理结果存入Broadcast中,然后传给下一个接收者,下一个接收者通过代码:  Bundle bundle=getResultExtras(true)能够获取上一个接收者存入的数据。

实例:点击button,两个Receiver接收同一条广播。在logcat中打印出数据(依照Receiver的优先顺序。Receiver2先,Receiver1后)

Manifest:      

<?

xml version="1.0" encoding="utf-8"?

> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.song" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:label="@string/app_name" android:name=".C48_BroadcastActivity" > <intent-filter > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <!--优先级的设定 MyReceiver2大于MyReceiver1。优先级的范围-1000~1000 --> </activity> <receiver android:name=".MyReceiver1"> <intent-filter android:priority="200"> <action android:name="com.song.123"/> </intent-filter> </receiver> <receiver android:name=".MyReceiver2"> <intent-filter android:priority="1000"> <action android:name="com.song.123"/> </intent-filter> </receiver> </application> </manifest>


activity代码:

//发送广播。bundle绑上key为a的数据
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class C48_BroadcastActivity extends Activity {
    /** Called when the activity is first created. */
	Button button;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        button=(Button)findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				Intent intent=new Intent("com.song.123");
				Bundle bundle=new Bundle();
				bundle.putString("a", "aaa");
				intent.putExtras(bundle);
				//有序广播
				sendOrderedBroadcast(intent, null);
			}
		});
        
    }
}
Receiver2

package com.song;
//优先接到广播,bundle绑上key为b的数据
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;

public class MyReceiver2 extends BroadcastReceiver{

	@Override
	public void onReceive(Context context, Intent intent) {
		// TODO Auto-generated method stub
		System.out.println("receiver2");
//		context.getSystemService(name);
		Bundle bundle=intent.getExtras();
		bundle.putString("b", "bbb");
		System.out.println("a="+bundle.get("a"));
		setResultExtras(bundle);
		//切断广播
//		abortBroadcast();     
	}

}

Receiver1

package com.song;
//接收从receiver2传来的广播,包括key为a和b的数据
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;

public class MyReceiver1 extends BroadcastReceiver{

	@Override
	public void onReceive(Context context, Intent intent) {
		// TODO Auto-generated method stub
		System.out.println("receiver1");
		//要不要接受上一个广播接收器receiver2传来的的数据
		Bundle bundle=getResultExtras(true);
		System.out.println("a="+bundle.getString("a")+",b="+bundle.getString("b"));
	}

}

依据上面的配置能够看出。该程序包含两个receiver。当中Receiver2高,Receiver1低。

假设Receiver2中的Receiver2zhabortBroadcast()凝视了。那么程序Receiver1中将能够看到完整的信息了。


六、Service与BroadcastReceiver怎样交互?

我们之前都是先启动了一个Activity。然后在Activity中启动服务。

假设是这样,在启动服务时必需要先启动一个Activity。

在非常多时候这样做有些多余。我们如今能够利用Broadcast Receiver在Android系统启动时执行一个Activity。或许我们会从中得到一些启示,既然能够在Broadcast Receiver中启动Activity。为什么不能启动Service呢?说做就做,如今让我们来验证一下这个想法。

先编写一个服务类。这个服务类没什么特别的。仍然使用前面两节编写的MyService类就可以。

在AndroidManifest.xml文件里配置MyService类的代码也同样。

以下来完毕最关键的一步。就是建立一个BroadcastReceiver。代码例如以下:

import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
 
public class StartupReceiver extends BroadcastReceiver  
{  
    @Override  
    public void onReceive(Context context, Intent intent)  
    {  
        //  启动一个Service  
        Intent serviceIntent = new Intent(context, MyService.class);          
        context.startService(serviceIntent);          
        Intent activityIntent = new Intent(context, MessageActivity.class);  
        //  要想在Service中启动Activity。必须设置例如以下标志  
        activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
        context.startActivity(activityIntent);  
    }  
} 

在StartupReceiver类的onReceive方法中完毕了两项工作:启动服务和显示一个Activity来提示服务启动成功。


AndroidManifest.xml文件的完整代码。

<?

xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.blogjava.mobile.startupservice" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MessageActivity" android:theme="@android:style/Theme.Dialog"> <intent-filter> <category android:name="android. intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name="StartupReceiver"> <intent-filter> <action android:name="android. intent.action.BOOT_COMPLETED" /> <category android:name="android. intent.category.LAUNCHER" /> </intent-filter> </receiver> <service android:enabled="true" android:name=".MyService" /> </application> <uses-sdk android:minSdkVersion="3" /> <uses-permission android:name="android. permission.RECEIVE_BOOT_COMPLETED" /> </manifest>


PS:

开机自己主动执行service

我们常常会有这种应用场合,比方消息推送服务,须要实现开机启动的功能。要实现这个功能,我们就能够订阅系统“启动完毕(BOOT_COMPLETED)”这条广播,接收到这条广播后我们就能够启动自己的服务了;

上面实例事实上和开机启动服务差点儿相同了,以下我们在说说开机启动服务。


Receiver :

public class BootCompleteReceiver extends BroadcastReceiver {
	
	private static final String TAG = "BootCompleteReceiver";
	
	@Override
	public void onReceive(Context context, Intent intent) {
		Intent service = new Intent(context, MsgPushService.class);
		context.startService(service);
		Log.i(TAG, "Boot Complete. Starting MsgPushService...");
	}

}


Service :

public class MsgPushService extends Service {

	private static final String TAG = "MsgPushService";
	
	@Override
	public void onCreate() {
		super.onCreate();
		Log.i(TAG, "onCreate called.");
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.i(TAG, "onStartCommand called.");
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public IBinder onBind(Intent arg0) {
		return null;
	}
}
AndroidManifest
        <!-- 开机广播接受者 -->
        <receiver android:name=".BootCompleteReceiver">
        	<intent-filter>
        		<!-- 注冊开机广播地址-->
        		<action android:name="android.intent.action.BOOT_COMPLETED"/>
        		<category android:name="android.intent.category.DEFAULT" />
        	</intent-filter>
        </receiver>
        <!-- 消息推送服务 -->
        <service android:name=".MsgPushService"/>

PS:

最后在说说Broadcast的生命周期

  1、一个BroadcastReceiver 对象仅仅有在被调用onReceive(Context, Intent)的才有效的,当从该函数返回后,该对象就无效的了。结束生命周期。



  因此从这个特征能够看出,在所调用的onReceive(Context, Intent)函数里。不能有过于耗时的操作。不能使用线程来运行。对于耗时的操作,请start service来完毕。由于当得到其它异步操作所返回的结果时,BroadcastReceiver 可能已经无效了。

2、一个Broadcast receiver仅仅有一个回调方法:

void onReceive(Context curContext, Intent broadcastMsg)

当Broadcast receiver接收到一条广播信息,android会调用它的onReceive()方法,并传递给它一个包括广播信息的intent对象。当Broadcast receiver在运行这种方法时能够觉得它是活动的。onReceive()方法返回时,它便终止了。

一个包括活动Broadcast receiver的进程会被系统保护以避免被终止。可是假设进程不包括不论什么活动组件。那么当它占用的内存要用于其他进程时,系统不论什么时候都能够终止执行该进程。

当应答一条广播信息十分耗时。而还有一个线程必须执行某个任务时会出现一个问题。试想一下,假设onReceive()方法产生一个线程然后返回,那么整个进程,包含新的线程会被觉得是不活动的(除非在进程中还有其他活动的组件)。该进程就有被系统终止执行的危急。解决问题的办法就是在onReceive()方法中启动一个服务,让这个服务做那样的工作,这样系统就会知道进程中有活动的组件而不会停止进程。



原文地址:https://www.cnblogs.com/zfyouxi/p/5137073.html