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" />

效果图

download_broadcast_receiver


1 0
原创粉丝点击