4

アプリケーションコンテキストを使用してバックグラウンドで Android WebView を作成しているため、表示する必要があるときにロードして準備ができています。必要に応じて addView を使用してアクティビティにアタッチします。これはほとんどの場合うまく機能しますが、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 を別のアクティビティまたはウィンドウに「アタッチ」する方法はありますか? 実行時にコンテキストを変更して、リフレクションを使用してこれを一緒に「ハック」する方法はありますか?

編集: 以下で提案されているように、MutableContextWrapperを使用してテストしたところ、この問題をうまく解決しているようです!

4

1 に答える 1

3

したがって、実際に同じ問題に遭遇しました (コンテキストをリークせずにページのリロードを防ぐためWebViewに保持された aを使用します)。ホストの独自のトークンではなく、ホストのウィンドウ トークンを返すカスタム サブクラスを試してみましたが、成功しませんでした。FragmentActivityWebViewActivityWebView

あなたが示唆したように、根底にあるコンテキストを変更するためにリフレクションを使用することになりました:

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。したがって、 で囲まれたWebViewを で膨らませます。次に、以前の参照を解放する必要がある場合は、それを にキャストし、ベースを新しい に設定します。レイアウトを膨らませるには、次のようにします。Activity ContextMutableContextWrapperWebViewContextMutableContextWrapperContextActivity

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 に答える