Android에서 Asynctask 및 오류 처리
-
20-09-2019 - |
문제
코드를 사용하여 변환하고 있습니다 Handler
에게 AsyncTask
. 후자는 기본 UI 스레드에서 비동기 업데이트 및 결과 처리에 능숙합니다. 나에게 불분명 한 것은 무언가가 건초 와이어에 들어가면 예외를 처리하는 방법입니다. AsyncTask#doInBackground
.
내가하는 방식은 오류 핸들러가 있고 메시지를 보내는 것입니다. 잘 작동하지만 "올바른"접근 방식입니까, 아니면 더 나은 대안이 있습니까?
또한 오류 핸들러를 활동 필드로 정의하면 UI 스레드에서 실행해야한다는 것을 이해합니다. 그러나 때때로 (매우 예측할 수 없을 정도로) 코드가 트리거된다는 예외를 얻을 것입니다. Handler#handleMessage
잘못된 스레드에서 실행 중입니다. 오류 처리기를 초기화해야합니까? Activity#onCreate
대신에? 자본 매출 runOnUiThread
~ 안으로 Handler#handleMessage
중복 된 것처럼 보이지만 매우 안정적으로 실행됩니다.
해결책
잘 작동하지만 "올바른"접근 방식이며 더 나은 대안이 있습니까?
나는 붙잡고있다 Throwable
또는 Exception
에서 AsyncTask
인스턴스 자체를 사용하여 무언가를 수행합니다 onPostExecute()
, 오류 처리에는 화면에 대화 상자를 표시 할 수있는 옵션이 있습니다.
다른 팁
비동기 객체 만들기 (다른 프로젝트에서도 사용할 수 있음)
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 메소드 에서이 개체를 반환하고 사후에 확인하십시오. (이 클래스를 다른 비동기 작업의 기본 클래스로 사용할 수 있습니다)
아래는 웹 서버에서 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 프레임 워크를 사용하려면 다른 혜택을 제공하는 다른 혜택을 제공 할 수 있습니다. 정말 잘 작동하고 사용합니다.http://code.google.com/p/roboguice/wiki/roboasynctask
성공과 실패를위한 콜백을 정의하는 인터페이스로 내 자신의 Asynctask 서브 클래스를 만들었습니다. 따라서 Asynctask에서 예외가 발생하면 Onfailure 기능이 예외가 전달됩니다. 그렇지 않으면 Onsuccess 콜백이 결과가 전달됩니다. Android가 더 나은 것을 가지고 있지 않은 이유는 저를 넘어선 것입니다.
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;
}
}
예외 처리장 입력
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);
}
가변 회원 공유에 의존하지 않는 또 다른 방법은 취소를 사용하는 것입니다.
이것은 Android 문서에서 나온 것입니다.
공개 최종 부울 취소 (부울 Mayinterruptifrunning)
이 작업의 실행을 취소하려고 시도합니다. 작업이 이미 완료되었거나 이미 취소되었거나 다른 이유로 취소 할 수없는 경우이 시도는 실패합니다. 성공하고 취소가 호출 될 때이 작업이 시작되지 않은 경우이 작업은 실행되지 않아야합니다. 작업이 이미 시작된 경우, MayinterRupRifrunning 매개 변수는이 작업을 실행하는 스레드가 작업을 중지하려는 시도에서 중단되어야하는지 여부를 결정합니다.
이 메소드를 호출하면 doinbackground (Object [])가 반환 한 후 UI 스레드에서 ancancelled (객체)가 호출됩니다. 이 방법을 호출하면 OnPostExecute (Object)가 호출되지 않도록 보장합니다. 이 메소드를 호출 한 후에는 doinbackground (Object [])에서 iscancelled ()에 의해 반환 된 값을 최대한 빨리 완료하려면 작업을 완료해야합니다.
따라서 Catch 문서에서 취소를 호출하고 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"로 가서 Foling을 수행하십시오.
@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
변수 및 예외에 액세스하십시오.