티스토리 뷰
Stack Overflow에 자주 검색, 등록되는 문제들과 제가 개발 중 찾아 본 문제들 중에서 나중에도 찾아 볼 것 같은 문제들을 정리하고 있습니다.
Stack Overflow에서 가장 먼저 확인하게 되는 가장 높은 점수를 받은 Solution과 현 시점에 도움이 될 수 있는 가장 최근에 업데이트(최소 점수 확보)된 Solution을 각각 정리하였습니다.
아래 word cloud를 통해 이번 포스팅의 주요 키워드를 미리 확인하세요.
How to handle screen orientation change when progress dialog and background thread active?
ProgressDialog와 백그라운드 스레드가 활성화되어 있는 경우 어떻게 화면 회전을 처리하나요?
문제 내용
My program does some network activity in a background thread. Before starting, it pops up a progress dialog. The dialog is dismissed on the handler. This all works fine, except when screen orientation changes while the dialog is up (and the background thread is going). At this point the app either crashes, or deadlocks, or gets into a weird stage where the app does not work at all until all the threads have been killed.
내 프로그램은 백그라운드 스레드에서 일부 네트워크 활동을 수행합니다. 시작하기 전에 진행 대화 상자가 표시됩니다. 이 대화 상자는 핸들러에서 해제됩니다. 이 모든 작업은 대화 상자가 열린 상태에서 (백그라운드 스레드가 실행 중인 상태에서) 화면 방향이 변경될 때를 제외하고는 잘 작동합니다. 이 때 앱은 충돌하거나 데드락에 빠지거나 모든 스레드가 종료될 때까지 앱이 전혀 작동하지 않는 이상한 상태에 빠집니다.
How can I handle the screen orientation change gracefully?
화면 회전 전환을 원활하게 처리하는 방법은 무엇인가요?
The sample code below matches roughly what my real program does:
아래의 샘플 코드는 실제 프로그램과 거의 일치합니다.
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();
}
};
}
Stack:
오류 스택
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)
I have tried to dismiss the progress dialog in onSaveInstanceState, but that just prevents an immediate crash. The background thread is still going, and the UI is in partially drawn state. Need to kill the whole app before it starts working again.
저장 인스턴스 상태에서 프로그래스 대화 상자를 해제하려고 시도했지만, 이는 즉시 충돌을 방지할 뿐입니다. 백그라운드 스레드는 계속 실행되고 UI는 일부만 그려진 상태입니다. 이를 해결하려면 앱 전체를 종료해야 합니다.
높은 점수를 받은 Solution
Edit: Google engineers do not recommend this approach, as described by Dianne Hackborn (a.k.a. hackbod) in this StackOverflow post. Check out this blog post for more information.
수정: 구글 엔지니어들은 이 방법을 추천하지 않습니다. hackbod(Dianne Hackborn)가 StackOverflow 게시물에서 설명한 내용을 참조하십시오. 자세한 내용은 이 블로그 게시물을 확인하십시오.
You have to add this to the activity declaration in the manifest:
해당 활동을 Manifest 파일에 선언해야합니다.
android:configChanges="orientation|screenSize"
so it looks like
그래서 이것이 마치 ~처럼 보입니다.
<activity android:label="@string/app_name"
android:configChanges="orientation|screenSize|keyboardHidden"
android:name=".your.package">
The matter is that the system destroys the activity when a change in the configuration occurs. See ConfigurationChanges.
문제는 시스템이 구성 변경이 발생할 때 액티비티를 파괴한다는 것입니다. ConfigurationChanges를 참조하십시오.
So putting that in the configuration file avoids the system to destroy your activity. Instead it invokes the onConfigurationChanged(Configuration)
method.
그래서 구성 파일에 추가하면 시스템이 액티비티를 파괴하는 것을 방지할 수 있습니다. 대신 onConfigurationChanged(Configuration) 메소드를 호출합니다.
가장 최근 달린 Solution
This is my proposed solution:
이것은 제가 제안하는 해결책입니다:
- Move the AsyncTask or Thread to a retained Fragment, as explained here. I believe it is a good practice to move all network calls to fragments. If you are already using fragments, one of them could be made responsible for the calls. Otherwise, you can create a fragment just for doing the request, as the linked article proposes.
- The fragment will use a listener interface to signal the task completion/failure. You don't have to worry for orientation changes there. The fragment will always have the correct link to the current activity and progress dialog can be safely resumed.
- Make your progress dialog a member of your class. In fact you should do that for all dialogs. In the onPause method you should dismiss them, otherwise you will leak a window on the configuration change. The busy state should be kept by the fragment. When the fragment is attached to the activity, you can bring up the progress dialog again, if the call is still running. A
void showProgressDialog()
method can be added to the fragment-activity listener interface for this purpose.
1. AsyncTask나 Thread를 보존된 Fragment로 이동합니다. 이것은 여기에서 설명되어 있습니다. 모든 네트워크 호출을 Fragment로 이동하는 것이 좋은 방법이라고 생각합니다. 이미 Fragment를 사용 중이라면, 호출을 담당할 Fragment를 만들 수 있습니다. 그렇지 않은 경우, 링크된 기사에서 제안하는 대로 요청을 처리하기 위한 Fragment를 만들 수 있습니다.
2. Fragment는 listener 인터페이스를 사용하여 작업 완료/실패를 신호로 보냅니다. 여기서 orientation 변경에 대해 걱정할 필요가 없습니다. Fragment는 항상 현재 Activity와 올바른 링크를 유지하며, 진행 대화 상자를 안전하게 재개할 수 있습니다.
3. 진행 대화 상자를 클래스 멤버로 만듭니다. 사실 모든 대화 상자에 대해 그렇게 해야합니다. onPause 메소드에서는 대화 상자를 해제해야합니다. 그렇지 않으면 구성 변경 시 창을 누출할 수 있습니다. 바쁜 상태는 Fragment에 의해 유지되어야합니다. Fragment가 Activity에 연결되면, 호출이 아직 실행 중인 경우 진행 대화 상자를 다시 표시할 수 있습니다. 이를 위해 Fragment-Activity listener 인터페이스에 showProgressDialog() 메소드를 추가할 수 있습니다.
출처 : https://stackoverflow.com/questions/1111980/how-to-handle-screen-orientation-change-when-progress-dialog-and-background-thre
'개발 > 안드로이드' 카테고리의 다른 글
ScrollView 안에 HorizontalScrollView가 있는 경우 터치 핸들링하기 (0) | 2022.12.25 |
---|---|
'AppDatabase_Impl does not exist' 오류 수정하기 (0) | 2022.12.24 |
뷰페이저(viewpager) 내 현재 프래그먼트 인스턴스(instance) 가져오기 (0) | 2022.12.24 |
putExtra(), getExtra()로 다른 액티비티로 문자열 보내기 (0) | 2022.12.24 |
안드로이드에서 클립보드에 텍스트 복사하기 (0) | 2022.12.23 |