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 |