Android 不依赖于Activity的全局对话框Dialog

正常初始化对话框的写法

一般的Dialog对象初始化都需要依赖于Activity,如下

1
2
3
4
new AlertDialog.Builder(activity)
.setTitle("野猿新一")
.setMessage("我是对话框内容啦")
.show();

Click and drag to move

BroadcastReceiver如果是在Activity中注册的,用onReceive(Context context, Intent intent)方法的context参数来初始化Dialog是可以正常弹出对话框的。因为这里的context其实是一个Activity对象,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (context instanceof Activity) {
new AlertDialog.Builder(context)
.setTitle("野猿新一")
.setMessage("我是对话框内容啦")
.show();
}
}
}, new IntentFilter("com.him.action"));
}
}

Click and drag to move

会报错的写法

虽说AlertDialog.Builder(Context context)的参数是Context,但是如果传入的是非Activity的Context,比如说Application,就会报如下所示的错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
at android.view.ViewRootImpl.setView(ViewRootImpl.java:700)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:289)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:85)
at android.app.Dialog.show(Dialog.java:298)
at android.app.AlertDialog.show(AlertDialog.java:1128)
at android.app.AlertDialog$Builder.show(AlertDialog.java:1008)
at com.him.autosizingtest.MainActivity$1.onClick(MainActivity.java:21)
at android.view.View.performClick(View.java:4918)
at android.view.View$PerformClick.run(View.java:20399)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5869)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1019)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:814)

Click and drag to move

所以以下的几种写法都会报错

1
2
3
4
5
6
7
8
9
new AlertDialog.Builder(activity.getApplication())
.setTitle("野猿新一")
.setMessage("我是对话框内容啦")
.show();

new AlertDialog.Builder(activity.getApplicationContext())
.setTitle("野猿新一")
.setMessage("我是对话框内容啦")
.show();

Click and drag to move

BroadcastReceiver如果是在Manifest中声明的,用onReceive(Context context, Intent intent)方法的context参数来初始化Dialog,一样还会报Unable to add window – token null is not for an application错误。所以如下写法也不可行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
new AlertDialog.Builder(context)
.setTitle("野猿新一")
.setMessage("我是对话框内容啦")
.show();
}
}

// 在manifest中声明
<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="com.him.action"/>
</intent-filter>
</receiver>

Click and drag to move

全局Dialog

但是如果说有些情况下获取不到Activity对象,但是又想弹出Dialog呢?这时候用全局的Dialog是可以实现的。

只需要设置dialog为WindowManager.LayoutParams.TYPE_SYSTEM_ALERT类型

然后添加android.permission.SYSTEM_ALERT_WINDOW权限

1
2
3
4
5
6
7
8
9
10
Dialog dialog = new AlertDialog.Builder(activity.getApplicationContext())
.setTitle("野猿新一")
.setMessage("我是对话框内容啦")
.create();
// 增加这句代码
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
dialog.show();

// 然后在manifest中添加权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

Click and drag to move