4

我正在使用应用程序上下文在后台创建一个 Android WebView,以便在我需要显示它时加载并准备好它。我在需要时使用 addView 将它附加到我的 Activity 中。这通常效果很好,但是当我尝试打开 HTML 选择下拉列表时,我遇到了崩溃:

android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
  at android.view.ViewRootImpl.setView(ViewRootImpl.java:540)
  at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:259)
  at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)
  at android.app.Dialog.show(Dialog.java:286)
  at com.android.org.chromium.content.browser.input.SelectPopupDialog.show(SelectPopupDialog.java:217)
  at com.android.org.chromium.content.browser.ContentViewCore.showSelectPopup(ContentViewCore.java:2413)
  at com.android.org.chromium.base.SystemMessageHandler.nativeDoRunLoopOnce(Native Method)
  at com.android.org.chromium.base.SystemMessageHandler.handleMessage(SystemMessageHandler.java:27)
  at android.os.Handler.dispatchMessage(Handler.java:102)
  at android.os.Looper.loop(Looper.java:136)
  at android.app.ActivityThread.main(ActivityThread.java:5017)
  at java.lang.reflect.Method.invokeNative(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:515)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
  at dalvik.system.NativeStart.main(Native Method)

我假设这是因为我使用 ApplicationContext 创建了 WebView。我的问题是:有没有办法解决这个问题?有没有办法将现有的 WebView “附加”到不同的 Activity 或 Window 以便可以创建 Dialog?有没有办法通过在运行时更改上下文来使用反射来“破解”这个?

编辑:如下所示,我使用MutableContextWrapper进行了测试,它似乎很好地解决了这个问题!

4

1 回答 1

3

所以,我们实际上遇到了同样的问题(WebView在保留中使用 aFragment来防止页面重新加载,同时不泄漏Activity上下文),简短的回答似乎是没有真正干净的方法可以做到这一点。我什至尝试了一个自定义WebView子类,它返回 hostActivity的窗口令牌而不是WebView自己的令牌,但没有成功。

正如您所建议的,我们最终使用反射来修改底层上下文:

public static boolean setContext(View v, Context ctx) {
    try {
        final Field contextField = View.class.getDeclaredField("mContext");
        contextField.setAccessible(true);
        contextField.set(v, ctx);
        return (v.getContext() == ctx);
    } catch (IllegalAccessException | NoSuchFieldException e) {
        Log.e(TAG, String.valueOf(e), e);
        return false;
    }
}

它只是mContext在实例上设置字段,View如果成功修改则返回 true。但是,我最近看到了另一个建议(我还没有测试过这个,所以 YMMV)使用MutableContextWrapper. 所以你会WebViewActivity Context, 包裹在MutableContextWrapper. 然后,当您需要释放先前的引用时,您会将WebView'sContext转换为 a MutableContextWrapper,然后将 base 设置Context为 new Activity。所以像这样膨胀布局:

MutableContextWrapper contextWrapper = new MutableContextWrapper(activity);

WebView webView = (WebView) LayoutInflater.from(contextWrapper)
        .inflate(R.layout.your_webview_layout, theParent, false);

然后,重新附加到一个新的Activity

if (webView.getContext() instanceof MutableContextWrapper) {
    ((MutableContextWrapper) webView.getContext()).setBaseContext(newActivity);
}
于 2015-01-19T17:13:36.927 回答