Android'de AsyncTask ve hata işleme


147

Ben kullanmasını kodumu dönüştürme ediyorum Handleriçin AsyncTask. İkincisi yaptığı işte harika - asenkronize güncellemeler ve sonuçların ana kullanıcı arayüzü iş parçacığında işlenmesi. Benim için belirsiz olan şey, bir şey içeri girerse istisnaların nasıl ele alınacağıdır AsyncTask#doInBackground.

Bunu yapmak için bir hata işleyici ve ona mesaj göndermek etmektir. İyi çalışıyor, ancak "doğru" yaklaşım mı yoksa daha iyi bir alternatif var mı?

Ayrıca hata işleyicisi bir Etkinlik alanı olarak tanımlamak, UI iş parçacığında yürütmek gerektiğini anlıyorum. Ancak, bazen (çok öngörülemez) Ben tetiklenen kod Handler#handleMessageyanlış iş parçacığında yürütülüyor söyleyerek bir istisna alırsınız . Bunun Activity#onCreateyerine hata işleyiciyi başlatmalı mıyım ? Yerleştirme runOnUiThreadiçine Handler#handleMessagegereksiz görünüyor ama çok güvenilir bir yürütür.


Kodunuzu neden dönüştürmek istediniz? Bunun iyi bir nedeni var mıydı?
HGPB

4
@Haraldo daha iyi bir kodlama uygulaması en azından ben böyle hissediyorum
Bostone

Yanıtlar:


178

İyi çalışıyor ama "doğru" yaklaşım mı ve daha iyi bir alternatif var mı?

Tutunacak Throwableveya Exceptioniçinde AsyncTaskörneğin kendisi ve sonra onunla bir şeyler yapmak onPostExecute()benim hata işleme ekrandaki bir iletişim görüntüleme seçeneği vardır, böylece.


8
Parlak! Artık Handlers ile maymuna gerek yok
Bostone

5
Atılabilir veya İstisna üzerine tutmam gereken yol bu mu? "Kendi AsyncTask alt sınıfınıza, arka plan işleminizin sonucunu tutacak bir örnek değişkeni ekleyin." Bir istisna aldığınızda, istisnayı (veya başka bir hata dizesini / kodunu) bu değişkende saklayın. OnPostExecute çağrıldığında, bu örnek değişkeninin bir hataya ayarlanıp ayarlanmadığına bakın. Eğer öyleyse, bir hata mesajı gösterin. "(" Boston Sokakları "kullanıcısından groups.google.com/group/android-developers/browse_thread/thread/… )
OneWorld

1
@OneWorld: Evet, bu iyi olmalı.
CommonsWare

2
Merhaba CW, lütfen bunu daha ayrıntılı bir şekilde yapma şeklinizi açıklar mısınız - lütfen kısa bir kod örneğiyle? Çok teşekkürler!!
Bruiser


140

Bir AsyncResult nesnesi oluşturun (diğer projelerde de kullanabilirsiniz)

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

Bu nesneyi AsyncTask doInBackground yöntemlerinizden döndürün ve postExecute içinde denetleyin. (Bu sınıfı diğer eşzamansız görevleriniz için temel sınıf olarak kullanabilirsiniz)

Aşağıda, web sunucusundan JSON yanıtı alan bir görevin maketi bulunmaktadır.

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

    }

1
Bunu sevdim. Güzel kapsülleme. Bu orijinal cevabın bir açıklaması olduğu için cevap kalır ancak bu kesinlikle bir noktayı hak ediyor
Bostone

Bu Generics'in ne kadar yararlı olabileceğinin güzel bir gösterisidir. Karmaşıklık açısından garip bir koku atıyor, ama gerçekten eklemleyebileceğim bir şekilde değil.
num1

4
Güzel fikir, sadece bir soru: neden diyorsunuz super()içinde AsyncTaskResultsınıf şey uzatmaz zaman?
donturner

7
"zararsız" - yedekli kod okunabilirlik ve bakım için her zaman zararlıdır. Çık oradan! :)
donturner

2
Gerçekten çözümü sevdim ... düşünmek için geliyor - C # çocuklar C # karşılık gelen BackgroundTask yerel uygulamada tam olarak aynı yöntemi kullandı ...
Vova

11

İstisnaları AsyncTaskdüzgün bir şekilde ele almam gerektiğini hissettiğimde , bunu süper sınıf olarak kullanıyorum:

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

}

Normal olarak, doInBackgroundarka plan çalışması yapmak için alt sınıfınızda geçersiz kılınır ve gerektiğinde İstisnalar atılır. Daha sonra uygulamak zorundasınız onPostExecute(soyut olduğu için) ve bu size Exceptionparametre olarak geçirilen her türlü işlemeyi nazikçe hatırlatır . Çoğu durumda, İstisnalar bir tür ui çıktısına yol açar, bu nedenle onPostExecutebunu yapmak için mükemmel bir yerdir.


1
Whoa, neden sadece paramsöne geçmiyorsunuz , bu yüzden orijinaline daha çok benziyor ve taşınması daha kolay?
TWiStErRob

@TWiStErRob bu fikirde yanlış bir şey yok. Params kullanma eğiliminde olduğum için bu kişisel bir tercih meselesi. Ben tercih new Task("Param").execute()üzerinde new Task().execute("Param").
sulai

5

Size başka avantajlar getiren RoboGuice çerçevesini kullanmak isterseniz, ekstra bir Geri Arama onException () özelliğine sahip RoboAsyncTask'ı deneyebilirsiniz. Gerçekten iyi çalışıyor ve kullanıyorum. http://code.google.com/p/roboguice/wiki/RoboAsyncTask


bununla ilgili deneyiminiz nedir? oldukça kararlı mı?
nickaknudson

RoboGuicehâlâ hayatta? Seems 2012 yılından beri güncellenmedi mi?
Dimitry K

Hayır, RoboGuice öldü ve kullanımdan kaldırıldı. Dagger2 önerilen değiştirmedir, ancak yalnızca çıplak kemikli bir DI kütüphanesidir.
Avi Cherry

3

Başarı ve başarısızlık için geri çağrıları tanımlayan bir arayüz ile kendi AsyncTask alt sınıfımı yaptım. Bu nedenle, AsyncTask'ınıza bir istisna atılırsa, onFailure işlevi istisnayı, aksi halde onSuccess geri araması sonucunuzu iletir. Neden android daha iyi kullanılabilir bir şey yok benden öte.

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

3

Çağatay Kalan'ın çözümü için daha kapsamlı bir çözüm aşağıda gösterilmiştir:

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

Örnek Görev

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

MyActivity.getApplicationContext (). GetResources ()
Stephane

2

Bu basit sınıf size yardımcı olabilir

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

2

Değişken üye paylaşımına bağlı olmayan başka bir yol da iptal kullanmaktır.

Bu android belgelerinden:

genel son boole iptal (boolean mayInterruptIfRunning)

Bu görevin yürütülmesini iptal etme girişimleri. Görev zaten tamamlanmış, zaten iptal edilmişse veya başka bir nedenle iptal edilememişse bu girişim başarısız olur. Başarılı olursa ve iptal çağrıldığında bu görev başlatılmadıysa, bu görev asla çalıştırılmamalıdır. Görev zaten başlamışsa, mayInterruptIfRunning parametresi, görevi durdurma girişiminde bu görevi yürüten iş parçacığının kesilip kesilmeyeceğini belirler.

Bu yöntemin çağrılması, doInBackground (Object []) döndükten sonra onCancelled (Object) öğesinin UI iş parçacığında çağrılmasına neden olur. Bu yöntemin çağrılması onPostExecute (Object) öğesinin hiçbir zaman çağrılmadığını garanti eder. Bu yöntemi çağırdıktan sonra, görevi olabildiğince erken bitirmek için isCancelled () tarafından doInBackground'dan (Object []) düzenli olarak döndürülen değeri kontrol etmelisiniz.

Böylece catch deyiminde cancel çağrısı yapabilir ve onPostExcute öğesinin hiçbir zaman çağrılmadığından emin olabilirsiniz, bunun yerine onCancelled kullanıcı arabiriminde çağrılır. Böylece hata mesajını gösterebilirsiniz.


Hata mesajını düzgün bir şekilde gösteremezsiniz, çünkü sorunu bilmiyorsanız (İstisna), yine de bir AsyncTaskResult'u yakalamanız ve geri göndermeniz gerekir. Ayrıca bir kullanıcının iptal etmesi bir hata değildir, beklenen bir etkileşimdir: Bunları nasıl ayırt edersiniz?
TWiStErRob

cancel(boolean)onCancelled()bu da başlangıçtan beri var olma çağrısıyla sonuçlandı , ancak onCancelled(Result)API 11'e eklendi .
TWiStErRob

0

Aslında, AsyncTask FutureTask & Executor'u kullanıyor, FutureTask destek istisna zinciri Önce bir yardımcı sınıf tanımlayalım

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

İkincisi, kullanalım

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

-2

Şahsen ben bu yaklaşımı kullanacağım. İstisnaları yakalayabilir ve bilgiye ihtiyacınız varsa yığın izlemesini yazdırabilirsiniz.

arka plandaki görevinizin bir boole değeri döndürmesini sağlayın.

bunun gibi:

    @Override
                protected Boolean doInBackground(String... params) {
                    return readXmlFromWeb(params[0]);
         }

        @Override
                protected void onPostExecute(Boolean result) {

              if(result){
              // no error
               }
              else{
                // error handling
               }
}

-2

Başka bir olasılık Object, dönüş türü olarak ve onPostExecute()nesne türünü kontrol etmek için kullanmak olabilir . O kısa.

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

1
tamamen alakasız
Dinu

-2

Doğru istisnayı biliyorsanız,

Exception e = null;

publishProgress(int ...);

Örneğin:

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

ve "onProgressUpdate" e gidin ve

@Override
protected void onProgressUpdate(final Integer... values) {

    // TODO Auto-generated method stub
    super.onProgressUpdate(values);
    mDialog.dismiss();
    OptionPane.showMessage(mActivity, "Connection error", e.getMessage());
}

Bu sadece bazı durumlarda yardımcı olacaktır. Ayrıca bir Global Exceptiondeğişkeni tutabilir ve istisnayı kullanabilirsiniz.


1
Lütfen bunu yapma. Bu gerçekten, gerçekten, kötü bir tarz!
JimmyB
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.