進行状況]ダイアログとバックグラウンドスレッドがアクティブになった場合、画面の向きの変化を処理する方法は?

StackOverflow https://stackoverflow.com/questions/1111980

質問

私のプログラムは、バックグラウンドスレッドでいくつかのネットワークアクティビティを行います。開始する前に、進行状況ダイアログが表示されます。ダイアログはハンドラーで却下されます。これはすべて、ダイアログが上がっている間に画面の向きが変化する場合を除き、バックグラウンドスレッドが進行している場合を除きます。この時点で、アプリはクラッシュするか、デッドロック、またはすべてのスレッドが殺されるまでアプリがまったく機能しない奇妙な段階に入ります。

スクリーンオリエンテーションの変更を優雅に処理するにはどうすればよいですか?

以下のサンプルコードは、私の実際のプログラムが行うことをほぼ一致させます。

public class MyAct extends Activity implements Runnable {
    public ProgressDialog mProgress;

    // UI has a button that when pressed calls send

    public void send() {
         mProgress = ProgressDialog.show(this, "Please wait", 
                      "Please wait", 
                      true, true);
        Thread thread = new Thread(this);
        thread.start();
    }

    public void run() {
        Thread.sleep(10000);
        Message msg = new Message();
        mHandler.sendMessage(msg);
    }

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            mProgress.dismiss();
        }
    };
}

スタック:

E/WindowManager(  244): Activity MyAct has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@433b7150 that was originally added here
E/WindowManager(  244): android.view.WindowLeaked: Activity MyAct has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@433b7150 that was originally added here
E/WindowManager(  244):     at android.view.ViewRoot.<init>(ViewRoot.java:178)
E/WindowManager(  244):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:147)
E/WindowManager(  244):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:90)
E/WindowManager(  244):     at android.view.Window$LocalWindowManager.addView(Window.java:393)
E/WindowManager(  244):     at android.app.Dialog.show(Dialog.java:212)
E/WindowManager(  244):     at android.app.ProgressDialog.show(ProgressDialog.java:103)
E/WindowManager(  244):     at android.app.ProgressDialog.show(ProgressDialog.java:91)
E/WindowManager(  244):     at MyAct.send(MyAct.java:294)
E/WindowManager(  244):     at MyAct$4.onClick(MyAct.java:174)
E/WindowManager(  244):     at android.view.View.performClick(View.java:2129)
E/WindowManager(  244):     at android.view.View.onTouchEvent(View.java:3543)
E/WindowManager(  244):     at android.widget.TextView.onTouchEvent(TextView.java:4664)
E/WindowManager(  244):     at android.view.View.dispatchTouchEvent(View.java:3198)

OnSaveinstanceStateでの進行状況ダイアログを却下しようとしましたが、それはすぐにクラッシュを防ぎます。バックグラウンドスレッドはまだ進んでおり、UIは部分的に描かれた状態です。アプリ全体が再び動作し始める前に、アプリ全体を殺す必要があります。

役に立ちましたか?

解決

オリエンテーションを切り替えると、Androidは新しいビューを作成します。あなたの背景スレッドが古い状態の状態を変えようとしているので、あなたはおそらくクラッシュしているでしょう。 (背景スレッドがUIスレッド上にないため、問題が発生している可能性もあります)

私は、そのmhandlerを揮発性にして、方向が変わったときにそれを更新することをお勧めします。

他のヒント

編集: Googleエンジニアは、Dianne Hackborn(別名 ハックボッド)これで Stackoverflow Post. 。チェックアウト このブログ投稿 詳細については。


これをマニフェストのアクティビティ宣言に追加する必要があります。

android:configChanges="orientation|screenSize"

だからそうです

<activity android:label="@string/app_name" 
        android:configChanges="orientation|screenSize|keyboardHidden" 
        android:name=".your.package">

問題は、構成の変更が発生したときにシステムがアクティビティを破壊することです。見る ConfigurationChanges.

そのため、構成ファイルにそれを入力すると、システムがアクティビティを破壊することを回避します。代わりに、それが呼び出されます onConfigurationChanged(Configuration) 方法。

私は、物事の「Androidの方法」に準拠するこれらの問題の堅実なソリューションを思いつきました。 Intentserviceパターンを使用して、すべての長期にわたる操作があります。

つまり、私のアクティビティの放送意図、Intentserviceが作業を行い、DBのデータを保存してからブロードキャストします 粘着性 意図。粘着性の部分は重要です。そのため、ユーザーが作業を開始した後、アクティビティが一時停止し、Intentserviceからリアルタイムブロードキャストを逃した場合でも、通話アクティビティからデータを取得できます。 ProgressDialogSはこのパターンで非常にうまく機能します onSaveInstanceState().

基本的に、保存されたインスタンスバンドルで実行されている[進行状況]ダイアログがあるフラグを保存する必要があります。 しない 進行状況]ダイアログオブジェクトを保存します。これにより、アクティビティ全体が漏れます。 [進行状況]ダイアログに永続的なハンドルを持つために、アプリケーションオブジェクトに弱い参照として保存します。オリエンテーションの変更、またはアクティビティを一時停止させる他の何か(電話、ユーザーが家に帰るなど)、そして再開すると、古いダイアログを却下し、新しく作成されたアクティビティで新しいダイアログを再現します。

無期限の進行状況ダイアログの場合、これは簡単です。 Progress Barスタイルのために、バンドルに最後の既知の進捗状況と、アクティビティでローカルで使用している情報を、進行状況を追跡する必要があります。進行状況を復元すると、この情報を使用して、以前と同じ状態の進行状況バーを再出現し、現在の状態に基づいて更新します。

要約するために、長期にわたるタスクを賢明な使用と組み合わせて、 onSaveInstanceState() ダイアログを効率的に追跡し、アクティビティライフサイクルイベント全体で復元することができます。関連するアクティビティコードのビットは以下にあります。また、粘着性の意図を適切に処理するために、Broadcastreceiverのロジックが必要ですが、それはこれの範囲を超えています。

public void doSignIn(View view) {
    waiting=true;
    AppClass app=(AppClass) getApplication();
    String logingon=getString(R.string.signon);
    app.Dialog=new WeakReference<ProgressDialog>(ProgressDialog.show(AddAccount.this, "", logingon, true));
    ...
}

@Override
protected void onSaveInstanceState(Bundle saveState) {
    super.onSaveInstanceState(saveState);
    saveState.putBoolean("waiting",waiting);
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if(savedInstanceState!=null) {
        restoreProgress(savedInstanceState);    
    }
    ...
}

private void restoreProgress(Bundle savedInstanceState) {
    waiting=savedInstanceState.getBoolean("waiting");
    if (waiting) {
        AppClass app=(AppClass) getApplication();
        ProgressDialog refresher=(ProgressDialog) app.Dialog.get();
        refresher.dismiss();
        String logingon=getString(R.string.signon);
        app.Dialog=new WeakReference<ProgressDialog>(ProgressDialog.show(AddAccount.this, "", logingon, true));
    }
}

私は同じ問題に会いました。私のアクティビティは、URLからいくつかのデータを解析する必要があり、それは遅いです。そこで、スレッドを作成して、進行状況ダイアログを表示します。私はスレッドにメッセージを投稿させます。 Handler 終了したら。の Handler.handleMessage, 、スレッドからデータオブジェクト(今すぐ準備)を取得し、UIに入力します。したがって、それはあなたの例に非常に似ています。

多くの試行錯誤の後、解決策を見つけたようです。少なくとも今では、スレッドが完了する前または後にいつでも画面を回転させることができます。すべてのテストで、ダイアログは適切に閉じられており、すべての動作が予想通りです。

私がしたことは以下に示されています。目標は私のデータモデルを埋めることです(mDataObject)そして、それをUIに入力します。驚くことなく、いつでも画面回転を許可する必要があります。

class MyActivity {

    private MyDataObject mDataObject = null;
    private static MyThread mParserThread = null; // static, or make it singleton

    OnCreate() {
        ...
        Object retained = this.getLastNonConfigurationInstance();
        if(retained != null) {
            // data is already completely obtained before config change
            // by my previous self.
            // no need to create thread or show dialog at all
            mDataObject = (MyDataObject) retained;
            populateUI();
        } else if(mParserThread != null && mParserThread.isAlive()){
            // note: mParserThread is a static member or singleton object.
            // config changed during parsing in previous instance. swap handler
            // then wait for it to finish.
            mParserThread.setHandler(new MyHandler());
        } else {
            // no data and no thread. likely initial run
            // create thread, show dialog
            mParserThread = new MyThread(..., new MyHandler());
            mParserThread.start();
            showDialog(DIALOG_PROGRESS);
        }
    }

    // http://android-developers.blogspot.com/2009/02/faster-screen-orientation-change.html
    public Object onRetainNonConfigurationInstance() {
        // my future self can get this without re-downloading
        // if it's already ready.
        return mDataObject;
    }

    // use Activity.showDialog instead of ProgressDialog.show
    // so the dialog can be automatically managed across config change
    @Override
    protected Dialog onCreateDialog(int id) {
        // show progress dialog here
    }

    // inner class of MyActivity
    private class MyHandler extends Handler {
        public void handleMessage(msg) {
            mDataObject = mParserThread.getDataObject();
            populateUI();
            dismissDialog(DIALOG_PROGRESS);
        }
    }
}

class MyThread extends Thread {
    Handler mHandler;
    MyDataObject mDataObject;

    // constructor with handler param
    public MyHandler(..., Handler h) {
        ...
        mHandler = h;
    }

    public void setHandler(Handler h) { mHandler = h; } // for handler swapping after config change
    public MyDataObject getDataObject() { return mDataObject; } // return data object (completed) to caller

    public void run() {
        mDataObject = new MyDataObject();
        // do the lengthy task to fill mDataObject with data
        lengthyTask(mDataObject);
        // done. notify activity
        mHandler.sendEmptyMessage(0); // tell activity: i'm ready. come pick up the data.
    }
}

それが私にとってうまくいくものです。これがAndroidによって設計された「正しい」方法であるかどうかはわかりません - 彼らはこの「画面の回転中にアクティビティを破壊/再現する」と主張しているので、実際に物事を簡単にするので、あまりにも注意が必要ではないと思います。

あなたが私のコードに問題があるかどうか教えてください。上記のように、副作用があるかどうかは本当にわかりません。

当初の知覚された問題は、コードが画面の向きの変更に耐えられないということでした。どうやら、これは、UIフレームワークに(ondestroyを呼び出すことで)uiフレームワークに実行させる代わりに、画面の向きの変更自体をプログラムに処理させることで「解決」されました。

根本的な問題がプログラムがondestroy()に耐えられないということである場合、受け入れられたソリューションは、プログラムに深刻な他の問題と脆弱性を残す単なる回避策であることを提出します。 Androidフレームワークは、あなたの活動があなたの制御外の状況により、ほぼいつでも破壊されるリスクがあることを具体的に述べていることを忘れないでください。したがって、あなたのアクティビティは、スクリーンの向きの変更だけでなく、何らかの理由でondestroy()とその後のoncreate()を生き残ることができなければなりません。

OPの問題を解決するために画面の向きの取り扱いを受け入れる場合は、OPの問題を解決するために自分自身を変更する場合、Ondestroy()の他の原因が同じエラーになっていないことを確認する必要があります。あなたはこれをすることができますか?そうでない場合、「受け入れられた」答えが本当に非常に良いものであるかどうかを疑問視します。

私の解決策は、を拡張することでした ProgressDialog 私自身を得るためのクラス MyProgressDialog.
再定義しました show()dismiss() 表示する前に向きをロックする方法 Dialog そして、いつロックを解除します Dialog 却下されます。だからいつ Dialog 表示され、デバイスの方向が変化し、画面の向きがまで dismiss() 呼び出され、センサー値/デバイス向け方向に従ってスクリーン指向の変更が変更されます。

これが私のコードです:

public class MyProgressDialog extends ProgressDialog {
private Context mContext;

public MyProgressDialog(Context context) {
    super(context);
    mContext = context;
}

public MyProgressDialog(Context context, int theme) {
    super(context, theme);
    mContext = context;
}

public void show() {
    if (mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
        ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    else
        ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    super.show();
}

public void dismiss() {
    super.dismiss();
    ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}

}

私はこの同じ問題に直面し、ProgressDialogを使用して召喚されなかったソリューションを思いつき、より速い結果が得られます。

私がしたことは、進行中のレイアウトを作成することでした。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ProgressBar
    android:id="@+id/progressImage"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    />
</RelativeLayout>

次に、OnCreateメソッドで次のことを行います

public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    setContentView(R.layout.progress);
}

次に、スレッドで長いタスクを実行し、それが終了したら、このアクティビティに使用する実際のレイアウトのコンテンツビューを実行可能なセットにします。

例えば:

mHandler.post(new Runnable(){

public void run() {
        setContentView(R.layout.my_layout);
    } 
});

これは私がしたことであり、ProgressDialogを示すよりも速く実行されることがわかりました。

ただし、ProgressDialogを使用したい場合は、この答えはあなたのためではありません。

私はこれに対する解決策を発見しました。オリエンテーションの変更で破壊され再現されるアクティビティでこれを実行しようとするのではなく、バックグラウンドタスクが進行しているかどうかを知っているカスタムアプリケーションオブジェクトを使用できます。これについてブログを書きました ここ.

このローテーションの問題を処理するためのアプローチを提供するつもりです。彼が使用していないので、これはOPに関連しない可能性があります AsyncTask, 、しかし、他の人はそれが便利だと思うかもしれません。それは非常に簡単ですが、それは私のために仕事をしているようです:

ネストされたログインアクティビティがあります AsyncTask クラスと呼ばれます BackgroundLoginTask.

私の中で BackgroundLoginTask 呼び出し時にヌルチェックを追加することを除いて、私は普通に何もしません ProgressDialog「却下:

@Override
protected void onPostExecute(Boolean result)
{    
if (pleaseWaitDialog != null)
            pleaseWaitDialog.dismiss();
[...]
}

これは、バックグラウンドタスクが終了し、 Activity 目に見えないため、進行状況ダイアログはすでに却下されています onPause() 方法。

次に、私の親に Activity クラス、私は私にグローバルな静的ハンドルを作成します AsyncTask クラスと私 ProgressDialogAsyncTask, 、ネストされている場合、これらの変数にアクセスできます):

private static BackgroundLoginTask backgroundLoginTask;
private static ProgressDialog pleaseWaitDialog;

これは2つの目的に役立ちます。まず、私の Activity 常にアクセスするには AsyncTask 新しい回転後のアクティビティからでもオブジェクト。第二に、それは私を許します BackgroundLoginTask アクセスして却下する ProgressDialog 回転した後でも。

次に、これを追加します onPause(), 、進行状況ダイアログを引き起こします Activity 前景を離れています(醜い「フォースクローズ」クラッシュを防ぐ):

    if (pleaseWaitDialog != null)
    pleaseWaitDialog.dismiss();

最後に、私は以下を持っています onResume() 方法:

if ((backgroundLoginTask != null) && (backgroundLoginTask.getStatus() == Status.RUNNING))
        {
           if (pleaseWaitDialog != null)
             pleaseWaitDialog.show();
        }

これにより、 Dialog 後に再び現れる Activity 再現されています。

これがクラス全体です:

public class NSFkioskLoginActivity extends NSFkioskBaseActivity {
    private static BackgroundLoginTask backgroundLoginTask;
    private static ProgressDialog pleaseWaitDialog;
    private Controller cont;

    // This is the app entry point.
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (CredentialsAvailableAndValidated())
        {
        //Go to main menu and don't run rest of onCreate method.
            gotoMainMenu();
            return;
        }
        setContentView(R.layout.login);
        populateStoredCredentials();   
    }

    //Save current progress to options when app is leaving foreground
    @Override
    public void onPause()
    {
        super.onPause();
        saveCredentialsToPreferences(false);
        //Get rid of progress dialog in the event of a screen rotation. Prevents a crash.
        if (pleaseWaitDialog != null)
        pleaseWaitDialog.dismiss();
    }

    @Override
    public void onResume()
    {
        super.onResume();
        if ((backgroundLoginTask != null) && (backgroundLoginTask.getStatus() == Status.RUNNING))
        {
           if (pleaseWaitDialog != null)
             pleaseWaitDialog.show();
        }
    }

    /**
     * Go to main menu, finishing this activity
     */
    private void gotoMainMenu()
    {
        startActivity(new Intent(getApplicationContext(), NSFkioskMainMenuActivity.class));
        finish();
    }

    /**
     * 
     * @param setValidatedBooleanTrue If set true, method will set CREDS_HAVE_BEEN_VALIDATED to true in addition to saving username/password.
     */
    private void saveCredentialsToPreferences(boolean setValidatedBooleanTrue)
    {
        SharedPreferences settings = getSharedPreferences(APP_PREFERENCES, MODE_PRIVATE);
        SharedPreferences.Editor prefEditor = settings.edit();
        EditText usernameText = (EditText) findViewById(R.id.editTextUsername);
        EditText pswText = (EditText) findViewById(R.id.editTextPassword);
        prefEditor.putString(USERNAME, usernameText.getText().toString());
        prefEditor.putString(PASSWORD, pswText.getText().toString());
        if (setValidatedBooleanTrue)
        prefEditor.putBoolean(CREDS_HAVE_BEEN_VALIDATED, true);
        prefEditor.commit();
    }

    /**
     * Checks if user is already signed in
     */
    private boolean CredentialsAvailableAndValidated() {
        SharedPreferences settings = getSharedPreferences(APP_PREFERENCES,
                MODE_PRIVATE);
        if (settings.contains(USERNAME) && settings.contains(PASSWORD) && settings.getBoolean(CREDS_HAVE_BEEN_VALIDATED, false) == true)
         return true;   
        else
        return false;
    }

    //Populate stored credentials, if any available
    private void populateStoredCredentials()
    {
        SharedPreferences settings = getSharedPreferences(APP_PREFERENCES,
            MODE_PRIVATE);
        settings.getString(USERNAME, "");
       EditText usernameText = (EditText) findViewById(R.id.editTextUsername);
       usernameText.setText(settings.getString(USERNAME, ""));
       EditText pswText = (EditText) findViewById(R.id.editTextPassword);
       pswText.setText(settings.getString(PASSWORD, ""));
    }

    /**
     * Validate credentials in a seperate thread, displaying a progress circle in the meantime
     * If successful, save credentials in preferences and proceed to main menu activity
     * If not, display an error message
     */
    public void loginButtonClick(View view)
    {
        if (phoneIsOnline())
        {
        EditText usernameText = (EditText) findViewById(R.id.editTextUsername);
        EditText pswText = (EditText) findViewById(R.id.editTextPassword);
           //Call background task worker with username and password params
           backgroundLoginTask = new BackgroundLoginTask();
           backgroundLoginTask.execute(usernameText.getText().toString(), pswText.getText().toString());
        }
        else
        {
        //Display toast informing of no internet access
        String notOnlineMessage = getResources().getString(R.string.noNetworkAccessAvailable);
        Toast toast = Toast.makeText(getApplicationContext(), notOnlineMessage, Toast.LENGTH_SHORT);
        toast.show();
        }
    }

    /**
     * 
     * Takes two params: username and password
     *
     */
    public class BackgroundLoginTask extends AsyncTask<Object, String, Boolean>
    {       
       private Exception e = null;

       @Override
       protected void onPreExecute()
       {
           cont = Controller.getInstance();
           //Show progress dialog
           String pleaseWait = getResources().getString(R.string.pleaseWait);
           String commWithServer = getResources().getString(R.string.communicatingWithServer);
            if (pleaseWaitDialog == null)
              pleaseWaitDialog= ProgressDialog.show(NSFkioskLoginActivity.this, pleaseWait, commWithServer, true);

       }

        @Override
        protected Boolean doInBackground(Object... params)
        {
        try {
            //Returns true if credentials were valid. False if not. Exception if server could not be reached.
            return cont.validateCredentials((String)params[0], (String)params[1]);
        } catch (Exception e) {
            this.e=e;
            return false;
        }
        }

        /**
         * result is passed from doInBackground. Indicates whether credentials were validated.
         */
        @Override
        protected void onPostExecute(Boolean result)
        {
        //Hide progress dialog and handle exceptions
        //Progress dialog may be null if rotation has been switched
        if (pleaseWaitDialog != null)
             {
            pleaseWaitDialog.dismiss();
                pleaseWaitDialog = null;
             }

        if (e != null)
        {
         //Show toast with exception text
                String networkError = getResources().getString(R.string.serverErrorException);
                Toast toast = Toast.makeText(getApplicationContext(), networkError, Toast.LENGTH_SHORT);
            toast.show();
        }
        else
        {
            if (result == true)
            {
            saveCredentialsToPreferences(true);
            gotoMainMenu();
            }
            else
            {
            String toastText = getResources().getString(R.string.invalidCredentialsEntered);
                Toast toast = Toast.makeText(getApplicationContext(), toastText, Toast.LENGTH_SHORT);
            toast.show();
            } 
        }
        }

    }
}

私は決して味付けされたAndroid開発者なので、自由にコメントしてください。

長いタスクを別のクラスに移動します。それを対象者観察者パターンとして実装します。アクティビティが作成されたときはいつでも、タスククラスでUnregisterを閉じます。タスククラスでは、asynctaskを使用できます。

秘trickは、Onpreexecute/onpostexecute中にAsynctask内のダイアログを表示/却下することですが、オリエンテーションチェンジの場合には、アクティビティのダイアログの新しいインスタンスを作成/表示し、タスクへの参照を渡すことです。

public class MainActivity extends Activity {
    private Button mButton;
    private MyTask mTask = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        MyTask task = (MyTask) getLastNonConfigurationInstance();
        if(task != null){
            mTask = task;
            mTask.mContext = this;
            mTask.mDialog = ProgressDialog.show(this, "", "", true);        
        }

        mButton = (Button) findViewById(R.id.button1);
        mButton.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v){
                mTask = new MyTask(MainActivity.this);
                mTask.execute();
            }
        });
    }


    @Override
    public Object onRetainNonConfigurationInstance() {
        String str = "null";
        if(mTask != null){
            str = mTask.toString();
            mTask.mDialog.dismiss();
        }
        Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
        return mTask;
    }



    private class MyTask extends AsyncTask<Void, Void, Void>{
        private ProgressDialog mDialog;
        private MainActivity mContext;


        public MyTask(MainActivity context){
            super();
            mContext = context;
        }


        protected void onPreExecute() {
            mDialog = ProgressDialog.show(MainActivity.this, "", "", true);
        }

        protected void onPostExecute(Void result) {
            mContext.mTask = null;
            mDialog.dismiss();
        }


        @Override
        protected Void doInBackground(Void... params) {
            SystemClock.sleep(5000);
            return null;
        }       
    }
}

私はこのようにやった:

    package com.palewar;
    import android.app.Activity;
    import android.app.ProgressDialog;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;

    public class ThreadActivity extends Activity {


        static ProgressDialog dialog;
        private Thread downloadThread;
        final static Handler handler = new Handler() {

            @Override
            public void handleMessage(Message msg) {

                super.handleMessage(msg);

                dialog.dismiss();

            }

        };

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

        }

        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);

            downloadThread = (Thread) getLastNonConfigurationInstance();
            if (downloadThread != null && downloadThread.isAlive()) {
                dialog = ProgressDialog.show(ThreadActivity.this, "",
                        "Signing in...", false);
            }

            dialog = ProgressDialog.show(ThreadActivity.this, "",
                    "Signing in ...", false);

            downloadThread = new MyThread();
            downloadThread.start();
            // processThread();
        }

        // Save the thread
        @Override
        public Object onRetainNonConfigurationInstance() {
            return downloadThread;
        }


        static public class MyThread extends Thread {
            @Override
            public void run() {

                try {
                    // Simulate a slow network
                    try {
                        new Thread().sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    handler.sendEmptyMessage(0);

                } finally {

                }
            }
        }

    }

あなたはそれがあなたのために働くかどうかを私に知らせてもらうことができます

背景を作成する場合 Service それはすべての重いリフティング(TCPリクエスト/応答、未婚)、 ViewActivity 窓を漏らしたり、データを失ったりすることなく、破壊して再作成することができます。これにより、Androidが推奨する動作が可能になります。 各構成変更のアクティビティを破壊します (たとえば、各方向の変更について)。

もう少し複雑ですが、サーバーリクエスト、データの前/後処理などを呼び出すための最良の方法です。

あなたもあなたを使うことができます Service 各リクエストをサーバーにキューに入れるため、それらのものを処理するのが簡単で効率的になります。

開発ガイドには完全なものがあります Services.

画面の向きの変更でアクティビティを破壊することを可能にする実装がありますが、再作成されたアクティビティのダイアログを正常に破壊します。私が使う ...NonConfigurationInstance バックグラウンドタスクを再現されたアクティビティに添付します。通常のAndroidフレームワークは、ダイアログ自体の再作成を処理しますが、そこには何も変更されません。

「所有」アクティビティのフィールドとこの所有者を更新する方法を追加するasynctaskをサブクラス化しました。

class MyBackgroundTask extends AsyncTask<...> {
  MyBackgroundTask (Activity a, ...) {
    super();
    this.ownerActivity = a;
  }

  public void attach(Activity a) {
    ownerActivity = a;
  }

  protected void onPostExecute(Integer result) {
    super.onPostExecute(result);
    ownerActivity.dismissDialog(DIALOG_PROGRESS);
  }

  ...
}

私のアクティビティクラスでフィールドを追加しました backgroundTask 「所有」バックグラウンドを参照して、このフィールドを使用してこのフィールドを更新します onRetainNonConfigurationInstancegetLastNonConfigurationInstance.

class MyActivity extends Activity {
  public void onCreate(Bundle savedInstanceState) {
    ...
    if (getLastNonConfigurationInstance() != null) {
      backgroundTask = (MyBackgroundTask) getLastNonConfigurationInstance();
      backgroundTask.attach(this);
    }
  }

  void startBackgroundTask() {
    backgroundTask = new MyBackgroundTask(this, ...);
    showDialog(DIALOG_PROGRESS);
    backgroundTask.execute(...);
  }

  public Object onRetainNonConfigurationInstance() {
    if (backgroundTask != null && backgroundTask.getStatus() != Status.FINISHED)
      return backgroundTask;
    return null;
  }
  ...
}

さらなる改善のための提案:

  • クリアします backgroundTask タスクが終了した後のアクティビティの参照には、メモリまたはそれに関連するその他のリソースをリリースします。
  • クリアします ownerActivity アクティビティが破壊される前のバックグラウンドタスクでの参照は、すぐに再現されない場合に備えています。
  • aを作成します BackgroundTask インターフェイスやコレクションは、同じ所有アクティビティからさまざまなタイプのタスクを実行できるようにします。

2つのレイアウトを維持する場合、すべてのUIスレッドを終了する必要があります。

asyntaskを使用する場合、簡単に電話することができます .cancel() 内部の方法 onDestroy() 現在の活動の方法。

@Override
protected void onDestroy (){
    removeDialog(DIALOG_LOGIN_ID); // remove loading dialog
    if (loginTask != null){
        if (loginTask.getStatus() != AsyncTask.Status.FINISHED)
            loginTask.cancel(true); //cancel AsyncTask
    }
    super.onDestroy();
}

asynctaskについては、「タスクのキャンセル」セクションで詳細を読む ここ.

アップデート:ステータスを確認するための条件を追加します。これは、実行状態がある場合にのみキャンセルできるためです。また、Asynctaskは1回しか実行できないことに注意してください。

実装しようとしました jfelectronそれは「それは」だからです物事の「アンドロイドの方法」に準拠するこれらの問題に対する堅実な解決策「しかし、言及されたすべての要素を調べてまとめるのに時間がかかりました。これはわずかに異なっていました。そして、私はここに投稿されたよりエレガントなソリューションを全体として投稿していると思います。

アクティビティから発射されたIntentserviceを使用して、個別のスレッドで長い実行タスクを実行します。このサービスは、ダイアログを更新するアクティビティに、粘着性の放送意図を解消します。アクティビティは、showdialog()、oncreatedialog()、およびonpreparedialog()を使用して、アプリケーションオブジェクトまたはsavedinstancestateバンドルで永続的なデータを渡す必要性を排除します。これは、アプリケーションがどのように中断されても機能するはずです。

アクティビティクラス:

public class TesterActivity extends Activity {
private ProgressDialog mProgressDialog;
private static final int PROGRESS_DIALOG = 0;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Button b = (Button) this.findViewById(R.id.test_button);
    b.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            buttonClick();
        }
    });
}

private void buttonClick(){
    clearPriorBroadcast();
    showDialog(PROGRESS_DIALOG);
    Intent svc = new Intent(this, MyService.class);
    startService(svc);
}

protected Dialog onCreateDialog(int id) {
    switch(id) {
    case PROGRESS_DIALOG:
        mProgressDialog = new ProgressDialog(TesterActivity.this);
        mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mProgressDialog.setMax(MyService.MAX_COUNTER);
        mProgressDialog.setMessage("Processing...");
        return mProgressDialog;
    default:
        return null;
    }
}

@Override
protected void onPrepareDialog(int id, Dialog dialog) {
    switch(id) {
    case PROGRESS_DIALOG:
        // setup a broadcast receiver to receive update events from the long running process
        IntentFilter filter = new IntentFilter();
        filter.addAction(MyService.BG_PROCESS_INTENT);
        registerReceiver(new MyBroadcastReceiver(), filter);
        break;
    }
}

public class MyBroadcastReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.hasExtra(MyService.KEY_COUNTER)){
            int count = intent.getIntExtra(MyService.KEY_COUNTER, 0);
            mProgressDialog.setProgress(count);
            if (count >= MyService.MAX_COUNTER){
                dismissDialog(PROGRESS_DIALOG);
            }
        }
    }
}

/*
 * Sticky broadcasts persist and any prior broadcast will trigger in the 
 * broadcast receiver as soon as it is registered.
 * To clear any prior broadcast this code sends a blank broadcast to clear 
 * the last sticky broadcast.
 * This broadcast has no extras it will be ignored in the broadcast receiver 
 * setup in onPrepareDialog()
 */
private void clearPriorBroadcast(){
    Intent broadcastIntent = new Intent();
    broadcastIntent.setAction(MyService.BG_PROCESS_INTENT);
    sendStickyBroadcast(broadcastIntent);
}}

Intentserviceクラス:

public class MyService extends IntentService {

public static final String BG_PROCESS_INTENT = "com.mindspiker.Tester.MyService.TEST";
public static final String KEY_COUNTER = "counter";
public static final int MAX_COUNTER = 100;

public MyService() {
  super("");
}

@Override
protected void onHandleIntent(Intent intent) {
    for (int i = 0; i <= MAX_COUNTER; i++) {
        Log.e("Service Example", " " + i);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction(BG_PROCESS_INTENT);
        broadcastIntent.putExtra(KEY_COUNTER, i);
        sendStickyBroadcast(broadcastIntent);
    }
}}

マニフェストファイルエントリ:

アプリケーションの前:

uses-permission android:name="com.mindspiker.Tester.MyService.TEST"
uses-permission android:name="android.permission.BROADCAST_STICKY"

内部アプリケーションセクション

service android:name=".MyService"

これが私の提案されている解決策です:

  • 説明されているように、asynctaskまたはスレッドを保持されたフラグメントに移動します ここ. 。すべてのネットワーク呼び出しをフラグメントに移動することは良い習慣だと思います。すでにフラグメントを使用している場合、そのうちの1つは呼び出しに対して責任を負わせることができます。それ以外の場合は、リンクされた記事が提案しているように、リクエストを実行するためだけにフラグメントを作成できます。
  • フラグメントは、リスナーインターフェイスを使用して、タスクの完了/障害を信号します。オリエンテーションの変更を心配する必要はありません。フラグメントには常に現在のアクティビティへの正しいリンクがあり、[進行状況]ダイアログは安全に再開できます。
  • あなたの進行状況を作るあなたのクラスのメンバーとダイアログをします。実際、すべてのダイアログに対してそれを行う必要があります。 OnPauseメソッドでは、それらを却下する必要があります。そうしないと、構成変更時にウィンドウが漏れます。忙しい状態は断片によって保持されるべきです。フラグメントがアクティビティに取り付けられている場合、コールがまだ実行されている場合は、再び[進行状況]ダイアログを表示できます。 a void showProgressDialog() この目的のために、メソッドをフラグメントアクティブリスナーインターフェイスに追加できます。

私は同じ状況に直面しました。私がしたことは、アプリケーション全体で私の進行状況ダイアログのインスタンスを1つだけ取得することでした。

最初に、DialogSingletonクラスを作成して1つのインスタンス(Singleton Pattern)のみを取得しました

public class DialogSingleton
{
    private static Dialog dialog;

    private static final Object mLock = new Object();
    private static DialogSingleton instance;

    private DialogSingleton()
    {

    }

    public static DialogSingleton GetInstance()
    {
        synchronized (mLock)
        {
            if(instance == null)
            {
                instance = new DialogSingleton();
            }

            return instance;
        }
    }

    public void DialogShow(Context context, String title)
    {
        if(!((Activity)context).isFinishing())
        {
            dialog = new ProgressDialog(context, 2);

            dialog.setCanceledOnTouchOutside(false);

            dialog.setTitle(title);

            dialog.show();
        }
    }

    public void DialogDismiss(Context context)
    {
        if(!((Activity)context).isFinishing() && dialog.isShowing())
        {
            dialog.dismiss();
        }
    }
}

このクラスで示しているように、属性としての進行状況ダイアログを持っています。進行状況ダイアログを表示する必要があるたびに、ユニークなインスタンスを取得し、新しいProgressDialogを作成します。

DialogSingleton.GetInstance().DialogShow(this, "My title here!");

バックグラウンドタスクが完了したら、独自のインスタンスを再度呼び出して、そのダイアログを却下します。

DialogSingleton.GetInstance().DialogDismiss(this);

共有設定でバックグラウンドタスクステータスを保存します。画面を回転させると、このアクティビティの実行タスクがあるかどうかを尋ねます:(Create)

if(Boolean.parseBoolean(preference.GetValue(IS_TASK_NAME_EXECUTED_KEY, "boolean").toString()))
{
    DialogSingleton.GetInstance().DialogShow(this, "Checking credentials!");
} // preference object gets the info from shared preferences (my own implementation to get and put data to shared preferences) and IS_TASK_NAME_EXECUTED_KEY is the key to save this flag (flag to know if this activity has a background task already running).

背景タスクの実行を開始したとき:

preference.AddValue(IS_TASK_NAME_EXECUTED_KEY, true, "boolean");

DialogSingleton.GetInstance().DialogShow(this, "My title here!");

背景タスクの実行が終了したとき:

preference.AddValue(IS_TASK_NAME_EXECUTED_KEY, false, "boolean");

DialogSingleton.GetInstance().DialogDismiss(ActivityName.this);

私はそれが役立つことを願っています。

これは、何らかの理由でサイドバーに出てきた非常に古い質問です。

バックグラウンドタスクが前景にあるときに生き残る必要がある場合、「新しい」ソリューションはバックグラウンドスレッドをホストすることです(またはできれば、 AsyncTask)で 保持されたフラグメント, 、これで説明されているように 開発者ガイド多数のQ&A.

構成変更のためにアクティビティが破壊された場合、保持されたフラグメントは生き残りますが、 いいえ アクティビティがバックグラウンドスタックまたはバックスタックで破壊されたとき。したがって、バックグラウンドタスクはまだ中断する必要があります isChangingConfigurations() で偽です onPause().

私はAndroidの新鮮で、これを試してみましたが、うまくいきました。

public class loadTotalMemberByBranch extends AsyncTask<Void, Void,Void> {
        ProgressDialog progressDialog = new ProgressDialog(Login.this);
        int ranSucess=0;
        @Override
        protected void onPreExecute() {
            // TODO Auto-generated method stub
            super.onPreExecute();
            progressDialog.setTitle("");    
            progressDialog.isIndeterminate();
            progressDialog.setCancelable(false);
            progressDialog.show();
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);

        }
        @Override
        protected Void doInBackground(Void... params) {
            // TODO Auto-generated method stub

            return null;
        }
        @Override
        protected void onPostExecute(Void result) {
            // TODO Auto-generated method stub
            super.onPostExecute(result);
            progressDialog.dismiss();
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
        }
}

私はすべてを試しました。実験に何日か費やしました。アクティビティが回転をブロックしたくありませんでした。私のシナリオは次のとおりです。

  1. ユーザーに動的な情報を示す進行状況ダイアログ。例:「サーバーへの接続...」、「データのダウンロード...」など
  2. 重いものをしてダイアログを更新するスレッド
  3. 最後に結果を使用してUIを更新します。

問題は、画面を回転させると、本のすべてのソリューションが失敗したことでした。この状況に対処する正しいAndroidの方法であるAsynctaskクラスでも。画面を回転させると、開始スレッドが動作している現在のコンテキストがなくなり、表示されているダイアログを台無しにします。問題は、コードに追加したトリックの数に関係なく、常にダイアログでした(スレッドの実行に新しいコンテキストを渡す、スレッドの状態を回転させて保持するなど)。最後のコードの複雑さは常に巨大であり、常に問題になる可能性のあるものが常にありました。

私のために働いた唯一の解決策は、アクティビティ/ダイアログのトリックでした。それはシンプルで天才であり、それはすべてローテーションの証明です:

  1. ダイアログを作成して表示するように依頼する代わりに、Android:theme = "@android:style/theme.dialog"でマニフェストで設定されたアクティビティを作成します。だから、それはただダイアログのように見えます。

  2. showdialog(dialog_id)をstartactivityforresult(youractivitydialog、yourcode)に置き換えます。

  3. 呼び出しアクティビティのonactivityResultを使用して、実行されたスレッド(エラーも)の結果を取得し、UIを更新します。

  4. 「ActivityDialog」では、スレッドまたはAsynctaskを使用して長いタスクを実行し、OntainNonConfigurationInstanceを実行して、画面を回転させるときに「ダイアログ」状態を保存します。

これは速く、正常に機能します。画面上の一定のダイアログを必要としないものには、他のタスクとAsynctaskのダイアログを使用しています。しかし、このシナリオでは、私は常にアクティビティ/ダイアログパターンを求めています。

そして、私はそれを試しませんでしたが、スレッドが実行されているときにそのアクティビティ/ダイアログが回転するのをブロックすることさえ可能です。

最近では、これらのタイプの問題を処理するためのはるかに明確な方法があります。典型的なアプローチは次のとおりです。

1.データがUIから適切に分離されていることを確認してください。

バックグラウンドプロセスであるものはすべて保持されるべきです Fragment (これを設定します Fragment.setRetainInstance(). 。これは、保持したいデータベースのすべてが保持される「永続的なデータストレージ」になります。オリエンテーション変更イベントの後、これ Fragment 元の状態ではまだアクセスできます FragmentManager.findFragmentByTag() 呼び出し(あなたがそれを作成するとき、あなたはそれにタグを与えるべきです IDではありません aに添付されていないため View).

を参照してください ランタイムの変更を処理します これを正しく行うための情報と、それが最良の選択肢である理由については、開発されたガイドを開発しました。

2.バックグラウンドプロセスとUIの間で正しく安全にインターフェースを出していることを確認してください。

絶対です 逆行する リンクプロセス。現時点では、背景プロセスは View - 代わりにあなた View バックグラウンドプロセスに自分自身を付ける必要があります。もっと理にかなっていますか? Viewのアクションはバックグラウンドプロセスに依存しますが、バックグラウンドプロセスは Viewこれは、リンクを標準に変更することを意味します Listener インターフェース。あなたのプロセスを言う(それが何であれ - それが AsyncTask, Runnable または何であれ)を定義します OnProcessFinishedListener, 、プロセスが完了したら、リスナーが存在する場合はそのリスナーを呼び出す必要があります。

これ 答え カスタムリスナーを行う方法のすてきな説明です。

3. UIが作成されるたびにUIをデータプロセスにリンクします(方向変更を含む):

今、あなたはあなたの現在のものと背景タスクをインターフェースすることを心配する必要があります View 構造はです。オリエンテーションの変更を処理している場合 ちゃんと (そうではありません configChanges ハッキングの人々は常にお勧めします)、そしてあなた Dialog システムによって再作成されます。これは重要です、それはオリエンテーションの変化で、すべてのあなたのすべてを意味します Dialogのライフサイクル方法はリコールされます。したがって、これらの方法のいずれかで(onCreateDialog 通常は良い場所です)、次のような電話をかけることができます。

DataFragment f = getActivity().getFragmentManager().findFragmentByTag("BACKGROUND_TAG");
if (f != null) {
    f.mBackgroundProcess.setOnProcessFinishedListener(new OnProcessFinishedListener() {
        public void onProcessFinished() {
            dismiss();
        }
    });
 }

を参照してください フラグメントライフサイクル リスナーを設定する場所を決定するには、個々の実装に最適です。

これは、この質問で尋ねられた一般的な問題に対する堅牢で完全な解決策を提供するための一般的なアプローチです。おそらく、個々のシナリオに応じてこの回答にはいくつかのマイナーな作品が欠けていますが、これは一般に、適切にオリエンテーションの変更イベントを適切に処理するための最も正しいアプローチです。

オリエンテーションが変更されたときに、スレッドを処理するための簡単な解決策を見つけました。アクティビティ/フラグメントへの静的な参照を保持し、UIに作用する前にそのヌルの場合を確認することができます。トライキャッチも使用することをお勧めします:

 public class DashListFragment extends Fragment {
     private static DashListFragment ACTIVE_INSTANCE;

     @Override
     public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ACTIVE_INSTANCE = this;

        new Handler().postDelayed(new Runnable() {
            public void run() {
                try {
                        if (ACTIVE_INSTANCE != null) {
                            setAdapter(); // this method do something on ui or use context
                        }
                }
                catch (Exception e) {}


            }
        }, 1500l);

    }

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

        ACTIVE_INSTANCE = null;
    }


}

ダイアログのオリエンテーション変更イベントの検出に苦労している場合 アクティビティリファレンスとは無関係です, 、この方法はエキサイティングにうまく機能します。複数の異なるアクティビティで表示できる独自のダイアログクラスがあるため、これを使用します。これが表示されているアクティビティを常に知っているわけではありません。この方法では、AndroidManifestを変更する必要はありません。そして、カスタムダイアログは必要ありません(私が持っているように)。ただし、カスタムコンテンツビューが必要なため、その特定のビューを使用して方向変更を検出できます。これが私の例です:

設定

public class MyContentView extends View{
    public MyContentView(Context context){
        super(context);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig){
        super.onConfigurationChanged(newConfig);

        //DO SOMETHING HERE!! :D
    }
}

実装1-ダイアログ

Dialog dialog = new Dialog(context);
//set up dialog
dialog.setContentView(new MyContentView(context));
dialog.show();

実装2 -alertdialog.builder

AlertDialog.Builder builder = new AlertDialog.Builder(context);
//set up dialog builder
builder.setView(new MyContentView(context));        //Can use this method
builder.setCustomTitle(new MycontentView(context)); // or this method
builder.build().show();

実装3 -ProgressDialog / alertdialog

ProgressDialog progress = new ProgressDialog(context);
//set up progress dialog
progress.setView(new MyContentView(context));        //Can use this method
progress.setCustomTitle(new MyContentView(context)); // or this method
progress.show();

これが私がそれに直面したときの私の解決策です:ProgressDialog ではありません Fragment 子供、だから私のカスタムクラス」ProgressDialogFragment「拡張できます DialogFragment 代わりに、構成変更のために表示されるダイアログを保持するため。

import androidx.annotation.NonNull;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.Bundle; 
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;

 /**
 * Usage:
 * To display the dialog:
 *     >>> ProgressDialogFragment.showProgressDialogFragment(
 *              getSupportFragmentManager(), 
 *              "fragment_tag", 
 *              "my dialog title", 
 *              "my dialog message");
 *              
 * To hide the dialog
 *     >>> ProgressDialogFragment.hideProgressDialogFragment();
 */ 


public class ProgressDialogFragment extends DialogFragment {

    private static String sTitle, sMessage;
    private static ProgressDialogFragment sProgressDialogFragment;

    public ProgressDialogFragment() {
    }

    private ProgressDialogFragment(String title, String message) {
        sTitle = title;
        sMessage = message;
    }


    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return ProgressDialog.show(getActivity(), sTitle, sMessage);
    }

    public static void showProgressDialogFragment(FragmentManager fragmentManager, String fragmentTag, String title, String message) {
        if (sProgressDialogFragment == null) {
            sProgressDialogFragment = new ProgressDialogFragment(title, message);
            sProgressDialogFragment.show(fragmentManager, fragmentTag);

        } else { // case of config change (device rotation)
            sProgressDialogFragment = (ProgressDialogFragment) fragmentManager.findFragmentByTag(fragmentTag); // sProgressDialogFragment will try to survive its state on configuration as much as it can, but when calling .dismiss() it returns NPE, so we have to reset it on each config change
            sTitle = title;
            sMessage = message;
        }

    }

    public static void hideProgressDialogFragment() {
        if (sProgressDialogFragment != null) {
            sProgressDialogFragment.dismiss();
        }
    }
}

課題は、ダイアログがまだ表示されていますが、デフォルトの空の文字列にリセットするときに画面回転中にダイアログのタイトルとメッセージを保持することでした

これを解決するための2つのアプローチがあります。

最初のアプローチ: マニフェストファイルの構成変更中に状態を保持するためにダイアログを利用するアクティビティを作成します。

android:configChanges="orientation|screenSize|keyboardHidden"

このアプローチはGoogleには好まれません。

2番目のアプローチ:アクティビティについて onCreate() 方法、あなたはあなたを保持する必要があります DialogFragment 再構築することによって ProgressDialogFragment 再びタイトルとメッセージが次のように savedInstanceState nullではありません:

@Override
protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_deal);

 if (savedInstanceState != null) {
      ProgressDialogFragment saveProgressDialog = (ProgressDialogFragment) getSupportFragmentManager()
              .findFragmentByTag("fragment_tag");
      if (saveProgressDialog != null) {
          showProgressDialogFragment(getSupportFragmentManager(), "fragment_tag", "my dialog title", "my dialog message");
      }
  }
}

真実であるにはあまりにも「速くて汚い」ように思えますので、欠陥を指摘してくださいが、私が見つけたのは...

asynctaskのonpostexecuteメソッド内では、トライ/キャッチブロック(空のキャッチ付き)で進行状況ダイアログの「.dismiss」を単に巻き付けてから、発生した例外を無視しました。間違っているように見えますが、悪影響はないように見えます(少なくとも私が行っていることについては、長期にわたるクエリの結果を追加として渡す別のアクティビティを開始することです)

最も単純で最も柔軟なソリューションは、 asynctask に静的な参照があります プログレスバー. 。これにより、カプセル化された、したがって、方向変更の問題に対する再利用可能なソリューションが提供されます。このソリューションは、インターネットのダウンロード、通信など、さまざまな非同期タスクに役立ちました サービス, 、およびファイルシステムスキャン。このソリューションは、複数のAndroidバージョンと電話モデルでよくテストされています。完全なデモを見つけることができます ここ 特別な関心を持って downloadfile.java

概念の例として以下を提示します

public class SimpleAsync extends AsyncTask<String, Integer, String> {
    private static ProgressDialog mProgressDialog = null;
    private final Context mContext;

    public SimpleAsync(Context context) {
        mContext = context;
        if ( mProgressDialog != null ) {
            onPreExecute();
        }
    }

    @Override
    protected void onPreExecute() {
        mProgressDialog = new ProgressDialog( mContext );
        mProgressDialog.show();
    }

    @Override
    protected void onPostExecute(String result) {
        if ( mProgressDialog != null ) {
            mProgressDialog.dismiss();
            mProgressDialog = null;
        }
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        mProgressDialog.setProgress( progress[0] );
    }

    @Override
    protected String doInBackground(String... sUrl) {
        // Do some work here
        publishProgress(1);
        return null;
    }

    public void dismiss() {
        if ( mProgressDialog != null ) {
            mProgressDialog.dismiss();
        }
    }
}

Androidアクティビティでの使用は簡単です

public class MainActivity extends Activity {
    DemoServiceClient mClient = null;
    DownloadFile mDownloadFile = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.main );
        mDownloadFile = new DownloadFile( this );

        Button downloadButton = (Button) findViewById( R.id.download_file_button );
        downloadButton.setOnClickListener( new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mDownloadFile.execute( "http://www.textfiles.com/food/bakebred.txt");
            }
        });
    }

    @Override
    public void onPause() {
        super.onPause();
        mDownloadFile.dismiss();
    }
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top