广播onReceive()方法的context类型探究

1. 铺垫

1). 非Activity类型的context并没有所谓的任务栈;

2). 出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,我们只能使用Activity类型的context,否则将会出错。

2. 动态、静态注册的广播,其onReceive()方法里的context类型

1). 静态注册广播,如果没声明android:process,那么BroadcastReceiver和Activity是在同一个进程的;onReceive()里传进来的context的类型最初以为是Application,但打印发现是android.app.ReceiverRestrictedContext,。此context非Activity类型,不可直接用来构造AlertDialog。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.e("main", "pid = " + Process.myPid() + ", tid = "+ Process.myTid());
        Thread thread = Thread.currentThread();
        Log.e("main", "tid = " + thread.getId() + ", name = "+ thread.getName());
    }

    //static receiver
    public static class MyReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e("static receiver", "pid = " + Process.myPid() + ", tid = "+ Process.myTid());
            Thread thread = Thread.currentThread();
            Log.e("static receiver", "tid = " + thread.getId() + ", name = "+ thread.getName());

            Log.e("static receiver", "Context class = " + context.getClass().getName());
            Log.e("static receiver", "Application Context class = "
                    + context.getApplicationContext().getClass().getName());
        }
    }

输出结果:


2). 动态注册中BroadcastReceiver和Activity也是运行在同一个进程中的,因此调用sendBraodcast()时,传入onReceive()方法里的Context对象context其实就是调用sendBroadcast()的Activty对象,这里的context可以直接用来构造AlertDialog。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        IntentFilter filter = new IntentFilter("my.action_LYL");
        registerReceiver(receiver, filter);

        Log.e("main", "pid = " + Process.myPid() + ", tid = "+ Process.myTid());
        Thread thread = Thread.currentThread();
        Log.e("main", "tid = " + thread.getId() + ", name = "+ thread.getName());
    }

   //dynamic receiver
    private BroadcastReceiver receiver = new BroadcastReceiver(){
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e("dynamic receiver", "pid = " + Process.myPid() + ", tid = "+ Process.myTid());
            Thread thread = Thread.currentThread();
            Log.e("dynamic receiver", "tid = " + thread.getId() + ", name = "+ thread.getName());

            Log.e("dynamic receiver", "Activity Context class  = "
                    + MainActivity.this.getClass().getName());
            Log.e("dynamic receiver", "Context class  = " + context.getClass().getName());

            Log.e("dynamic receiver", "Application Context class  = "
                    + context.getApplicationContext().getClass().getName());
        }
    };

输出结果:

故:
静态注册时,onRecice()方法的context不是Activity类型,故此时不能在接收器中弹出对话框。
动态注册时,BroadcastReceiver的onRecice()方法的context来自Activity,可弹框提示。

同时,在源码中,我们也可以看到,在onReceive()方法前有这么一句注释:* @param context The Context in which the receiver is running.


    /**
     * This method is called when the BroadcastReceiver is receiving an Intent
     * broadcast.  During this time you can use the other methods on
     * BroadcastReceiver to view/modify the current result values.  This method
     * is always called within the main thread of its process, unless you
     * explicitly asked for it to be scheduled on a different thread using
     * {@link android.content.Context#registerReceiver(BroadcastReceiver,
     * IntentFilter, String, android.os.Handler)}. When it runs on the main
     * thread you should
     * never perform long-running operations in it (there is a timeout of
     * 10 seconds that the system allows before considering the receiver to
     * be blocked and a candidate to be killed). You cannot launch a popup dialog
     * in your implementation of onReceive().
     *
     * @param context The Context in which the receiver is running.
     * @param intent The Intent being received.
     */
    public abstract void onReceive(Context context, Intent intent);

在onReceive()方法的解释中,不要在广播的onReceive()方法中执行耗时操作,也不要添加过多的逻辑。因为广播接收器中不允许开启线程,当onReceive()运行较长时间而没有结束,程序就会报错。广播接收器的作用是用于打开其他组件。

Android四大组件:BroadcastReceiver史上最全面解析一文中,作者介绍了OnReceive()的context类型:

对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的:
    对于静态注册(全局+应用内广播),回调onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext;
    对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity Context;
    对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Application Context。
    对于应用内广播的动态注册(非LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Activity Context;

3. 在打印线程id的时候,使用到了Process.myTid() 和Thread.currentThread().getId()

Android: Process.myTid() VS Thread.currentThread().getId()一文中,作者对二者做出了解释:

Process.myTid()返回Linux内核给出的线程ID。
Thread.getId(),对每个线程来说,只是“java层”静态自增长的long型数据

4. 代码

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        IntentFilter filter = new IntentFilter("my.action_LYL");
        registerReceiver(receiver, filter);

        Log.e("main", "pid = " + Process.myPid() + ", tid = "+ Process.myTid());
        Thread thread = Thread.currentThread();
        Log.e("main", "tid = " + thread.getId() + ", name = "+ thread.getName());
    }

    @Override
    protected void onDestroy() {
        unregisterReceiver(receiver);
        super.onDestroy();
    }

    public void send_bc(View v){
        Intent intent = new Intent("my.action_LYL");
        intent.setPackage("test.com.testforbc_20180718");//static register must set package name
        sendBroadcast(intent);
    }

    //dynamic receiver
    private BroadcastReceiver receiver = new BroadcastReceiver(){
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e("dynamic receiver", "pid = " + Process.myPid() + ", tid = "+ Process.myTid());
            Thread thread = Thread.currentThread();
            Log.e("dynamic receiver", "tid = " + thread.getId() + ", name = "+ thread.getName());

            Log.e("dynamic receiver", "Activity Context class  = "
                    + MainActivity.this.getClass().getName());
            Log.e("dynamic receiver", "Context class  = " + context.getClass().getName());

            Log.e("dynamic receiver", "Application Context class  = "
                    + context.getApplicationContext().getClass().getName());
        }
    };

    //static receiver
    public static class MyReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e("static receiver", "pid = " + Process.myPid() + ", tid = "+ Process.myTid());
            Thread thread = Thread.currentThread();
            Log.e("static receiver", "tid = " + thread.getId() + ", name = "+ thread.getName());

            Log.e("static receiver", "Context class = " + context.getClass().getName());
            Log.e("static receiver", "Application Context class = "
                    + context.getApplicationContext().getClass().getName());
        }
    }
}

输出结果:

5. 参考

Context是什么,怎么用

Android中,Context,什么是Context?

Android四大组件:BroadcastReceiver史上最全面解析

What is the Context passed into onReceive() of a BroadcastReceiver?

能否在BroadcastReceiver里创建AlertDialog并显示?

Context都没弄明白,还怎么做Android开发?

Broadcast的onReceive方法中弹出AlertDialog

Android: Process.myTid() VS Thread.currentThread().getId()

  • 13
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值