web-dev-qa-db-ja.com

androidウィンドウマネージャーに接続されていないビュー

次の例外がいくつかあります。

Java.lang.IllegalArgumentException: View not attached to window manager
at Android.view.WindowManagerImpl.findViewLocked(WindowManagerImpl.Java:355)
at Android.view.WindowManagerImpl.updateViewLayout(WindowManagerImpl.Java:191)
at Android.view.Window$LocalWindowManager.updateViewLayout(Window.Java:428)
at Android.app.Dialog.onWindowAttributesChanged(Dialog.Java:596)
at Android.view.Window.setDefaultWindowFormat(Window.Java:1013)
at com.Android.internal.policy.impl.PhoneWindow.access$700(PhoneWindow.Java:86)
at com.Android.internal.policy.impl.PhoneWindow$DecorView.drawableChanged(PhoneWindow.Java:1951)
at com.Android.internal.policy.impl.PhoneWindow$DecorView.fitSystemWindows(PhoneWindow.Java:1889)
at Android.view.ViewRoot.performTraversals(ViewRoot.Java:727)
at Android.view.ViewRoot.handleMessage(ViewRoot.Java:1633)
at Android.os.Handler.dispatchMessage(Handler.Java:99)
at Android.os.Looper.loop(Looper.Java:123)
at Android.app.ActivityThread.main(ActivityThread.Java:4338)
at Java.lang.reflect.Method.invokeNative(Native Method)
at Java.lang.reflect.Method.invoke(Method.Java:521)
at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:860)
at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:618)
at dalvik.system.NativeStart.main(Native Method)

私はそれをグーグルで調べて、ポップアップと画面の回転に関係していることがわかりましたが、私のコードへの参照はありません。

質問は次のとおりです。

  1. この問題がいつ起こっているかを正確に知る方法はありますか?
  2. 画面を回す以外に、このエラーをトリガーする別のイベントまたはアクションがありますか?
  3. どうすればこれを防ぐことができますか?
110
Daniel Benedykt

この問題は、画面の向きを変更すると、進行ダイアログを含むAsyncTaskが完了する前にアクティビティが終了するという問題でした。ダイアログをnull onPause()に設定し、AsyncTaskでこれをチェックしてから閉じることで、これを解決したようです。

@Override
public void onPause() {
    super.onPause();

    if ((mDialog != null) && mDialog.isShowing())
        mDialog.dismiss();
    mDialog = null;
}

...私のAsyncTaskで:

protected void onPreExecute() {
    mDialog = ProgressDialog.show(mContext, "", "Saving changes...",
            true);
}

protected void onPostExecute(Object result) {
   if ((mDialog != null) && mDialog.isShowing()) { 
        mDialog.dismiss();
   }
}
160
johnnyblizzard

この問題との戦いの後、私は最終的にこの回避策になりました:

/**
 * Dismiss {@link ProgressDialog} with check for nullability and SDK version
 *
 * @param dialog instance of {@link ProgressDialog} to dismiss
 */
public void dismissProgressDialog(ProgressDialog dialog) {
    if (dialog != null && dialog.isShowing()) {

            //get the Context object that was used to great the dialog
            Context context = ((ContextWrapper) dialog.getContext()).getBaseContext();

            // if the Context used here was an activity AND it hasn't been finished or destroyed
            // then dismiss it
            if (context instanceof Activity) {

                // Api >=17
                if (!((Activity) context).isFinishing() {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                        if (!((Activity) context).isDestroyed()) {
                            dismissWithExceptionHandling(dialog);
                        }
                    } else { 
                        // Api < 17. Unfortunately cannot check for isDestroyed()
                        dismissWithExceptionHandling(dialog);
                    }
                }
            } else
                // if the Context used wasn't an Activity, then dismiss it too
                dismissWithExceptionHandling(dialog);
        }
        dialog = null;
    }
}

/**
 * Dismiss {@link ProgressDialog} with try catch
 *
 * @param dialog instance of {@link ProgressDialog} to dismiss
 */
public void dismissWithExceptionHandling(ProgressDialog dialog) {
    try {
        dialog.dismiss();
    } catch (final IllegalArgumentException e) {
        // Do nothing.
    } catch (final Exception e) {
        // Do nothing.
    } finally {
        dialog = null;
    }
}

この問題のより良い解決策がなければ、優れた例外処理がうまく機能する場合があります。

8
blueware

Activityオブジェクトがぶらぶらしている場合は、isDestroyed()メソッドを使用できます。

Activity activity;

// ...

if (!activity.isDestroyed()) {
    // ...
}

これは、さまざまな場所で使用する非匿名のAsyncTaskサブクラスがある場合に便利です。

5
shawkinaw

ダイアログの表示と非表示を切り替えるカスタムの静的クラスを使用しています。このクラスは、1つのアクティビティだけでなく、他のアクティビティでも使用されています。今、あなたが説明した問題も私に現れ、私は解決策を見つけるために一晩滞在しました。

最後に解決策を紹介します!

ダイアログを表示または非表示にしたいが、どのアクティビティがダイアログに触れて開始したかわからない場合は、次のコードが役立ちます。

 static class CustomDialog{

     public static void initDialog(){
         ...
         //init code
         ...
     }

      public static void showDialog(){
         ...
         //init code for show dialog
         ...
     }

     /****This is your Dismiss dialog code :D*******/
     public static void dismissProgressDialog(Context context) {                
            //Can't touch other View of other Activiy..
            //http://stackoverflow.com/questions/23458162/dismiss-progress-dialog-in-another-activity-Android
            if ( (progressdialog != null) && progressdialog.isShowing()) {

                //is it the same context from the caller ?
                Log.w("ProgressDIalog dismiss", "the dialog is from"+progressdialog.getContext());

                Class caller_context= context.getClass();
                Activity call_Act = (Activity)context;
                Class progress_context= progressdialog.getContext().getClass();

                Boolean is_act= ( (progressdialog.getContext()) instanceof  Activity )?true:false;
                Boolean is_ctw= ( (progressdialog.getContext()) instanceof  ContextThemeWrapper )?true:false;

                if (is_ctw) {
                    ContextThemeWrapper cthw=(ContextThemeWrapper) progressdialog.getContext();
                    Boolean is_same_acivity_with_Caller= ((Activity)(cthw).getBaseContext() ==  call_Act )?true:false;

                    if (is_same_acivity_with_Caller){
                        progressdialog.dismiss();
                        progressdialog = null;
                    }
                    else {
                        Log.e("ProgressDIalog dismiss", "the dialog is NOT from the same context! Can't touch.."+((Activity)(cthw).getBaseContext()).getClass());
                        progressdialog = null;
                    }
                }


            }
        } 

 }
3
aimiliano

そのアクティビティのマニフェストに次を追加しました

Android:configChanges="keyboardHidden|orientation|screenLayout"
1
Rickey

上記の解決策は私にとってはうまくいきませんでした。だから私はProgressDialogをグローバルに取り、それをアクティビティに追加しました

@Override
    protected void onDestroy() {
        if (progressDialog != null && progressDialog.isShowing())
            progressDialog.dismiss();
        super.onDestroy();
    }

アクティビティが破棄された場合、ProgressDialogも破棄されます。

1

もう1つのオプションは、ダイアログでonAttachedToWindow()をオーバーライドしてダイアログをウィンドウにアタッチするまで非同期タスクを開始しないことです。これにより、常に非表示になります。

0
Nick Palmer

windowManagerのコードに従って(リンク here )、これは、更新しようとしているビュー(おそらくダイアログに属しますが、必須ではありません)は、ウィンドウの実際のルートに添付されなくなりました。

他の人が示唆しているように、ダイアログで特別な操作を実行する前に、アクティビティのステータスを確認する必要があります。

これが問題の原因である関連コードです(Androidソースコードからコピー):

public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
    if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }

    final WindowManager.LayoutParams wparams
            = (WindowManager.LayoutParams)params;

    view.setLayoutParams(wparams);

    synchronized (this) {
        int index = findViewLocked(view, true);
        ViewRootImpl root = mRoots[index];
        mParams[index] = wparams;
        root.setLayoutParams(wparams, false);
    }
}

private int findViewLocked(View view, boolean required) {
        synchronized (this) {
            final int count = mViews != null ? mViews.length : 0;
            for (int i=0; i<count; i++) {
                if (mViews[i] == view) {
                    return i;
                }
            }
            if (required) {
                throw new IllegalArgumentException(
                        "View not attached to window manager");
            }
            return -1;
        }
    }
0

または単に追加できます

protected void onPreExecute() {
    mDialog = ProgressDialog.show(mContext, "", "Saving changes...", true, false);
}

ProgressDialogをキャンセルできないようにします

0
Sarith Vasu

Androidの画面回転をブロックすることで問題が解決しました。問題を引き起こしていたアプリが完全に動作するようになりました

0
Roger

質問1)の場合:

エラーメッセージが、コードのどの行が問題を引き起こしているのかを示していないように思えると、ブレークポイントを使用して追跡できます。プログラムが特定のコード行に到達すると、ブレークポイントはプログラムの実行を一時停止します。重要な場所にブレークポイントを追加することにより、どのコード行がクラッシュの原因かを判断できます。たとえば、プログラムがsetContentView()行でクラッシュしている場合、そこにブレークポイントを置くことができます。プログラムを実行すると、その行を実行する前に一時停止します。その後再開すると、プログラムが次のブレークポイントに到達する前にクラッシュする場合、プログラムを強制終了した行が2つのブレークポイントの間にあることがわかります。

Eclipseを使用している場合、ブレークポイントの追加は簡単です。コードのすぐ左のマージンを右クリックし、「ブレークポイントの切り替え」を選択します。次に、アプリケーションをデバッグモードで実行する必要があります。デバッグモードは、通常の実行ボタンの横にある緑色の虫のように見えるボタンです。プログラムがブレークポイントに達すると、Eclipseはデバッグパースペクティブに切り替わり、待機している行を表示します。プログラムの実行を再開するには、「再開」ボタンを探します。これは、通常の「再生」のように見えますが、三角形の左側に垂直バーがあります。

アプリケーションにLog.d( "My application"、 "ログ行の場所を示すいくつかの情報")を入力して、EclipseのLogCatウィンドウにメッセージを投稿することもできます。そのウィンドウが見つからない場合は、ウィンドウ->ビューの表示->その他...-> Android-> LogCatで開きます。

お役に立てば幸いです!

0
Steve Haley