سؤال

أقوم بتحويل الكود الخاص بي من استخدام Handler إلى AsyncTask. هذا الأخير رائع في ما يفعله - التحديثات غير المتزامنة والتعامل مع النتائج في موضوع واجهة المستخدم الرئيسية. ما هو غير واضح بالنسبة لي هو كيفية التعامل مع الاستثناءات إذا كان هناك شيء ما AsyncTask#doInBackground.

الطريقة التي أقوم بها هي أن يكون لديك معالج خطأ وإرسال رسائل إليه. إنه يعمل بشكل جيد ، ولكن هل هو النهج "الصحيح" أم أن هناك بديل أفضل؟

كما أفهم أنه إذا حددت معالج الخطأ كحقل نشاط ، فيجب أن يتم تنفيذها في مؤشر ترابط واجهة المستخدم. ومع ذلك ، في بعض الأحيان (بشكل غير متوقع للغاية) سأحصل على استثناء يقول إن الرمز ينتج عنه 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;
    }
}

أعد هذا الكائن من أساليب DoinCtask Asynctask الخاصة بك وتحقق منه في postexecute. (يمكنك استخدام هذه الفئة كفئة أساسية لمهام Async الأخرى)

فيما يلي مجموعة من المهمة التي تحصل على استجابة 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, والتي يتم تمريرها كمعلمة. في معظم الحالات ، تؤدي الاستثناءات إلى نوع من إخراج واجهة المستخدم ، لذلك onPostExecute هو مكان مثالي للقيام بذلك.

إذا كنت ترغب في استخدام Framework Roboguice الذي يجلب لك مزايا أخرى ، فيمكنك تجربة RoboasyNctask التي لديها رد اتصال إضافي Onexception (). يعمل جيدًا وأستخدمه.http://code.google.com/p/roboguice/wiki/RoboasyNctask

لقد صنعت الفئة الفرعية الخاصة بي من Asynctask مع واجهة تحدد عمليات الاسترجاعات للنجاح والفشل. لذا ، إذا تم إلقاء استثناء في Asynctask الخاص بك ، يتم تمرير وظيفة OnFaileure الاستثناء ، وإلا فإن رد الاتصال 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);
    }
}

حل أكثر شمولا ل كاجاتاي كالانيظهر الحل أدناه:

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;
    }
}

استثناء HandlingAnctask

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:

إلغاء منطقي النهائي العام (Boolean Mayinterrupruprunning)

محاولات إلغاء تنفيذ هذه المهمة. ستفشل هذه المحاولة في حالة الانتهاء من المهمة بالفعل ، أو تم إلغاؤها بالفعل ، أو لا يمكن إلغاؤها لسبب آخر. إذا نجحت ، ولم تبدأ هذه المهمة عند استدعاء الإلغاء ، يجب ألا يتم تشغيل هذه المهمة أبدًا. إذا كانت المهمة قد بدأت بالفعل ، فإن معلمة MayinTrupruptifrunning تحدد ما إذا كان ينبغي مقاطعة مؤشر الترابط الذي ينفذ هذه المهمة في محاولة لإيقاف المهمة.

ستؤدي استدعاء هذه الطريقة إلى استدعاء (كائن) مُحسّن على مؤشر ترابط واجهة المستخدم بعد عودة doinbackground (Object []). يضمن استدعاء هذه الطريقة أن onpostexecute (كائن) لم يتم الاحتجاج به أبدًا. بعد استدعاء هذه الطريقة ، يجب عليك التحقق من القيمة التي يتم إرجاعها بواسطة Iscancelled () بشكل دوري من DoInbackground (Object []) لإنهاء المهمة في أقرب وقت ممكن.

لذلك يمكنك الاتصال بالإلغاء في بيان الصيد والتأكد من عدم استدعاء onpostexcute أبدًا ، ولكن بدلاً من ذلك يتم استدعاء 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 متغير والوصول إلى الاستثناء.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top