programing

진행 대화 상자와 배경 스레드가 활성화되었을 때 화면 방향 변경을 처리하는 방법은 무엇입니까?

bestprogram 2023. 6. 6. 10:23

진행 대화 상자와 배경 스레드가 활성화되었을 때 화면 방향 변경을 처리하는 방법은 무엇입니까?

내 프로그램은 백그라운드 스레드에서 일부 네트워크 작업을 수행합니다.시작하기 전에 진행률 대화 상자가 나타납니다.대화 상자가 처리기에서 해제됩니다.대화 상자가 위에 있는 동안(배경 스레드가 진행되는 동안) 화면 방향이 변경되는 경우를 제외하고는 모두 정상적으로 작동합니다.이 시점에서 앱이 충돌하거나 교착 상태에 빠지거나 모든 스레드가 삭제될 때까지 앱이 전혀 작동하지 않는 이상한 단계에 들어갑니다.

화면 방향 변경을 정상적으로 처리하려면 어떻게 해야 합니까?

아래 샘플 코드는 실제 프로그램이 수행하는 작업과 대략 일치합니다.

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)

SaveInstanceState(인스턴스 상태 저장)의 진행률 대화 상자를 해제하려고 했지만, 즉시 충돌을 방지할 뿐입니다.백그라운드 스레드는 계속 진행 중이며 UI는 부분적으로 그려진 상태입니다.앱이 다시 작동하기 전에 전체 앱을 종료해야 합니다.

편집:스택 오버플로 게시물에서 Dianne Hackborn(일명 hackbod)이 설명한 바와 같이 Google 엔지니어는 이 접근 방식을 권장하지 않습니다.자세한 내용은 이 블로그 게시물을 확인하십시오.


이것을 매니페스트의 활동 선언에 추가해야 합니다.

android:configChanges="orientation|screenSize"

그래서 보기에는

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

문제는 구성 변경이 발생하면 시스템이 활동을 파괴한다는 것입니다.구성 변경을 참조하십시오.

따라서 구성 파일에 저장하면 시스템이 작업을 삭제하지 않습니다.대신에 그것은 그것을 호출합니다.onConfigurationChanged(Configuration)방법.

방향을 전환하면 Android가 새 보기를 만듭니다.백그라운드 스레드가 이전 스레드의 상태를 변경하려고 하기 때문에 충돌이 발생할 수 있습니다. (또한 백그라운드 스레드가 UI 스레드에 없기 때문에 문제가 있을 수 있습니다.)

mHandler를 휘발성으로 만들고 방향이 바뀌면 업데이트하는 것이 좋습니다.

저는 이러한 문제들에 대해 '안드로이드 방식'에 부합하는 확고한 해결책을 생각해냈습니다.IntentService 패턴을 사용하여 모든 장기 실행 작업을 수행합니다.

즉, 활동 브로드캐스트 의도, 즉 IntentService는 작업을 수행하고 데이터를 DB에 저장한 다음 고정 의도를 브로드캐스트합니다.사용자가 작업을 시작한 후 활동이 일시 중지되어 IntentService의 실시간 브로드캐스트를 놓치더라도 호출 중인 활동에서 응답하고 데이터를 가져올 수 있습니다. ProgressDialog이 패턴과 잘 작동할 수 있습니다.onSaveInstanceState().

기본적으로 저장된 인스턴스 번들에서 진행률 대화 상자가 실행 중인 플래그를 저장해야 합니다.전체 활동이 누출되므로 진행률 대화 상자 개체를 저장하지 마십시오.진행률 대화 상자에 대한 지속적인 핸들을 유지하기 위해 응용 프로그램 개체에 약한 참조로 저장합니다.방향 변경 또는 활동이 일시 중지된 후 다시 시작되는 다른 이유(전화 통화, 사용자가 집으로 이동하는 등)가 있을 경우 이전 대화상자를 닫고 새로 작성된 활동에 새 대화상자를 다시 만듭니다.

무한 진행 대화 상자의 경우 이 작업은 쉽습니다.진행률 표시줄 스타일의 경우 마지막으로 알려진 진행률과 활동에서 로컬로 사용 중인 정보를 번들에 넣어야 진행률을 추적할 수 있습니다.진행률을 복원할 때 이 정보를 사용하여 진행률 표시줄을 이전과 동일한 상태로 다시 생성한 다음 현재 상태를 기준으로 업데이트합니다.

것과 한 IntentService에 넣는 입니다.onSaveInstanceState()에서는 작업 수명 주기 이벤트 전반에 걸쳐 대화 상자를 효율적으로 추적하고 복원할 수 있습니다.활동 코드의 관련 비트는 아래와 같습니다.또한 Sticky 의도를 적절하게 처리하려면 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에서 일부 데이터를 구문 분석해야 하는데 속도가 느립니다.그래서 저는 그렇게 할 스레드를 만들고 진행 대화상자를 보여줍니다.스레드가 다음을 통해 UI 스레드에 메시지를 다시 게시하도록 허용합니다.Handler그것이 완성되면요.Handler.handleMessage스레드에서 데이터 개체(ready now)를 가져와 UI로 채웁니다.그래서 이것은 당신의 예와 매우 비슷합니다.

많은 시행착오 끝에 해결책을 찾은 것 같습니다.이제 스레드가 완료되기 전이나 후에 언제든지 화면을 회전할 수 있습니다.모든 테스트에서 대화 상자가 올바르게 닫히고 모든 동작이 예상대로 수행됩니다.

제가 한 일은 아래와 같습니다.모델을 입니다.mDataObject를 입력한 를 입력합니다.언제라도 깜짝 놀랄 일 없이 화면을 회전시킬 수 있어야 합니다.

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 프레임워크가 (Destroy 호출을 통해) 수행하도록 하는 대신 프로그램이 화면 방향 변경 자체를 처리하도록 함으로써 "해결"되었습니다.

근본적인 문제가 프로그램이 Destroy()에서 살아남지 못할 것이라는 것이라면, 수용된 해결책은 프로그램을 심각한 다른 문제와 취약성으로 남기는 해결책에 불과하다는 것을 제출하겠습니다.Android 프레임워크에는 사용자가 통제할 수 없는 상황으로 인해 거의 언제든지 활동이 파괴될 위험이 있다고 명시되어 있습니다.따라서 사용자의 활동은 단순히 화면 방향 변경이 아니라 어떤 이유로든 Destroy() 및 Create() 이후에도 유지될 수 있어야 합니다.

OP의 문제를 해결하기 위해 화면 방향 변경 처리를 직접 수락하려면 onDestroy()의 다른 원인이 동일한 오류를 발생시키지 않는지 확인해야 합니다.당신은 이것을 할 수 있습니까?그렇지 않다면, 저는 "인정된" 대답이 정말 좋은 대답인지 의문을 제기할 것입니다.

내 해결책은 확장하는 것이었습니다.ProgressDialog만의 MyProgressDialog.

다시 정의했습니다.show()그리고.dismiss()▁the를 표시하기 Dialog그리고 그것을 다시 잠금 해제합니다.Dialog해고당했습니다.

래서그 ▁the 때.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);
    }
}

저는 이와 같은 문제에 직면했고 Progress Dialog를 사용하지 않는 해결책을 생각해 냈고 결과가 더 빠릅니다.

제가 한 일은 진행률 표시줄이 있는 레이아웃을 만드는 것이었습니다.

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

이것이 제가 한 일입니다. 진행 상황 대화 상자를 표시하는 것보다 더 빨리 실행되고 덜 방해적이며 제 생각에 더 나은 모습을 보여줍니다.

그러나 진행률 대화 상자를 사용하려는 경우 이 대답은 사용자에게 적합하지 않습니다.

저는 다른 곳에서는 아직 보지 못했던 해결책을 발견했습니다.방향 변경 시 삭제되고 다시 생성되는 활동에서 백그라운드 작업이 진행 중인지 여부를 알 수 있는 사용자 지정 응용 프로그램 개체를 사용할 수 있습니다.저는 여기서 이것에 대해 블로그를 했습니다.

저는 이 순환 문제를 처리하는 저의 접근 방식에 기여할 것입니다.하지 않기 때문에 수 .AsyncTask하지만 다른 사람들은 그것이 유용하다고 생각할지도 모릅니다.꽤 가 있는 것 : 단 간 만 제 도 되 것 꽤 같 다 습 니 는 이 움 하 게 지 같 니 습 다 ▁it 것 꽤 ▁it 되 는 ' : ▁the ▁to ▁seems

된 활동이 .AsyncTask라고 불리는 클래스BackgroundLoginTask.

내 안에서BackgroundLoginTask나는 전화할 때 null 체크를 추가하는 것 외에는 특이한 일을 하지 않습니다.ProgressDialog의 기각제해의:

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

이 완료되는 동안 작업이 입니다.Activity 상자는 표되지않대상이제미다거니습었에 되었습니다.onPause()방법.

다음으로 부모님 안에서Activity제▁my▁to에 글로벌 정적 핸들을 .AsyncTask과 나의 급과나의.ProgressDialog (그)AsyncTask다음 변수에 액세스할 수 있음):

private static BackgroundLoginTask backgroundLoginTask;
private static ProgressDialog pleaseWaitDialog;

이는 두 가지 목적에 부합합니다.첫째, 그것은 나를 허락합니다.Activity 항액세수있다니습할에 합니다.AsyncTask새로운 사후 활동의 대상이 될 수도 있습니다.둘째, 그것은 나를 허용합니다.BackgroundLoginTaskProgressDialog회전 후에도.

저는 을 다으로, 나이은에 합니다.onPause()진행 대화상자가 사라지도록 하는 것은 우리의Activity는 (저 클로즈" 전경을 . :("force close" 충돌을 설명합니다.

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

    }
}

저는 결코 안드로이드 개발자가 아니기 때문에 자유롭게 의견을 말씀해주세요.

긴 작업을 별도의 클래스로 이동합니다.제목-관찰자 패턴으로 구현합니다.활동이 생성될 때마다 작업 클래스에 등록을 취소하고 종료합니다.작업 클래스는 비동기 작업을 사용할 수 있습니다.

이 방법은 사전 실행/onPost 동안 비동기 작업 내에서 대화 상자를 표시/해제하는 것입니다.방향을 변경하는 경우에는 대화상자의 새 인스턴스를 작성/표시하고 작업에 참조를 전달합니다.

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

저는 이렇게 해왔습니다.이 코드는 처리기와 비동기 작업 및 로더를 사용한 Android 백그라운드 처리 - Lars Vogel의 튜토리얼:

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

당신은 또한 그것이 당신에게 효과가 있는지 아닌지를 시도하고 나에게 알려줄 수 있습니다.

이것이 제가 제안한 솔루션입니다.

  • 여기에 설명된 대로 비동기 작업 또는 스레드를 보존된 조각으로 이동합니다.모든 네트워크 통화를 조각으로 옮기는 것이 좋은 방법이라고 생각합니다.이미 fragment를 사용하고 있는 경우, fragment 중 하나가 통화에 대한 책임을 질 수 있습니다.그렇지 않으면 링크된 문서에서 제안하는 대로 요청을 수행하기 위한 조각을 만들 수 있습니다.
  • 이 조각은 수신기 인터페이스를 사용하여 작업 완료/실패 신호를 보냅니다.당신은 그곳에서 방향 변경에 대해 걱정할 필요가 없습니다.조각에는 항상 현재 활동에 대한 올바른 링크가 있으며 진행 대화 상자를 안전하게 다시 시작할 수 있습니다.
  • 진행률 대화 상자를 클래스의 구성원으로 만듭니다.실제로 모든 대화 상자에 대해 이 작업을 수행해야 합니다.. 그렇지 시 . 그렇지 않으면 구성 변경 시 창이 누출됩니다.사용 중 상태는 fragment에 의해 유지되어야 합니다.프래그먼트가 활동에 연결되면 통화가 계속 실행 중인 경우 진행 대화상자를 다시 열 수 있습니다.void showProgressDialog()이를 위해 메서드를 fragment-activity 수신기 인터페이스에 추가할 수 있습니다.

이것은 어떤 이유에서인지 사이드바에 올라온 아주 오래된 질문입니다.

동안 "활동전있동백작새드는운경또되살, "우업운로솔루")를 호스트하는 입니다.AsyncTask)는 본 개발자 안내서 및 수많은 Q&A에 설명된 바와 같이 보유된 단편에 포함됩니다.

보존된 조각은 구성 변경을 위해 활동이 삭제된 경우에도 유지되지만, 백그라운드 또는 백스택에서 활동이 삭제된 경우에는 유지되지 않습니다.따라서 백그라운드 작업은 다음에서 거짓인 경우에도 중단되어야 합니다.onPause().

모든 무거운 리프팅(tcp 요청/응답, 마샬링 해제)을 수행하는 백그라운드를 생성하는 경우,View그리고.Activity창을 유출하거나 데이터를 손실하지 않고 삭제 및 재생성할 수 있습니다.이를 통해 Android 권장 동작을 수행할 수 있습니다. 즉, 각 구성 변경 시(예: 각 방향 변경 시) 활동을 삭제합니다.

좀 더 복잡하지만 서버 요청, 데이터 사전/사후 처리 등을 호출하는 데 가장 좋은 방법입니다.

여러분의 사용수있다니를 .Service각 요청을 서버로 대기열에 저장하여 이러한 작업을 쉽고 효율적으로 처리할 수 있습니다.

개발 가이드에는 에 대한 전체 장이 나와 있습니다.

화면 방향 변경 시 활동을 삭제할 수 있지만 재생성된 활동의 대화 상자를 삭제할 수 있는 구현이 있습니다.사용합니다...NonConfigurationInstance백그라운드 작업을 재생성된 활동에 첨부합니다.일반적인 Android 프레임워크는 대화 상자 자체를 다시 만드는 작업을 처리하며, 변경된 내용은 없습니다.

소유' 활동에 대한 필드와 이 소유자를 업데이트하는 방법을 추가하여 비동기 작업을 하위 분류했습니다.

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작업을 하여 이 합니다.onRetainNonConfigurationInstance그리고.getLastNonConfigurationInstance.

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작업이 삭제되기 전에 백그라운드 작업에서 참조할 수 있습니다(작업이 즉시 다시 생성되지 않을 경우).
  • 성을 합니다.BackgroundTask인터페이스 및/또는 수집을 통해 동일한 소유 활동에서 서로 다른 유형의 작업을 실행할 수 있습니다.

두 개의 레이아웃을 유지하는 경우 모든 UI 스레드가 종료되어야 합니다.

AsynTask를하면 AsynTask를 할 수 ..cancel() inside 내법방의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();
}

비동기 작업의 경우 여기의 "작업 취소" 섹션을 자세히 읽어 보십시오.

업데이트: 실행 중인 경우에만 취소할 수 있으므로 상태를 확인할 수 있는 조건이 추가되었습니다.또한 비동기 작업은 한 번만 실행할 수 있습니다.

jfeelectron의 솔루션은 "사물의 '안드로이드 방식'에 부합하는 이러한 문제에 대한 확고한 솔루션"이기 때문에 구현하려고 했지만 언급된 모든 요소를 찾아 정리하는 데 시간이 걸렸습니다.결국 약간 다른 솔루션이 여기에 전체적으로 게시되었습니다.

활동에서 실행된 IntentService를 사용하여 별도의 스레드에서 장시간 실행 작업을 수행합니다.이 서비스는 대화 상자를 업데이트하는 활동에 대한 고정 브로드캐스트 의도를 다시 실행합니다.Activity는 CreateDialog() 및 PrepareDialog()에서 showDialog()를 사용하여 애플리케이션 개체 또는 저장된 InstanceState 번들에 영구 데이터를 전달할 필요가 없습니다.응용프로그램이 중단되는 경우에도 이 작업은 수행됩니다.

활동 클래스:

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

Intent 서비스 클래스:

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"

저도 같은 상황에 직면했습니다.제가 한 일은 전체 응용프로그램에서 진행 상황 대화상자에 대한 하나의 인스턴스만 얻은 것입니다.

먼저 DialogSingleton 클래스를 만들어 인스턴스 하나만 가져옵니다(Singleton 패턴).

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

이 수업에서 보여드린 것처럼 진행 대화상자를 속성으로 가지고 있습니다.진행률 대화 상자를 표시해야 할 때마다 고유한 인스턴스를 가져오고 새 진행률 대화 상자를 만듭니다.

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

백그라운드 작업을 마치면 고유 인스턴스를 다시 호출하고 해당 대화 상자를 닫습니다.

DialogSingleton.GetInstance().DialogDismiss(this);

백그라운드 태스크 상태를 공유 환경설정에 저장합니다.화면을 돌리면 이 활동에 대해 실행 중인 태스크가 있는지 묻는 메시지가 나타납니다. (생성 시)

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

도움이 되길 바랍니다.

저는 안드로이드 초보자이고 이것을 시도해 보았는데 효과가 있었습니다.

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. 대화상자를 만들고 표시를 요청하는 대신 안드로이드:theme="@android:style/Theme.Dialog"로 매니페스트에 설정된 활동을 만듭니다.그래서, 이것은 단지 대화상자처럼 보입니다.

  2. showDialog(DIALOGY_ID)를 startActivityForResult(사용자의 ActivityDialog, 사용자의 코드)로 바꿉니다.

  3. 실행 중인 스레드(오류도 포함)에서 결과를 가져오고 UI를 업데이트하려면 onActivityResult를 호출합니다.

  4. 'ActivityDialog'에서 스레드 또는 비동기 작업을 사용하여 긴 작업을 실행하고 비구성 유지화면을 회전할 때 "대화 상자" 상태를 저장하는 인스턴스입니다.

이것은 빠르고 잘 작동합니다.저는 여전히 다른 작업에는 대화상자를 사용하고 화면에 일정한 대화상자가 필요하지 않은 작업에는 비동기 작업을 사용합니다.하지만 이 시나리오에서는 항상 활동/대화 상자 패턴을 선호합니다.

또한 시도하지는 않았지만 스레드가 실행 중일 때 활동/대화 상자가 회전하지 않도록 차단하여 작업 속도를 높이고 호출하는 활동을 회전시킬 수도 있습니다.

요즘에는 이러한 유형의 문제를 처리하는 훨씬 더 뚜렷한 방법이 있습니다.일반적인 접근 방식은 다음과 같습니다.

데이터가 UI에서 올바르게 분리되었는지 확인합니다.

은 보존된 .Fragment((으)로 Fragment.setRetainInstance()이 스토리지는 보존하고자 하는 데이터 기반의 모든 데이터를 보관하는 '영구 데이터 스토리지'가 됩니다.이벤트가 에, 이 오엔테끝후난에가이변트벤션이경리,Fragment를 통해 원래 상태에서 여전히 액세스할 수 있습니다.FragmentManager.findFragmentByTag()call (당신이 그것을 만들 때 당신은 그것에 ID가 아닌 태그를 주어야 합니다. 왜냐하면 그것은 그것에 부착되지 않았기 때문입니다.View).

이 작업을 올바르게 수행하는 방법과 이 작업이 가장 좋은 옵션인 이유는 개발된 런타임 변경 처리 안내서를 참조하십시오.

백그라운드 프로세스와 UI 간에 정확하고 안전하게 인터페이스해야 합니다.

연결 프로세스를 되돌려야 합니다.현재 당신의 배경 과정은 다음과 같습니다.View에 당신의 신당의신대의.View백그라운드 프로세스에 연결해야 합니다.그게 더 말이 되죠?View의 과정에 , 은 배경 과정에 달라지지 View. 링크로 하는 것을 Listener process (어떤이든 간에 - 어떤 이든 간에) 말해보세요.AsyncTask,Runnable 무엇이든는 정의합니다.OnProcessFinishedListener프로세스가 완료되면 해당 수신기를 호출해야 합니다.

답변은 맞춤형 청취자를 수행하는 방법에 대한 간결한 설명입니다.

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에서 작업하기 전에 활동/조각에 대한 정적 참조를 유지하고 null인지 확인할 수 있습니다.저는 시도 캐치도 사용할 것을 제안합니다.

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


}

활동 참조와 관계없이 대화 상자의 방향 변경 이벤트를 탐지하는 데 어려움을 겪고 있는 경우 이 방법이 매우 효과적입니다.여러 개의 다른 활동에 표시될 수 있는 고유한 대화 상자 클래스가 있으므로 항상 어떤 활동에 표시되는지 알 수 없기 때문에 이 대화 상자 클래스를 사용합니다.이 방법을 사용하면 Android Manifest를 변경할 필요가 없고 작업 참조에 대해 걱정할 필요가 없으며 사용자 지정 대화 상자가 필요하지 않습니다(나처럼).그러나 특정 보기를 사용하여 방향 변경을 탐지하려면 사용자 정의 내용 보기가 필요합니다.다음은 제 예입니다.

세우다

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 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 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();

입니다: 이이제가직해결입니다책한면것다니▁this▁when입▁solution해책결▁is.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();
        }
    }
}

문제는 화면이 기본 빈 문자열로 재설정되는 동안 대화 상자 제목과 메시지를 유지하는 것이었습니다. 대화 상자는 여전히 표시됩니다.

이를 해결하기 위한 두 가지 접근 방식이 있습니다.

번째 접근법: 매니페스트 파일에서 구성 변경 중 상태를 유지하기 위해 대화 상자를 사용하는 활동을 만듭니다.

android:configChanges="orientation|screenSize|keyboardHidden"

Google에서는 이 방법을 선호하지 않습니다.

번째 접근법: 활동에 대한onCreate()방법, 당신은 당신의 것을 유지해야 합니다.DialogFragment건으로써를 으로써.ProgressDialogFragment다음과 같은 제목과 메시지로 다시 만약에savedInstanceState

@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의 메서드를 실행합니다. 진행 대화 상자의 '.dismiss'를 시도/캐치 블록(빈 캐치 포함)으로 래핑한 다음 제기된 예외를 단순히 무시했습니다.잘못된 것 같지만 부작용은 없는 것 같습니다(적어도 추가로 오래 실행된 쿼리의 결과로 다른 활동을 시작하는 것).

가장 간단하고 유연한 솔루션은 ProgressBar에 대한 정적 참조가 있는 비동기 작업을 사용하는 것입니다.이는 방향 변경 문제에 대한 캡슐화된 재사용 가능한 솔루션을 제공합니다.이 솔루션은 인터넷 다운로드, 서비스와의 통신 및 파일 시스템 검색을 비롯한 다양한 비동기 작업에 적합합니다.이 솔루션은 여러 안드로이드 버전과 전화 모델에서 잘 테스트되었습니다.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();
    }
}

방향을 변경하면 Android는 해당 활동을 종료하고 새로운 활동을 생성합니다.크래시를 자동으로 처리하는 Rx java와 함께 레트로핏을 사용할 것을 제안합니다.

재적합 호출 시 이 방법을 사용합니다.

.subscribeOn(Schedulers.io subscribe) .observeOn(AndroidSchedulers.mainThread())

언급URL : https://stackoverflow.com/questions/1111980/how-to-handle-screen-orientation-change-when-progress-dialog-and-background-thre