백그라운드 스레드에서 인터넷에서 일부 데이터를 다운로드하고 (을 사용합니다 AsyncTask
) 다운로드하는 동안 진행률 대화 상자를 표시합니다. 방향이 변경되고 활동이 다시 시작된 다음 내 AsyncTask가 완료됩니다. progess 대화 상자를 닫고 새 활동을 시작하고 싶습니다. 그러나 dismissDialog를 호출하면 때때로 예외가 발생합니다 (아마도 활동이 파괴되었고 새로운 활동이 아직 시작되지 않았기 때문에).
이러한 종류의 문제를 처리하는 가장 좋은 방법은 무엇입니까 (사용자가 방향을 변경하더라도 작동하는 백그라운드 스레드에서 UI 업데이트)? Google 직원이 “공식 솔루션”을 제공 했습니까?
답변
1 단계 : 당신의 확인 중첩 클래스, 또는 완전히 별도의 클래스, 그냥 내부 (비 정적 중첩) 클래스를.AsyncTask
static
2 단계 : 생성자와 setter를 통해 설정된 데이터 멤버를 통해 via를 AsyncTask
유지합니다 Activity
.
3 단계 : 생성시 생성자에 AsyncTask
전류 Activity
를 공급합니다 .
단계 # 4 : 에서, 원래의 진행중인 활동에서 분리 한 후을 onRetainNonConfigurationInstance()
리턴하십시오 AsyncTask
.
5 단계 : 에서이 아닌 onCreate()
경우 클래스로 전송하고 세터를 호출하여 새 활동을 작업과 연결합니다.getLastNonConfigurationInstance()
null
AsyncTask
6 단계 :의 활동 데이터 멤버를 참조하지 마십시오 doInBackground()
.
위의 레시피를 따르면 모든 것이 작동합니다. onProgressUpdate()
그리고 onPostExecute()
다음의 시작 onRetainNonConfigurationInstance()
과 끝 사이에 일시 중단 onCreate()
됩니다.
또 다른 방법은를 버리고 AsyncTask
작업을으로 옮기는 것 IntentService
입니다. 이것은 수행 할 작업이 길 수 있고 사용자가 활동 측면에서 수행하는 작업 (예 : 큰 파일 다운로드)에 관계없이 진행되어야하는 경우에 특히 유용합니다. 정렬 된 브로드 캐스트 Intent
를 사용하여 활동이 수행중인 작업에 응답하도록하거나 (아직 포 그라운드에있는 Notification
경우) 작업을 완료했는지 사용자에게 알리기 위해 a 를 올릴 수 있습니다. 이 패턴에 대한 자세한 내용 은 블로그 게시물 입니다.
답변
수락 된 답변은 매우 도움이되었지만 진행률 대화 상자가 없습니다.
다행히도 독자 여러분, 진행 대화 상자가있는 AsyncTask 의 매우 포괄적이고 실제적인 예를 만들었습니다 !
- 회전이 작동하고 대화가 유지됩니다.
- 뒤로 버튼을 눌러 작업 및 대화 상자를 취소 할 수 있습니다 (이 동작을 원하는 경우).
- 조각을 사용합니다.
- 활동 아래의 조각 레이아웃은 장치가 회전 할 때 올바르게 변경됩니다.
답변
나는 매니페스트 파일을 편집하지 않고이 딜레마에 대한 해결책을 찾기 위해 일주일 동안 노력했습니다. 이 솔루션의 가정은 다음과 같습니다.
- 항상 진행률 대화 상자를 사용해야합니다
- 한 번에 하나의 작업 만 수행
- 전화가 회전하고 진행률 대화 상자가 자동으로 닫힐 때 작업을 지속해야합니다.
이행
이 게시물의 하단에있는 두 파일을 작업 공간에 복사해야합니다. 다음을 확인하십시오.
-
당신
Activity
의 모든 확장해야합니다BaseActivity
-
에서
onCreate()
에super.onCreate()
액세스해야하는 멤버를 초기화 한 후에서을 (를) 호출해야합니다ASyncTask
. 또한getContentViewId()
양식 레이아웃 ID를 제공하도록 대체 하십시오. -
활동에 의해 관리되는 대화 상자를 작성하려면
onCreateDialog()
평소처럼 무시 하십시오. -
AsyncTasks를 만들기위한 정적 정적 내부 클래스 샘플은 아래 코드를 참조하십시오. mResult에 결과를 저장하여 나중에 액세스 할 수 있습니다.
final static class MyTask extends SuperAsyncTask<Void, Void, Void> {
public OpenDatabaseTask(BaseActivity activity) {
super(activity, MY_DIALOG_ID); // change your dialog ID here...
// and your dialog will be managed automatically!
}
@Override
protected Void doInBackground(Void... params) {
// your task code
return null;
}
@Override
public boolean onAfterExecute() {
// your after execute code
}
}
마지막으로 새 작업을 시작하십시오.
mCurrentTask = new MyTask(this);
((MyTask) mCurrentTask).execute();
그게 다야! 이 강력한 솔루션이 누군가를 도울 수 있기를 바랍니다.
BaseActivity.java (가져 오기 구성)
protected abstract int getContentViewId();
public abstract class BaseActivity extends Activity {
protected SuperAsyncTask<?, ?, ?> mCurrentTask;
public HashMap<Integer, Boolean> mDialogMap = new HashMap<Integer, Boolean>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getContentViewId());
mCurrentTask = (SuperAsyncTask<?, ?, ?>) getLastNonConfigurationInstance();
if (mCurrentTask != null) {
mCurrentTask.attach(this);
if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
&& mDialogMap.get((Integer) mCurrentTask.dialogId)) {
mCurrentTask.postExecution();
}
}
}
@Override
protected void onPrepareDialog(int id, Dialog dialog) {
super.onPrepareDialog(id, dialog);
mDialogMap.put(id, true);
}
@Override
public Object onRetainNonConfigurationInstance() {
if (mCurrentTask != null) {
mCurrentTask.detach();
if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
&& mDialogMap.get((Integer) mCurrentTask.dialogId)) {
return mCurrentTask;
}
}
return super.onRetainNonConfigurationInstance();
}
public void cleanupTask() {
if (mCurrentTask != null) {
mCurrentTask = null;
System.gc();
}
}
}
SuperAsyncTask.java
public abstract class SuperAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
protected BaseActivity mActivity = null;
protected Result mResult;
public int dialogId = -1;
protected abstract void onAfterExecute();
public SuperAsyncTask(BaseActivity activity, int dialogId) {
super();
this.dialogId = dialogId;
attach(activity);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
mActivity.showDialog(dialogId); // go polymorphism!
}
protected void onPostExecute(Result result) {
super.onPostExecute(result);
mResult = result;
if (mActivity != null &&
mActivity.mDialogMap.get((Integer) dialogId) != null
&& mActivity.mDialogMap.get((Integer) dialogId)) {
postExecution();
}
};
public void attach(BaseActivity activity) {
this.mActivity = activity;
}
public void detach() {
this.mActivity = null;
}
public synchronized boolean postExecution() {
Boolean dialogExists = mActivity.mDialogMap.get((Integer) dialogId);
if (dialogExists != null || dialogExists) {
onAfterExecute();
cleanUp();
}
public boolean cleanUp() {
mActivity.removeDialog(dialogId);
mActivity.mDialogMap.remove((Integer) dialogId);
mActivity.cleanupTask();
detach();
return true;
}
}
답변
Google 직원이 “공식 솔루션”을 제공 했습니까?
예.
솔루션은 단지 일부 코드 보다는 응용 프로그램 아키텍처 제안에 가깝습니다 .
그들은 응용 프로그램 상태에 관계없이 응용 프로그램이 서버와 동기화되어 작동 할 수있게 해주는 3 가지 디자인 패턴 을 제안 했습니다 (사용자가 응용 프로그램을 마치고 사용자가 화면을 변경하고 응용 프로그램이 종료되는 경우 가능한 모든 다른 상태에서도 작동합니다) 백그라운드 데이터 작업이 뒤섞 일 수 있습니다.
제안은 Virgil Dobjanschi의 Google I / O 2010 중 Android REST 클라이언트 애플리케이션 연설 에서 설명됩니다 . 길이는 1 시간이지만 볼 가치가 있습니다.
그 기초는 네트워크 작업을 응용 프로그램에서 Service
독립적으로 작동 하는 네트워크 작업으로 추상화하는 것 입니다 Activity
. 당신이 데이터베이스 작업하는 경우, 사용 ContentResolver
하고 Cursor
당신에게 아웃 – 오브 – 박스 줄 것 옵저버 패턴 당신이 가져온 원격 데이터와 로컬 데이터베이스를 업데이트하면, 어떤이 추가적으로 로직없이 UI를 업데이트하는 것이 편리하다. 다른 모든 작업 후 코드는 콜백을 통해 실행됩니다 Service
( ResultReceiver
서브 클래스를 사용합니다).
어쨌든, 나의 설명은 실제로 매우 모호합니다. 당신은 분명히 연설을 봐야합니다.
답변
Mark의 (CommonsWare) 답변은 실제로 방향 변경에 작동하지만 전화 통화의 경우와 같이 활동이 직접 파괴되면 실패합니다.
Application 개체를 사용하여 ASyncTask를 참조하면 방향 변경 및 드물게 파괴 된 Activity 이벤트를 처리 할 수 있습니다.
여기에 문제와 해결책에 대한 훌륭한 설명이 있습니다 .
크레디트는이 사실을 알아 낸 라이언에게 완전히 간다.
답변
4 년 후 Google은 Activity onCreate에서 setRetainInstance (true)를 호출하여 문제를 해결했습니다. 장치 회전 중에 활동 인스턴스를 유지합니다. 구형 Android를위한 간단한 솔루션도 있습니다.
답변
활동 핸들러를 사용하여 모든 활동 조치를 호출해야합니다. 따라서 스레드가있는 경우 Runnable을 만들고 Activitie의 처리기를 사용하여 게시해야합니다. 그렇지 않으면 치명적인 예외로 인해 앱이 충돌하는 경우가 있습니다.