Android - 如何在使用应用程序上下文创建 WebView 后将其附加到 Activity [英] Android - How can I Attach a WebView to an Activity after creating it with the Application Context

查看:22
本文介绍了Android - 如何在使用应用程序上下文创建 WebView 后将其附加到 Activity的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

<块引用>

android.view.WindowManager$BadTokenException: 无法添加窗口 -- 令牌 null 不适用于应用程序在 android.view.ViewRootImpl.setView(ViewRootImpl.java:540)在 android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:259)在 android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)在 android.app.Dialog.show(Dialog.java:286)在 com.android.org.chromium.content.browser.input.SelectPopupDialog.show(SelectPopupDialog.java:217)在 com.android.org.chromium.content.browser.ContentViewCore.showSelectPopup(ContentViewCore.java:2413)在 com.android.org.chromium.base.SystemMessageHandler.nativeDoRunLoopOnce(本机方法)在 com.android.org.chromium.base.SystemMessageHandler.handleMessage(SystemMessageHandler.java:27)在 android.os.Handler.dispatchMessage(Handler.java:102)在 android.os.Looper.loop(Looper.java:136)在 android.app.ActivityThread.main(ActivityThread.java:5017)在 java.lang.reflect.Method.invokeNative(Native Method)在 java.lang.reflect.Method.invoke(Method.java:515)在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)在 dalvik.system.NativeStart.main(本机方法)

我假设这是因为我使用 ApplicationContext 创建了 WebView.我的问题是:有没有办法解决这个问题?有什么方法可以将现有的 WebView附加"到不同的活动或窗口,以便可以创建对话框?有没有办法通过在运行时更改上下文来使用反射将其破解"在一起?

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

解决方案

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

我们最终按照您的建议使用反射来修改底层上下文:

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

只是在 View 实例上设置 mContext 字段,如果成功修改则返回 true.但是,我最近看到了另一个建议(我还没有对此进行测试,所以 YMMV)使用 MutableContextWrapper.因此,您可以使用 Activity Context 来扩充 WebView,并包装在 MutableContextWrapper 中.然后,当您需要释放之前的引用时,您可以将该 WebViewContext 转换为 MutableContextWrapper,然后设置基Context 到新的 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);}

I am creating an Android WebView in the background using the Application Context so that it is loaded and ready when I need to display it. I attach it to my Activity using addView when it is needed. This mostly works great, however when I try to open an HTML select dropdown I get a crash:

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)

I'm assuming this is because I created the WebView with the ApplicationContext. My question is: is there any way to work around this issue? Is there any way to "attach" the existing WebView to a different Activity or Window so that the Dialog can be created? Is there any way to "hack" this together using Reflection by changing the Context at runtime?

EDIT: As suggested below I tested using a MutableContextWrapper and it appears to solve this problem nicely!

解决方案

So, we've actually run into the same issue (using a WebView in a retained Fragment to prevent page reloads, while not leaking an Activity context) and the short answer seems to be that there's no really clean way to do so. I've even tried a custom WebView subclass that returned the host Activity's window token instead of the WebView's own token, to no success.

We ended up using reflection, as you suggested, to modify the underlying context:

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;
    }
}

which just sets the mContext field on the View instance and returns true if it was successfully modified. However, I saw another suggestion recently (and I haven't tested this, so YMMV) to use MutableContextWrapper. So you would inflate the WebView with the Activity Context, wrapped in a MutableContextWrapper. Then, when you need to release the previous reference, you'd cast that WebView's Context to a MutableContextWrapper, and then set the base Context to the new Activity. So something like this to inflate the layout:

MutableContextWrapper contextWrapper = new MutableContextWrapper(activity);

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

Then, to re-attach to a new Activity:

if (webView.getContext() instanceof MutableContextWrapper) {
    ((MutableContextWrapper) webView.getContext()).setBaseContext(newActivity);
}

这篇关于Android - 如何在使用应用程序上下文创建 WebView 后将其附加到 Activity的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆