39.Android BroadcastReceiver
来源:互联网 发布:成都网络理政平台 编辑:程序博客网 时间:2024/05/29 13:45
39.Android BroadcastReceiver
- Android BroadcastReceiver
- Android BroadcastReceiver 介绍
- Android BroadcastReceiver 功能
- Android BroadcastReceiver 浅淡机制
- 静态BroadcastReceiver 实现
- 动态BroadcastReceiver 实现
- BroadcastReceiver 启动 Service
Android BroadcastReceiver 介绍
现在的BroadcastReceiver好多都用在了做推送上。比如极光推送、信鸽推送等等。
推送用BroadcastReceiver发多都是提示一个Notification。服务端发送推送,推送Android SDK内的通信机制接受到了推送,然后发送广播通知,BroadcastReceiver接收到了通知,进而区分通知类型,执行对应的操作,很多时候,我们都是看到一个Notification(QQ消息通知、微信消息通知等等)。
Android BroadcastReceiver 功能
就是简单的三句话。
- 1.Android的跨组件通信
- 2.Android的跨线程通信
- 3.Android的跨进程(App)通信
为什么会这样子呢?请见下面的浅淡机制。
Android BroadcastReceiver 浅淡机制
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 实现
静态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 实现
动态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; } }}
BroadcastReceiver 启动 Service
要做一个这样工作: Activity
发送广播,BroadcastReceiver
接收到了广播,启动Service
去开启一个AsyncTask
下载图片,下载好图片再发送数据给BroadcastReceiver
,广播再将数据带给Activity
,渲染到ImageView
上,最后再保存相册。
参考了如下:
- 1. AsyncTask模板
- 2. Android 下载图片保存到相册
- 3. Android Service 及 AIDL
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" />
效果图
- 39.Android BroadcastReceiver
- Android:BroadcastReceiver
- android BroadcastReceiver
- Android BroadcastReceiver
- BroadcastReceiver Android
- Android BroadcastReceiver
- android BroadcastReceiver
- Android BroadCastReceiver
- Android BroadcastReceiver
- Android BroadcastReceiver
- android BroadcastReceiver
- android BroadcastReceiver
- Android BroadcastReceiver
- Android BroadcastReceiver
- android BroadcastReceiver
- Android BroadcastReceiver
- Android BroadcastReceiver
- Android BroadcastReceiver
- 忘记了mysql登陆密码
- Revit二次开发——使用IExternalApplication定制UI
- 这是一个简单的洗牌实例
- javascipt学习第2节---函数
- 获取各种窗体的高度
- 39.Android BroadcastReceiver
- SIGPIPE信号引起程序退出
- 写入SDCard
- 使用matlab中的VideoReader函数读取,保存,显示,播放视频
- IDEA快捷键整理
- [转]Mysql支持的数据类型(总结)
- GCD简单使用
- SuperMap iClient如何使用WFS服务
- STL中map,multimap,hashmap的区别。