标签:
现在的BroadcastReceiver好多都用在了做推送上。比如极光推送、信鸽推送等等。
推送用BroadcastReceiver发多都是提示一个Notification。服务端发送推送,推送Android SDK内的通信机制接受到了推送,然后发送广播通知,BroadcastReceiver接收到了通知,进而区分通知类型,执行对应的操作,很多时候,我们都是看到一个Notification(QQ消息通知、微信消息通知等等)。
就是简单的三句话。
为什么会这样子呢?请见下面的浅淡机制。
Android BroadcastReceiver 的实现使用的是观察者模式。
观察者模式:假如,所有事件的都要实现Event接口,监听事件都需要将View的引用作为参数传入Event的构造方法中。有N个View添加了A事件的监听,M个View添加了B事件的监听。那么,就有N+M个View的引用放在观察者池里。此时,假如随便从哪发来了一个A事件,都要遍历那个长度为N+M的观察者池,查看里面的哪个引用的事件是A事件,然后调用引用的事件响应方法。从而,完成一次观察者模式的事件监听。
Android BroadcastReceiver 的实现原理就是如此:使用了观察者模式的监听方式,在动态或静态注册广播时,添加对应的Action作为与BroadcastReceiver通信的通道,发送广播等于消息的发布,添加Action等于订阅事件。这样大大解耦了发送者和接收者,流程大题如下:
1.BroadcastReceiver 通过Binder机制在AMS(Activity Manager Service)注册。
2.View发送广播时,也是通过Binder机制向AMS(Activity Manager Service)发送广播通知。
3.AMS根据发来的Intent里的Action查找对应的IntentFilter或者Permission,筛选出BroadcastReceiver,然后将广播转发到BroadcastReceiver上相应的消息循环队列中。
4.在BroadcastReceiver的消息循环队列执行循环时,拿到了此广播,回调BroadcastReceiver的onReceive()方法。
从此看来,这里的BroadcastReceiver充当了观察者;View充当了消息发送者;AMS就相当于消息处理中心,其内部包含了一个观察者池。
静态BroadcastReceiver就需要在AndroidMainfest.xml文件里配置<receiver>
:
receiver标签属性
<!--
enabled: "true" 或者 "false"
exported: "true" 或者 "false"
icon: 资源Id
label: 资源Id
name: 字符串
permission: 字符串
process: 字符串
-->
<receiver
android:enabled="true"
android:exported="true"
android:icon="@drawable/*"
android:label="@string/*"
android:name="String"
android:permission="String"
android:process="String">
</receiver>
android:exported
:表示的是该BroadcastReceiver是否接受其他App的广播。true为接收;false为不接收。其默认值:如果有intent-filter,默认值为true;没有intent-filter,默认值为false。( Activity 或 Service中的android:exported的默认值是这样设置的 )这里区分的是App,不是进程,因为一个App中可能有多进程。
StaticReceiver
public class StaticReceiver extends BroadcastReceiver {
public static final String INTENT_ACTION = "com.camnter.android.intent.static";
public static final String STATIC_MESSAGE = "message";
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onReceive(Context context, Intent intent) {
String message = intent.getStringExtra(STATIC_MESSAGE);
Intent data = new Intent();
PendingIntent pendingIntent = PendingIntent.getActivity(
context,
0,
data,
PendingIntent.FLAG_CANCEL_CURRENT
);
NotificationManager notificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification.Builder(context)
.setContentTitle("StaticBroadcastReceiver")
.setContentText(message)
.setSmallIcon(R.mipmap.zzmbs_2)
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.zzmbs_2))
.setContentIntent(pendingIntent)
.build();
notificationManager.notify(206, notification);
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
}
AndroidManifest.xml
<receiver
android:name=".broadcastreceiver.StaticReceiver"
android:exported="true"
android:process=":remotereceiver">
<intent-filter>
<action android:name="com.camnter.android.intent.static" />
</intent-filter>
</receiver>
StaticReceiverActivity
public class StaticReceiverActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_static_broadcast_receiver);
this.findViewById(R.id.static_broadcast_receiver_bt).setOnClickListener(this);
}
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.static_broadcast_receiver_bt:
Intent intent = new Intent(StaticReceiver.INTENT_ACTION);
intent.putExtra(StaticReceiver.STATIC_MESSAGE, UUID.randomUUID().toString());
this.sendBroadcast(intent);
break;
}
}
}
动态BroadcastReceiver就不需要在AndroidManifest.xml里配置,需要手动代码注册。
涉及到两个API:
1.Context.registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
2.IntentFilter.addAction(String action)
public final void addAction(String action)
DynamicReceiver
public class DynamicReceiver extends BroadcastReceiver {
public static final String INTENT_ACTION = "com.camnter.android.intent.dynamic";
public static final String DYNAMIC_MESSAGE = "message";
public static DynamicReceiver instance;
private DynamicReceiver() {
}
public static DynamicReceiver getInstance() {
if (instance == null) instance = new DynamicReceiver();
return instance;
}
/**
* 提供给外部注册广播
*
* @param context
*/
public static void register(Context context) {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(INTENT_ACTION);
context.registerReceiver(getInstance(), intentFilter);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onReceive(Context context, Intent intent) {
String message = intent.getStringExtra(DYNAMIC_MESSAGE);
Intent data = new Intent();
PendingIntent pendingIntent = PendingIntent.getActivity(
context,
0,
data,
PendingIntent.FLAG_CANCEL_CURRENT
);
NotificationManager notificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification.Builder(context)
.setContentTitle("StaticBroadcastReceiver")
.setContentText(message)
.setSmallIcon(R.mipmap.zzmbs_2)
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.zzmbs_2))
.setContentIntent(pendingIntent)
.build();
notificationManager.notify(206, notification);
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
}
DynamicReceiverActivity
public class DynamicReceiverActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_dynamic_broadcast_receiver);
DynamicReceiver.register(this);
this.findViewById(R.id.dynamic_broadcast_receiver_bt).setOnClickListener(this);
}
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.dynamic_broadcast_receiver_bt:
Intent intent = new Intent(DynamicReceiver.INTENT_ACTION);
intent.putExtra(DynamicReceiver.DYNAMIC_MESSAGE, UUID.randomUUID().toString());
this.sendBroadcast(intent);
break;
}
}
}
要做一个这样工作: Activity
发送广播,BroadcastReceiver
接收到了广播,启动Service
去开启一个AsyncTask
下载图片,下载好图片再发送数据给BroadcastReceiver
,广播再将数据带给Activity
,渲染到ImageView
上,最后再保存相册。
参考了如下:
DownloadReceiverActivity
public class DownloadReceiverActivity extends AppCompatActivity implements View.OnClickListener {
private static final String OBJECT_IMAGE_URL = "http://img.blog.csdn.net/20150913233900119";
private Button downloadBT;
private ImageView downloadIV;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_download_broadcast_receiver);
this.registerReceiver();
this.initViews();
this.initListeners();
}
private void registerReceiver() {
DownloadReceiver downloadReceiver = new DownloadReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(DownloadReceiver.INTENT_ACTION);
this.registerReceiver(downloadReceiver, intentFilter);
}
private void initViews() {
TextView downloadTV = (TextView) this.findViewById(R.id.down_broadcast_image_tv);
downloadTV.setText(OBJECT_IMAGE_URL);
this.downloadBT = (Button) this.findViewById(R.id.down_broadcast_start_bt);
this.downloadIV = (ImageView) this.findViewById(R.id.down_broadcast_image_iv);
}
private void initListeners() {
this.downloadBT.setOnClickListener(this);
}
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.down_broadcast_start_bt:
v.setEnabled(false);
Intent intent = new Intent(DownloadReceiver.INTENT_ACTION);
intent.putExtra(DownloadReceiver.INTENT_DATA_IMAGE_URL, OBJECT_IMAGE_URL);
intent.putExtra(DownloadReceiver.INTENT_TYPE, DownloadReceiver.TYPE_DOWNLOAD_START);
this.sendBroadcast(intent);
break;
}
}
/**
* 下载广播
*/
public class DownloadReceiver extends BroadcastReceiver {
public static final String INTENT_ACTION = "com.camnter.android.intent.download";
public static final String INTENT_TYPE = "type";
public static final String INTENT_DATA_IMAGE_URL = "image_url";
public static final String INTENT_DATA_IMAGE_PATH = "image_path";
public static final int TYPE_DOWNLOAD_START = 2061;
public static final int TYPE_DOWNLOAD_SUCCESS = 2062;
public static final int TYPE_DOWNLOAD_FAILURE = 2063;
@Override
public void onReceive(Context context, Intent intent) {
int type = intent.getIntExtra(INTENT_TYPE, -1);
if (type == -1) return;
switch (type) {
case TYPE_DOWNLOAD_START: {
String url = intent.getStringExtra(INTENT_DATA_IMAGE_URL);
DownloadIntentService.startActionDownload(context, url);
break;
}
case TYPE_DOWNLOAD_SUCCESS: {
String imageFilePath = intent.getStringExtra(INTENT_DATA_IMAGE_PATH);
/**
* 设置按钮可用,并隐藏Dialog
*/
DownloadReceiverActivity.this.downloadBT.setEnabled(true);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int screenWidth = metrics.widthPixels;
int screenHeight = metrics.heightPixels;
/**
* ImageUtil.decodeScaleImage 解析图片
*/
Bitmap bitmap = ImageUtil.decodeScaleImage(imageFilePath, screenWidth, screenHeight);
DownloadReceiverActivity.this.downloadIV.setImageBitmap(bitmap);
/**
* 保存图片到相册
*/
String imageName = System.currentTimeMillis() + ".jpg";
MediaStore.Images.Media.insertImage(DownloadReceiverActivity.this.getApplicationContext().getContentResolver(), bitmap, imageName, "camnter");
Toast.makeText(DownloadReceiverActivity.this, "已保存:" + imageName, Toast.LENGTH_LONG).show();
break;
}
case TYPE_DOWNLOAD_FAILURE: {
DownloadReceiverActivity.this.downloadBT.setEnabled(true);
break;
}
}
}
}
}
DownloadIntentService
public class DownloadIntentService extends IntentService {
// TODO: Rename actions, choose action names that describe tasks that this
// IntentService can perform, e.g. ACTION_FETCH_NEW_ITEMS
private static final String ACTION_DOWNLOAD = "com.camnter.newlife.service.action.download";
// TODO: Rename parameters
private static final String IMAGE_URL = "com.camnter.newlife.service.extra.image.url";
/**
* Starts this service to perform action Foo with the given parameters. If
* the service is already performing a task this action will be queued.
*
* @see IntentService
*/
// TODO: Customize helper method
public static void startActionDownload(Context context, String url) {
Intent intent = new Intent(context, DownloadIntentService.class);
intent.setAction(ACTION_DOWNLOAD);
intent.putExtra(IMAGE_URL, url);
context.startService(intent);
}
public DownloadIntentService() {
super("DownIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_DOWNLOAD.equals(action)) {
final String url = intent.getStringExtra(IMAGE_URL);
this.handleActionDownload(url);
}
}
}
/**
* Handle action Download in the provided background thread with the provided
* parameters.
*/
private void handleActionDownload(String url) {
// TODO: Handle action Download
new DownloadImageAsyncTask(this).execute(url);
}
/**
* 下载图片异步任务
*/
public class DownloadImageAsyncTask extends AsyncTask<String, Integer, String> {
private Service service;
private String localFilePath;
public DownloadImageAsyncTask(Service service) {
super();
this.service = service;
}
/**
* 对应AsyncTask第一个参数
* 异步操作,不在主UI线程中,不能对控件进行修改
* 可以调用publishProgress方法中转到onProgressUpdate(这里完成了一个handler.sendMessage(...)的过程)
*
* @param params The parameters of the task.
* @return A result, defined by the subclass of this task.
* @see #onPreExecute()
* @see #onPostExecute
* @see #publishProgress
*/
@Override
protected String doInBackground(String... params) {
URL fileUrl = null;
try {
fileUrl = new URL(params[0]);
} catch (MalformedURLException e) {
e.printStackTrace();
}
if (fileUrl == null) return null;
try {
HttpURLConnection connection = (HttpURLConnection) fileUrl.openConnection();
connection.setRequestMethod("GET");
connection.setDoInput(true);
connection.connect();
//计算文件长度
int lengthOfFile = connection.getContentLength();
/**
* 不存在SD卡,就放到缓存文件夹内
*/
File cacheDir = this.service.getCacheDir();
File downloadFile = new File(cacheDir, UUID.randomUUID().toString() + ".jpg");
this.localFilePath = downloadFile.getPath();
if (!downloadFile.exists()) {
File parent = downloadFile.getParentFile();
if (parent != null) parent.mkdirs();
}
FileOutputStream output = new FileOutputStream(downloadFile);
InputStream input = connection.getInputStream();
InputStream bitmapInput = connection.getInputStream();
//下载
byte[] buffer = new byte[1024];
int len;
long total = 0;
// 计算进度
while ((len = input.read(buffer)) > 0) {
total += len;
this.publishProgress((int) ((total * 100) / lengthOfFile));
output.write(buffer, 0, len);
}
output.close();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 对应AsyncTask第三个参数 (接受doInBackground的返回值)
* 在doInBackground方法执行结束之后在运行,此时已经回来主UI线程当中 能对UI控件进行修改
*
* @param string The result of the operation computed by {@link #doInBackground}.
* @see #onPreExecute
* @see #doInBackground
* @see #onCancelled(Object)
*/
@Override
protected void onPostExecute(String string) {
super.onPostExecute(string);
Intent intent = new Intent(DownloadReceiverActivity.DownloadReceiver.INTENT_ACTION);
intent.putExtra(
DownloadReceiverActivity.DownloadReceiver.INTENT_TYPE,
DownloadReceiverActivity.DownloadReceiver.TYPE_DOWNLOAD_SUCCESS
);
intent.putExtra(
DownloadReceiverActivity.DownloadReceiver.INTENT_DATA_IMAGE_PATH,
this.localFilePath
);
DownloadIntentService.this.sendBroadcast(intent);
}
/**
* 对应AsyncTask第二个参数
* 在doInBackground方法当中,每次调用publishProgress方法都会中转(handler.sendMessage(...))到onProgressUpdate
* 在主UI线程中,可以对控件进行修改
*
* @param values The values indicating progress.
* @see #publishProgress
* @see #doInBackground
*/
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
/**
* 运行在主UI线程中,此时是预执行状态,下一步是doInBackground
*
* @see #onPostExecute
* @see #doInBackground
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
}
/**
* <p>Applications should preferably override {@link #onCancelled(Object)}.
* This method is invoked by the default implementation of
* {@link #onCancelled(Object)}.</p>
* <p/>
* <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
* {@link #doInBackground(Object[])} has finished.</p>
*
* @see #onCancelled(Object)
* @see #cancel(boolean)
* @see #isCancelled()
*/
@Override
protected void onCancelled() {
super.onCancelled();
Intent intent = new Intent(DownloadReceiverActivity.DownloadReceiver.INTENT_ACTION);
intent.putExtra(
DownloadReceiverActivity.DownloadReceiver.INTENT_TYPE,
DownloadReceiverActivity.DownloadReceiver.TYPE_DOWNLOAD_FAILURE
);
DownloadIntentService.this.sendBroadcast(intent);
}
}
}
AndroidManifest.xml
<service android:name=".service.DownloadIntentService" />
效果图
标签:
原文地址:http://blog.csdn.net/qq_16430735/article/details/49995443