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. 参考
Android四大组件:BroadcastReceiver史上最全面解析
What is the Context passed into onReceive() of a BroadcastReceiver?
能否在BroadcastReceiver里创建AlertDialog并显示?