• Android 四大组件之再论BroadCast


    BroadCast 是android提供的跨进程通讯的有一利器。

    1.异步执行onReceiver

        @Nullable
        public abstract Intent registerReceiver(BroadcastReceiver receiver,
                IntentFilter filter, @Nullable String broadcastPermission,
                @Nullable Handler scheduler);

    这是context里面注册广播的API,duplex2个我们不常用的东东。

    我们分别来讨论这2个东西。

    先讨论异步handler。

    如果我们传入一个handler,会怎样?我们所有的onReceiver是运行在主线程吗?

    package com.joyfulmath.samples.broadcastsample;
    
    import android.app.Activity;
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.os.Handler;
    import android.os.HandlerThread;
    import android.os.Message;
    
    import com.joyfulmath.servicesample.R;
    import com.joyfulmath.servicesample.TraceLog;
    
    import org.androidannotations.annotations.AfterViews;
    import org.androidannotations.annotations.Click;
    import org.androidannotations.annotations.EActivity;
    
    /**
     * Created by Administrator on 2016/10/23 0023.
     */
    @EActivity(R.layout.activity_main)
    public class BcSampleActivity extends Activity {
    
        HandlerThread mThread = null;
        Handler nHandler = null;
        BroadCastAAA aReceiver = new BroadCastAAA();
    
        @Override
        protected void onResume() {
            super.onResume();
            TraceLog.i();
    
    
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            TraceLog.i();
            unregisterReceiver(aReceiver);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            aReceiver = null;
        }
    
        @AfterViews
        void initViews() {
            TraceLog.i();
            mThread = new HandlerThread("samples") {
                @Override
                protected void onLooperPrepared() {
                    nHandler = new Handler(getLooper()) {
                        @Override
                        public void dispatchMessage(Message msg) {
                            TraceLog.i(msg.toString());
                            super.dispatchMessage(msg);
                        }
    
                        @Override
                        public void handleMessage(Message msg) {
                            TraceLog.i();
                            dispatchMsg(msg);
                        }
                    };
                    IntentFilter filter = new IntentFilter("com.joyfulmath.sample.broadcast.action");
                    registerReceiver(aReceiver, filter, null, nHandler);
                }
            };
            mThread.start();
        }
    
        @Click(R.id.btn_connect)
        void onBtnClick() {
            TraceLog.i();
    //        nHandler.sendEmptyMessage(0x01);
            sendBroadcast(new Intent("com.joyfulmath.sample.broadcast.action"));
        }
    
        void dispatchMsg(Message msg) {
            switch (msg.what) {
                case 0x1:
                    TraceLog.i();
                    break;
            }
            TraceLog.i();
        }
    
        public class BroadCastAAA extends BroadcastReceiver {
    
            @Override
            public void onReceive(Context context, Intent intent) {
                TraceLog.i();
            }
        }
    }
    broadcastsample

    通过上述代码,我们添加了一个运行在handlerThread里面的nHandler。

    log如下:

    10-22 22:32:21.332 16889-16889/com.joyfulmath.servicesample I/BcSampleActivity$override: onBtnClick:  [at (BcSampleActivity.java:79)]
    10-22 22:32:21.347 16889-21599/com.joyfulmath.servicesample I/BcSampleActivity$1$1$override: dispatchMessage: { when=-2ms callback=android.app.LoadedApk$ReceiverDispatcher$Args target=com.joyfulmath.samples.broadcastsample.BcSampleActivity$1$1 } [at (BcSampleActivity.java:59)]
    10-22 22:32:21.348 16889-21599/com.joyfulmath.servicesample I/BcSampleActivity$BroadCastAAA: onReceive:  [at (BcSampleActivity.java:100)]

    可以看到,onReceive没有运行在主线程,运行在哪里?就是nHandler的线程。

    还有一个:callback=android.app.LoadedApk$ReceiverDispatcher$Args 这个是什么,看名字可以猜测,这个就是分发Receiver的东西。

    可以看这个registerReceiver的注释:

    Handler identifying the thread that will receive
         *      the Intent.  If null, the main thread of the process will be used.

    所以onReceiver 是可以运行在子线程的。这里有个问题:public class BroadCastAAA extends BroadcastReceiver

    是内部类,如果运行在子线程,会不会导致内存泄露?

    这些问题后续在分析.

    2.Permission

     权限管理分2块:

    public abstract void sendBroadcast(Intent intent,
                @Nullable String receiverPermission);

    这里有个permission

    还有就是上一节提到的permission。

    第一种:

            sendBroadcast(new Intent("com.joyfulmath.sample.broadcast.action"), Contants.BROADCAST_ACTION);
    Permission Denial: receiving Intent { act=com.joyfulmath.sample.broadcast.action flg=0x10 } to ProcessRecord{39c0edf1 2286:com.joyfulmath.servicesample/u0a62} (pid=2286, uid=10062) requires com.joyfulmath.samples.broadcast.action due to sender com.joyfulmath.servicesample (uid 10062)

    可以看到报permission denial。就是没有权限。报的错误是基于进程的。

    permission定义在发送app,接受app需要userpermission。

    当然解决这个错误就是在manifest里面use这条permission。

    第二种:

    registerReceiver(aReceiver, filter, "com.joyfulmath.samples.broadcast.permission", nHandler);

    静态注册也一样。

    这条语句的意思是:只有含有permisson的progress发送的广播才能被收到。

    permission定义在receiver中,发送app需要userpermission才能收到。

    3.Local Broadcast:App应用内广播(此处的App应用以App应用进程为界)

    相比于全局广播,App应用内广播优势体现在:

    1.安全性更高;

    2.更加高效。

    为此,Android v4兼容包中给出了封装好的LocalBroadcastManager类,用于统一处理App应用内的广播问题,使用方式上与通常的全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将主调context变成了LocalBroadcastManager的单一实例。

    4.不同注册方式的广播接收器回调onReceive(context, intent)中的context具体类型

    1).对于静态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是ReceiverRestrictedContext;

    2).对于全局广播的动态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是Activity Context;

    3).对于通过LocalBroadcastManager动态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是Application Context。

    注:对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册的ContextReceiver才有可能接收到(静态注册或其他方式动态注册的ContextReceiver是接收不到的)。

    5.不同Android API版本中广播机制相关API重要变迁

    1).Android5.0/API level 21开始粘滞广播和有序粘滞广播过期,以后不再建议使用;

    2).”静态注册的广播接收器即使app已经退出,主要有相应的广播发出,依然可以接收到,但此种描述自Android 3.1开始有可能不再成立“

    Android 3.1开始系统在Intent与广播相关的flag增加了参数,分别是FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES。

    FLAG_INCLUDE_STOPPED_PACKAGES:包含已经停止的包(停止:即包所在的进程已经退出)

    FLAG_EXCLUDE_STOPPED_PACKAGES:不包含已经停止的包

    主要原因如下:

    自Android3.1开始,系统本身则增加了对所有app当前是否处于运行状态的跟踪。在发送广播时,不管是什么广播类型,系统默认直接增加了值为FLAG_EXCLUDE_STOPPED_PACKAGES的flag,导致即使是静态注册的广播接收器,对于其所在进程已经退出的app,同样无法接收到广播。

    详情参加Android官方文档:http://developer.android.com/about/versions/android-3.1.html#launchcontrols

    由此,对于系统广播,由于是系统内部直接发出,无法更改此intent flag值,因此,3.1开始对于静态注册的接收系统广播的BroadcastReceiver,如果App进程已经退出,将不能接收到广播。

    但是对于自定义的广播,可以通过复写此flag为FLAG_INCLUDE_STOPPED_PACKAGES,使得静态注册的BroadcastReceiver,即使所在App进程已经退出,也能能接收到广播,并会启动应用进程,但此时的BroadcastReceiver是重新新建的。

    1 Intent intent = new Intent();
    2 intent.setAction(BROADCAST_ACTION);
    3 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
    4 intent.putExtra("name", "qqyumidi");
    5 sendBroadcast(intent);

    注1:对于动态注册类型的BroadcastReceiver,由于此注册和取消注册实在其他组件(如Activity)中进行,因此,不受此改变影响。

    注2:在3.1以前,相信不少app可能通过静态注册方式监听各种系统广播,以此进行一些业务上的处理(如即时app已经退出,仍然能接收到,可以启动service等..),3.1后,静态注册接受广播方式的改变,将直接导致此类方案不再可行。于是,通过将Service与App本身设置成不同的进程已经成为实现此类需求的可行替代方案。

     

  • 相关阅读:
    如何设置Vmware下Linux系统全屏显示
    LCD显示——点阵字体
    Android代码优化----Application节点的模板写法及UI工具类
    Android代码优化----PullToRefresh+universal-image-loader实现从网络获取数据并刷新
    android代码优化----ListView中自定义adapter的封装(ListView的模板写法)
    Android自定义控件----RadioGroup实现APP首页底部Tab的切换
    第一次使用Android Studio时你应该知道的一切配置(三):gradle项目构建
    第一次使用Android Studio时你应该知道的一切配置(二):新建一个属于自己的工程并安装Genymotion模拟器
    第一次使用Android Studio时你应该知道的一切配置
    Android代码规范----按钮单击事件的四种写法
  • 原文地址:https://www.cnblogs.com/deman/p/5989169.html
Copyright © 2020-2023  润新知