Android에서 AsyncTask 및 오류 처리
내가 사용하는 내 코드를 변환하고 있습니다 Handler에 AsyncTask. 후자는 기본 UI 스레드에서 비동기 업데이트 및 결과 처리와 같은 기능을 수행합니다. 나에게 분명하지 않은 것은 무언가가 haywire로 들어가면 예외를 처리하는 방법입니다 AsyncTask#doInBackground.
내가하는 방법은 오류 처리기가 있고 메시지를 보내는 것입니다. 그것은 잘 작동하지만 "올바른"접근법입니까 아니면 더 나은 대안이 있습니까?
또한 오류 처리기를 활동 필드로 정의하면 UI 스레드에서 실행되어야한다는 것을 이해합니다. 그러나 때로는 (매우 예기치 않게) 트리거 된 코드 Handler#handleMessage가 잘못된 스레드 에서 실행되고 있다는 예외 가 발생 합니다. Activity#onCreate대신 오류 처리기를 초기화해야합니까 ? 배치 는 중복 runOnUiThread된 Handler#handleMessage것처럼 보이지만 매우 안정적으로 실행됩니다.
잘 작동하지만 "올바른"접근 방식이며 더 나은 대안이 있습니까?
나는 인스턴스 자체 Throwable또는 인스턴스를 잡고 Exception에서 AsyncTask무언가를 수행 onPostExecute()하므로 오류 처리에는 화면에 대화 상자를 표시하는 옵션이 있습니다.
AsyncResult 개체를 만듭니다 (다른 프로젝트에서도 사용할 수 있음).
public class AsyncTaskResult<T> {
private T result;
private Exception error;
public T getResult() {
return result;
}
public Exception getError() {
return error;
}
public AsyncTaskResult(T result) {
super();
this.result = result;
}
public AsyncTaskResult(Exception error) {
super();
this.error = error;
}
}
AsyncTask doInBackground 메소드에서이 오브젝트를 리턴하고 postExecute에서 점검하십시오. (이 클래스를 다른 비동기 작업의 기본 클래스로 사용할 수 있습니다)
아래는 웹 서버에서 JSON 응답을 얻는 작업 모형입니다.
AsyncTask<Object,String,AsyncTaskResult<JSONObject>> jsonLoader = new AsyncTask<Object, String, AsyncTaskResult<JSONObject>>() {
@Override
protected AsyncTaskResult<JSONObject> doInBackground(
Object... params) {
try {
// get your JSONObject from the server
return new AsyncTaskResult<JSONObject>(your json object);
} catch ( Exception anyError) {
return new AsyncTaskResult<JSONObject>(anyError);
}
}
protected void onPostExecute(AsyncTaskResult<JSONObject> result) {
if ( result.getError() != null ) {
// error handling here
} else if ( isCancelled()) {
// cancel handling here
} else {
JSONObject realResult = result.getResult();
// result handling here
}
};
}
예외를 AsyncTask올바르게 처리해야한다고 생각하면 이것을 슈퍼 클래스로 사용합니다.
public abstract class ExceptionAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
private Exception exception=null;
private Params[] params;
@Override
final protected Result doInBackground(Params... params) {
try {
this.params = params;
return doInBackground();
}
catch (Exception e) {
exception = e;
return null;
}
}
abstract protected Result doInBackground() throws Exception;
@Override
final protected void onPostExecute(Result result) {
super.onPostExecute(result);
onPostExecute(exception, result);
}
abstract protected void onPostExecute(Exception exception, Result result);
public Params[] getParams() {
return params;
}
}
일반적으로 doInBackground백그라운드 작업을 수행하기 위해 서브 클래스에서 재정 의하여 필요한 경우 예외를 행복하게 처리합니다. 그런 다음 onPostExecute추상적이기 때문에 구현해야 Exception하며 매개 변수로 전달되는 모든 유형을 처리하도록 부드럽게 상기시킵니다 . 대부분의 경우 예외는 일부 유형의 UI 출력으로 이어 지므로 onPostExecute이를 수행하기에 완벽한 장소입니다.
다른 이점을 제공하는 RoboGuice 프레임 워크를 사용하려면 추가 콜백 onException ()이있는 RoboAsyncTask를 사용해보십시오. 잘 작동하고 사용합니다. http://code.google.com/p/roboguice/wiki/RoboAsyncTask
성공과 실패에 대한 콜백을 정의하는 인터페이스를 사용하여 자체 AsyncTask 하위 클래스를 만들었습니다. 따라서 AsyncTask에서 예외가 발생하면 onFailure 함수가 예외를 전달하고 그렇지 않으면 onSuccess 콜백이 결과를 전달합니다. 안드로이드에 더 나은 무언가가없는 이유는 저 밖에 있습니다.
public class SafeAsyncTask<inBackgroundType, progressType, resultType>
extends AsyncTask<inBackgroundType, progressType, resultType> {
protected Exception cancelledForEx = null;
protected SafeAsyncTaskInterface callbackInterface;
public interface SafeAsyncTaskInterface <cbInBackgroundType, cbResultType> {
public Object backgroundTask(cbInBackgroundType[] params) throws Exception;
public void onCancel(cbResultType result);
public void onFailure(Exception ex);
public void onSuccess(cbResultType result);
}
@Override
protected void onPreExecute() {
this.callbackInterface = (SafeAsyncTaskInterface) this;
}
@Override
protected resultType doInBackground(inBackgroundType... params) {
try {
return (resultType) this.callbackInterface.backgroundTask(params);
} catch (Exception ex) {
this.cancelledForEx = ex;
this.cancel(false);
return null;
}
}
@Override
protected void onCancelled(resultType result) {
if(this.cancelledForEx != null) {
this.callbackInterface.onFailure(this.cancelledForEx);
} else {
this.callbackInterface.onCancel(result);
}
}
@Override
protected void onPostExecute(resultType result) {
this.callbackInterface.onSuccess(result);
}
}
Cagatay Kalan 솔루션에 대한보다 포괄적 인 솔루션 은 다음과 같습니다.
AsyncTaskResult
public class AsyncTaskResult<T>
{
private T result;
private Exception error;
public T getResult()
{
return result;
}
public Exception getError()
{
return error;
}
public AsyncTaskResult(T result)
{
super();
this.result = result;
}
public AsyncTaskResult(Exception error) {
super();
this.error = error;
}
}
ExceptionHandlingAsyncTask
public abstract class ExceptionHandlingAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, AsyncTaskResult<Result>>
{
private Context context;
public ExceptionHandlingAsyncTask(Context context)
{
this.context = context;
}
public Context getContext()
{
return context;
}
@Override
protected AsyncTaskResult<Result> doInBackground(Params... params)
{
try
{
return new AsyncTaskResult<Result>(doInBackground2(params));
}
catch (Exception e)
{
return new AsyncTaskResult<Result>(e);
}
}
@Override
protected void onPostExecute(AsyncTaskResult<Result> result)
{
if (result.getError() != null)
{
onPostException(result.getError());
}
else
{
onPostExecute2(result.getResult());
}
super.onPostExecute(result);
}
protected abstract Result doInBackground2(Params... params);
protected abstract void onPostExecute2(Result result);
protected void onPostException(Exception exception)
{
new AlertDialog.Builder(context).setTitle(R.string.dialog_title_generic_error).setMessage(exception.getMessage())
.setIcon(android.R.drawable.ic_dialog_alert).setPositiveButton(R.string.alert_dialog_ok, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
//Nothing to do
}
}).show();
}
}
작업 예
public class ExampleTask extends ExceptionHandlingAsyncTask<String, Void, Result>
{
private ProgressDialog dialog;
public ExampleTask(Context ctx)
{
super(ctx);
dialog = new ProgressDialog(ctx);
}
@Override
protected void onPreExecute()
{
dialog.setMessage(getResources().getString(R.string.dialog_logging_in));
dialog.show();
}
@Override
protected Result doInBackground2(String... params)
{
return new Result();
}
@Override
protected void onPostExecute2(Result result)
{
if (dialog.isShowing())
dialog.dismiss();
//handle result
}
@Override
protected void onPostException(Exception exception)
{
if (dialog.isShowing())
dialog.dismiss();
super.onPostException(exception);
}
}
이 간단한 수업은 당신을 도울 수 있습니다
public abstract class ExceptionAsyncTask<Param, Progress, Result, Except extends Throwable> extends AsyncTask<Param, Progress, Result> {
private Except thrown;
@SuppressWarnings("unchecked")
@Override
/**
* Do not override this method, override doInBackgroundWithException instead
*/
protected Result doInBackground(Param... params) {
Result res = null;
try {
res = doInBackgroundWithException(params);
} catch (Throwable e) {
thrown = (Except) e;
}
return res;
}
protected abstract Result doInBackgroundWithException(Param... params) throws Except;
@Override
/**
* Don not override this method, override void onPostExecute(Result result, Except exception) instead
*/
protected void onPostExecute(Result result) {
onPostExecute(result, thrown);
super.onPostExecute(result);
}
protected abstract void onPostExecute(Result result, Except exception);
}
가변 멤버 공유에 의존하지 않는 또 다른 방법은 cancel을 사용하는 것입니다.
이것은 안드로이드 문서에서 온 것입니다 :
공개 최종 부울 취소 (boolean mayInterruptIfRunning)
이 작업의 실행을 취소하려고합니다. 작업이 이미 완료되었거나 이미 취소되었거나 다른 이유로 취소 할 수없는 경우이 시도는 실패합니다. 성공하고 cancel이 호출 될 때이 태스크가 시작되지 않은 경우이 태스크는 절대 실행되지 않아야합니다. 작업이 이미 시작된 경우 mayInterruptIfRunning 매개 변수는 작업을 중지하려는 시도에서이 작업을 실행하는 스레드를 중단해야하는지 여부를 결정합니다.
이 메소드를 호출하면 doInBackground (Object [])가 반환 된 후 UI 스레드에서 onCancelled (Object)가 호출됩니다. 이 메소드를 호출하면 onPostExecute (Object)가 호출되지 않습니다. 이 메소드를 호출 한 후 doInBackground (Object [])에서 isCancelled ()에 의해 리턴 된 값을 점검하여 가능한 빨리 태스크를 완료해야합니다.
따라서 catch 문에서 cancel을 호출하고 onPostExcute가 호출되지 않지만 UI 스레드에서 onCancelled가 호출되는지 확인할 수 있습니다. 따라서 오류 메시지를 표시 할 수 있습니다.
실제로 AsyncTask는 FutureTask & Executor를 사용하고 FutureTask는 예외 체인을 지원합니다. 먼저 도우미 클래스를 정의 해 봅시다.
public static class AsyncFutureTask<T> extends FutureTask<T> {
public AsyncFutureTask(@NonNull Callable<T> callable) {
super(callable);
}
public AsyncFutureTask<T> execute(@NonNull Executor executor) {
executor.execute(this);
return this;
}
public AsyncFutureTask<T> execute() {
return execute(AsyncTask.THREAD_POOL_EXECUTOR);
}
@Override
protected void done() {
super.done();
//work done, complete or abort or any exception happen
}
}
둘째, 사용합시다
try {
Log.d(TAG, new AsyncFutureTask<String>(new Callable<String>() {
@Override
public String call() throws Exception {
//throw Exception in worker thread
throw new Exception("TEST");
}
}).execute().get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
//catch the exception throw by worker thread in main thread
e.printStackTrace();
}
개인적으로이 접근법을 사용하겠습니다. 정보가 필요한 경우 예외를 잡아서 스택 추적을 인쇄 할 수 있습니다.
백그라운드에서 작업이 부울 값을 반환하도록합니다.
다음과 같습니다.
@Override
protected Boolean doInBackground(String... params) {
return readXmlFromWeb(params[0]);
}
@Override
protected void onPostExecute(Boolean result) {
if(result){
// no error
}
else{
// error handling
}
}
또 다른 가능성은 Object리턴 유형 으로 사용 onPostExecute()하고 오브젝트 유형 을 확인하는 것입니다. 짧습니다.
class MyAsyncTask extends AsyncTask<MyInObject, Void, Object> {
@Override
protected AsyncTaskResult<JSONObject> doInBackground(MyInObject... myInObjects) {
try {
MyOutObject result;
// ... do something that produces the result
return result;
} catch (Exception e) {
return e;
}
}
protected void onPostExecute(AsyncTaskResult<JSONObject> outcome) {
if (outcome instanceof MyOutObject) {
MyOutObject result = (MyOutObject) outcome;
// use the result
} else if (outcome instanceof Exception) {
Exception e = (Exception) outcome;
// show error message
} else throw new IllegalStateException();
}
}
올바른 예외를 알고 있다면
Exception e = null;
publishProgress(int ...);
예 :
@Override
protected Object doInBackground(final String... params) {
// TODO Auto-generated method stub
try {
return mClient.call(params[0], params[1]);
} catch(final XMLRPCException e) {
// TODO Auto-generated catch block
this.e = e;
publishProgress(0);
return null;
}
}
"onProgressUpdate"로 가서 다음 작업을 수행하십시오.
@Override
protected void onProgressUpdate(final Integer... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
mDialog.dismiss();
OptionPane.showMessage(mActivity, "Connection error", e.getMessage());
}
일부 경우에만 도움이됩니다. 또한 Global Exception변수 를 유지 하고 예외에 액세스 할 수 있습니다 .
참고 URL : https://stackoverflow.com/questions/1739515/asynctask-and-error-handling-on-android
'programing tip' 카테고리의 다른 글
| iPhone-그랜드 센트럴 디스패치 메인 스레드 (0) | 2020.06.15 |
|---|---|
| 사용하는 방법 ? (0) | 2020.06.15 |
| IQueryable과 IEnumerable의 차이점은 무엇입니까 (0) | 2020.06.14 |
| EditText가 포커스를 잃을 때 어떻게 알 수 있습니까? (0) | 2020.06.14 |
| MySQL에서 하나의 행을 복사하여 동일한 테이블에 삽입 할 수 있습니까? (0) | 2020.06.14 |