为什么在onDestroy或onPause中不会解雇Dialog,removeDialog或dialog.dismiss?

时间:2012-07-06 19:56:25

标签: android android-dialog android-orientation

我不能为我的生活弄清楚如何管理对话框而不使用configChanges来指定您想要手动处理方向更改。 所以,假设你有这个AndroidManifest:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.testandroid"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />    
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>    
</manifest>

拿这个MainActivity.java:

package com.example.testandroid;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;

public class MainActivity extends Activity {
    private final static String TAG = "MainActivity";
    Dialog mDialog = null;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");
        setContentView(R.layout.activity_main);
    }    
    public void doShowDialog(View b) {
        Log.d(TAG, "doShowDialog");
        showDialog(1);
    }

    private void tryDismiss() {
        Log.d(TAG, "tryDismiss");
        try {           
            dismissDialog(1);
            removeDialog(1);
            mDialog.dismiss();
        } catch(IllegalArgumentException ex) {
            Log.e(TAG, ex.getMessage());
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

    @Override
    protected void onPause() {
        tryDismiss();
        super.onPause();
        Log.d(TAG, "onPause");

    }
    @Override
    protected Dialog onCreateDialog(int dialog) {
        Builder b = new AlertDialog.Builder(this);
        b.setTitle("Hello").setMessage("Waiting..");
        mDialog =  b.create();
        return mDialog;

    }    
}

和这个布局(main.xml)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
 >
    <Button 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Open Dialog"
        android:onClick="doShowDialog"
        />

</LinearLayout>

如果从onDestroy或onPause调用似乎没关系,对话框会在方向切换后显示备份。但为什么?我告诉它要走了。如果调用removeDialog / dismissDialog,它在方向更改之前调用时不执行任何操作。我无法弄清楚为什么会这样。摆脱这种我知道的唯一方法是使用

自己处理方向变化
android:configChanges="keyboardHidden|orientation"

我知道新的工作方式是使用我尚未升级的FragmentDialog内容,并且我还没准备好重写我的整个应用程序。看起来很奇怪,这不起作用。

这只是我在我的应用中遇到的一个现实世界问题的示例,用户可以请求从远程服务器提取一些数据(更新微调器的数据),如果他们切换方向,则加载对话框将永远不会离开,除了使用android:configChanges选项处理方向更改之外似乎没有解决这个问题。我能做什么,但我必须做到这一点似乎很荒谬。

- 更新 - 删除按钮以关闭对话框,因为没有必要,因为对话框位于顶部,所以无论如何都无法单击它。

要重现只需启动应用程序,请单击打开对话框的按钮,然后旋转手机。

4 个答案:

答案 0 :(得分:2)

您的对话框已保存在onSaveInstanceState中,因此您可能会在启动之前尝试将其解除:

@Override
protected void onSaveInstanceState(Bundle state)
{
  tryDismiss();
  super.onSaveInstanceState(state);
}

另外我真的不明白为什么你使用Activity的onCreateDialog来管理对话框。它的设计原因是自动处理方向变化。如果你想手动处理它,为什么不只是使用对话框的功能?不是直接使用showDialog(id)onCreateDialog(id),而是在旋转屏幕后不再重新显示。

    Builder b = new AlertDialog.Builder(this);
    b.setTitle("Hello").setMessage("Waiting..");
    Dialog mDialog =  b.create();
    mDialog.show(); // <-----

答案 1 :(得分:1)

塞巴斯蒂安得到了它,但我想更多地解释一下情况以及我对话的发现,因为它可能很混乱:

  1. 正如塞巴斯蒂安所说,你必须从中调用dismissDialog / removeDialog onSaveInstanceState 如果您希望对话在旋转之前消失。
  2. 虽然你可以从onCreate创建一个对话框,但如果你没有在方向之前解除它 更改您在活动重新启动时无法在onCreate方法中解除。您必须从 onPostCreate
  3. 调用dismissDialog
  4. 调整dismissDialog也不会在方向更改后在 onRestoreInstanceState 中工作。我在调用super.onRestoreInstanceState之前和之后都试过了,并且都没有工作(认为它会因为在onSaveInstanceState中发生昏暗)
  5. 比这更重要的是我学会了你是否正在做一些异步任务,比如一个HTTP调用,你有一个内部类,它包含一个回调函数,它将在任务完成时运行,你需要知道如果旋转屏幕,内部类方法将包含对外部Activity类的原始实例的引用。这对我来说并不是很明显,而且我实际上并没有使用AsyncTask,因为其他许多人都遇到了问题(我使用的是异步http库)。

    举一个类似于我的真实代码的小例子:

         public class MyActivity extends Activity {
       private static MyActivity sThis;
    
       @Override
       public void onCreate(Bundle state) {
          super.onCreate(state);
          sThis = this;
          doAsyncWork();
       }
    
       @Override
       public void onDestroy() {
          super.onDestroy();
          sThis = null;
       }
       private void doAsyncWork() {
          showDialog(LOADING_DIALOG); 
          ExampleAsyncWorker.work(new AsyncWorkerCallback() {   
               @Override
               public void onWorkComplete() {
                 dismissDialog(LOADING_DIALOG); //doesn't work if orientation change happened.
               }        
          });
       }
    }
    

    以上代码通过CategoryManager连接到外部服务器,下载类别列表,一旦完成,就调用onCategoriesObtained然后onFetchComplete(为简洁起见,还会删除一些错误处理回调函数)。如果fetchCategories调用和onFetchComplete之间发生方向更改,则onFetchComplete中对dismissDialog的调用将永远不会起作用。原因是此内部类具有对在调整方向之前创建的Activity类的原始实例的隐式引用。因此,当您调用dismissDialog时,您在原始实例上调用它而不是新实例,这将导致dismissDialog失败并显示以下消息:&#34;没有通过Activity#showDialog&#34;显示没有ID为1的对话框。我确实找到了解决这个问题的方法,但它有点破解:

    在Activity类中,包含对 this 引用的静态引用,并在onCreate中设置它,并在onDestroy中将其null,并使用内部类中的引用,如下所示:

    public class MyActivity extends Activity {
       private static MyActivity sThis;
    
       @Override
       public void onCreate(Bundle state) {
          super.onCreate(state);
          sThis = this;
          doAsyncWork();
       }
    
       @Override
       public void onDestroy() {
          super.onDestroy();
          sThis = null;
       }
       private void doAsyncWork() {
          showDialog(LOADING_DIALOG); 
          ExampleAsyncWorker.work(new AsyncWorkerCallback() {   
               @Override
               public void onWorkComplete() {
                  sThis.dismissDialog(LOADING_DIALOG);
               }        
          });
       }
    }
    

    请注意,我不确定这是一个很好的做法,但它对我有用。我知道在引用外部类(泄露上下文)的活动中可能存在内部类的问题,因此可能有更好的方法来解决这个问题。

答案 2 :(得分:0)

AFAIK,此窗口泄露可以通过两种方式处理。

HttpResponse

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

这里Dailog是你的进度/警报dailog

答案 3 :(得分:-1)

如果没有已保存的应用创建应用,则可以使用super.onCreate(null);