Ekran Döndürme sırasında AsyncTask nasıl yapılır?


88

Örnek durumumu nasıl kaydedeceğime veya ekran döndürme sırasında yok olan etkinliğimin üstesinden nasıl geleceğime dair çok şey okudum.

Görünüşe göre pek çok olasılık var ama AsyncTask'ın sonuçlarını almak için hangisinin en iyi olduğunu bulamadım.

Basitçe yeniden başlatılan ve isFinishing()etkinlik yöntemini çağıran ve etkinlik bitiyorsa hiçbir şeyi güncellemeyecekleri bazı AsyncTasks'ım var .

Sorun şu ki, başarısız olabilecek veya başarılı olabilecek bir web hizmetine istek yapan bir Görevim var ve görevi yeniden başlatmak kullanıcı için mali bir kayba neden olacak.

Bunu nasıl çözersiniz? Olası çözümlerin avantajları veya dezavantajları nelerdir?


1
Cevabımı burada görün . Aslında neyinsetRetainInstance(true) yararlı olduğu hakkında bu bilgiyi de bulabilirsiniz .
Timmmm

Yapacağım şey, asyncTask'ınızın yaptığı işlemi (bir iş parçacığında) gerçekleştiren yerel bir hizmet uygulamaktır. Sonuçları görüntülemek için verileri aktivitenize yayınlayın. Artık etkinlik yalnızca verilerin gösterilmesinden sorumludur ve işleme hiçbir zaman bir ekran döndürme ile kesintiye uğramaz.
Someone Somewhere

AsyncTask yerine AsyncTaskLoader kullanmaya ne dersiniz?
Sourangshu Biswas

Yanıtlar:


6

İlk önerim, bir ekran döndürmede (varsayılan davranış) aktivitenizin sıfırlanmasına gerçekten ihtiyacınız olduğundan emin olmak olacaktır. Rotasyonla ilgili her sorun yaşadığımda bu özelliği <activity>AndroidManifest.xml'deki etiketime ekledim ve gayet iyi.

android:configChanges="keyboardHidden|orientation"

Garip görünüyor, ancak onConfigurationChanged()yönteminize ne veriyor, bir tane sağlamazsanız, düzeni yeniden ölçmekten başka bir şey yapmıyor, bu da çoğu zaman döndürmeyi ele almanın mükemmel bir yolu gibi görünüyor. .


5
Ancak bu, Aktivitenin düzeni değiştirmesini engelleyecektir. Ve bu nedenle, kullanıcıyı kendi ihtiyaçları tarafından değil, uygulamanızın belirlediği belirli bir yönde kullanmaya zorlar.
Janusz

77
Bu tekniği kullanmak, konfigürasyona özel kaynakları kolayca kullanmanızı engeller. Örneğin, mizanpajınızın veya çekmecelerinizin veya dizelerinizin veya dikey ve manzaralarda farklı olmasını istiyorsanız, varsayılan davranışı isteyeceksiniz. Yapılandırma değişikliğinin geçersiz kılınması yalnızca çok özel durumlarda (oyun, web tarayıcısı vb.) Yapılmalıdır ve kendinizi kısıtladığınız için tembellik veya rahatlık nedeniyle yapılmamalıdır.
Romain Guy

38
İşte bu kadar, Romain. "Mizanpajınızın veya çekmecelerinizin veya dizelerinizin veya portre ve manzaralarda farklı olmasını istiyorsanız, varsayılan davranışı isteyeceksiniz", inanıyorum ki bu sizin düşündüğünüzden çok daha nadir bir kullanım durumu. "Çok özel durumlar" dediğiniz şey, inandığım çoğu geliştiricidir. Tüm boyutlarda çalışan göreceli düzenleri kullanmak en iyi uygulamadır ve o kadar da zor değildir. Tembellikten bahsetmek oldukça yanlıştır, bu teknikler geliştirme süresini azaltmak değil, kullanıcı deneyimini iyileştirmek içindir.
Jim Blackler

2
Bunun LinearLayout için mükemmel çalıştığını buldum, ancak RelativeLayout kullanırken manzara moduna geçerken düzeni doğru şekilde yeniden çizmiyor (en azından N1'de değil). Şu sorulara bakın: stackoverflow.com/questions/2987049/…
JohnRock

9
Romain'e katılıyorum (ne hakkında konuştuğunu biliyor, işletim sistemini geliştiriyor). Uygulamanızı bir tablete taşımak istediğinizde ve kullanıcı arayüzünüz uzatıldığında korkunç göründüğünde ne olur? Bu cevaba yaklaşırsanız, tüm çözümünüzü yeniden kodlamanız gerekecek çünkü bu tembel hack ile gittiniz.
Austyn Mahoney

46

Code.google.com/p/shelvesAsyncTask adresinde s ve yön değişikliklerini nasıl ele aldığımı öğrenebilirsiniz . Bunu yapmanın çeşitli yolları var, bu uygulamada seçtiğim, şu anda çalışan herhangi bir görevi iptal etmek, durumunu kaydetmek ve yeni oluşturulduğunda kaydedilmiş durumla yeni bir tane başlatmak . Yapması kolaydır, iyi çalışır ve bir bonus olarak, kullanıcı uygulamadan ayrıldığında görevlerinizi durdurmaya özen gösterir.Activity

Ayrıca kullanabilirsiniz onRetainNonConfigurationInstance()Pass'iniz için AsyncTaskyeniye Activity(önceki sızıntı değil konuda dikkatli olmak Activitybu şekilde olsa.)


1
Bunu denedim ve kitap arama sırasında döndürme işlemi kesintiye
uğruyor

1
Bu kodda AsyncTask'ın tek bir kullanımını bulamadım. Benzer görünen bir sınıf UserTask var. Bu proje AsyncTask'tan önce mi geliyor?
devconsole

7
AsyncTask, UserTask'tan geldi. Başlangıçta kendi uygulamalarım için UserTask yazdım ve daha sonra bunu AsyncTask'a çevirdim. Üzgünüm, yeniden adlandırıldığını unuttum.
Romain Guy

@RomainGuy Merhaba, umarım iyisindir. Kodunuza göre ilk görev iptal olmasına rağmen sunucuya 2 istek gönderiliyor ancak başarılı bir şekilde iptal edilmiyor. Neden bilmiyorum Lütfen bana bunu çözmenin herhangi bir yolu olduğunu söyler misiniz?
iamcrypticcoder

10

Bu, Android ile ilgili gördüğüm en ilginç soru !!! Aslında son aylarda çözümü zaten arıyordum. Hala çözülmedi.

Dikkatli olun, basitçe

android:configChanges="keyboardHidden|orientation"

yeterli değil.

AsyncTask'ınız çalışırken kullanıcının bir telefon çağrısı aldığını düşünün. İsteğiniz zaten sunucu tarafından işleniyor, bu nedenle AsyncTask yanıt bekliyor. Şu anda, uygulamanız arka planda geçiyor, çünkü Telefon uygulaması daha yeni ön plana çıkıyor. İşletim sistemi, arka planda olduğu için aktivitenizi öldürebilir.


6

Neden her zaman Android tarafından sağlanan Singleton'daki mevcut AsyncTask'e bir referans tutmuyorsunuz?

PreExecute'ta veya oluşturucuda bir görev başladığında şunları tanımlarsınız:

((Application) getApplication()).setCurrentTask(asyncTask);

Ne zaman biterse onu null olarak ayarlayın.

Bu şekilde, her zaman belirli mantığınıza uygun şekilde onCreate veya onResume gibi bir şey yapmanıza izin veren bir referansınız olur:

this.asyncTaskReference = ((Application) getApplication()).getCurrentTask();

Boş ise, şu anda hiçbir şeyin çalışmadığını biliyorsunuz!

:-)


Bu işe yarayacak mı? Bunu test eden oldu mu? Bir telefon görüşmesi kesintisi olursa veya yeni bir aktiviteye gidip sonra geri dönersek görev sistem tarafından yine de öldürülür mü?
Robert

6
Applicationörneğin kendi yaşam döngüsü vardır - işletim sistemi tarafından da öldürülebilir, bu nedenle bu çözüm yeniden üretilmesi zor bir hataya neden olabilir.
Vit Khudenko

7
Düşündüm: Uygulama öldürülürse, tüm uygulama (ve dolayısıyla tüm AsyncTasks de) öldürülür.
manmal

Bence bu uygulama, tüm eşzamansız görevler olmadan da (çok nadir) kapatılabilir. Ancak @Arhimed, her eşzamansız görevin başlangıcında ve sonunda yapılması kolay bazı doğrulamalar ile hataları önleyebilirsiniz.
neteinstein



3

Benim bakış açıma göre, asenkron görevi onRetainNonConfigurationInstancemevcut Activity nesnesinden ayırarak ve oryantasyon değişikliğinden sonra yeni bir Activity nesnesine bağlayarak depolamak daha iyidir . Burada AsyncTask ve ProgressDialog ile nasıl çalışılacağına dair çok güzel bir örnek buldum.


2

Android: arka planda işleme / Yapılandırma değişikliğiyle Async Opeartion

Arka plan işlemi sırasında eşzamansız işlem durumlarını korumak için: parçalardan yardım alabilirsiniz.

Aşağıdaki adımlara bakın:

Adım 1: Başlıksız bir parça oluşturun, diyelim ki arka plan görevi ve içinde özel bir zaman uyumsuz görev sınıfı ekleyin.

2. Adım (İsteğe Bağlı Adım): Aktivitenizin üstüne bir yükleme imleci koymak istiyorsanız aşağıdaki kodu kullanın:

Adım 3: Ana Aktivitenizde, 1. adımda tanımlanan BackgroundTaskCallbacks arayüzünü uygulayın

class BackgroundTask extends Fragment {
public BackgroundTask() {

}

// Add a static interface 

static interface BackgroundTaskCallbacks {
    void onPreExecute();

    void onCancelled();

    void onPostExecute();

    void doInBackground();
}

private BackgroundTaskCallbacks callbacks;
private PerformAsyncOpeation asyncOperation;
private boolean isRunning;
private final String TAG = BackgroundTask.class.getSimpleName();

/**
 * Start the async operation.
 */
public void start() {
    Log.d(TAG, "********* BACKGROUND TASK START OPERATION ENTER *********");
    if (!isRunning) {
        asyncOperation = new PerformAsyncOpeation();
        asyncOperation.execute();
        isRunning = true;
    }
    Log.d(TAG, "********* BACKGROUND TASK START OPERATION EXIT *********");
}

/**
 * Cancel the background task.
 */
public void cancel() {
    Log.d(TAG, "********* BACKGROUND TASK CANCEL OPERATION ENTER *********");
    if (isRunning) {
        asyncOperation.cancel(false);
        asyncOperation = null;
        isRunning = false;
    }
    Log.d(TAG, "********* BACKGROUND TASK CANCEL OPERATION EXIT *********");
}

/**
 * Returns the current state of the background task.
 */
public boolean isRunning() {
    return isRunning;
}

/**
 * Android passes us a reference to the newly created Activity by calling
 * this method after each configuration change.
 */
public void onAttach(Activity activity) {
    Log.d(TAG, "********* BACKGROUND TASK ON ATTACH ENTER *********");
    super.onAttach(activity);
    if (!(activity instanceof BackgroundTaskCallbacks)) {
        throw new IllegalStateException(
                "Activity must implement the LoginCallbacks interface.");
    }

    // Hold a reference to the parent Activity so we can report back the
    // task's
    // current progress and results.
    callbacks = (BackgroundTaskCallbacks) activity;
    Log.d(TAG, "********* BACKGROUND TASK ON ATTACH EXIT *********");
}

public void onCreate(Bundle savedInstanceState) {
    Log.d(TAG, "********* BACKGROUND TASK ON CREATE ENTER *********");
    super.onCreate(savedInstanceState);
    // Retain this fragment across configuration changes.
    setRetainInstance(true);
    Log.d(TAG, "********* BACKGROUND TASK ON CREATE EXIT *********");
}

public void onDetach() {
    super.onDetach();
    callbacks = null;
}

private class PerformAsyncOpeation extends AsyncTask<Void, Void, Void> {
    protected void onPreExecute() {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON PRE EXECUTE ENTER *********");
        if (callbacks != null) {
            callbacks.onPreExecute();
        }
        isRunning = true;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON PRE EXECUTE EXIT *********");
    }

    protected Void doInBackground(Void... params) {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > DO IN BACKGROUND ENTER *********");
        if (callbacks != null) {
            callbacks.doInBackground();
        }
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > DO IN BACKGROUND EXIT *********");
        return null;
    }

    protected void onCancelled() {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON CANCEL ENTER *********");
        if (callbacks != null) {
            callbacks.onCancelled();
        }
        isRunning = false;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON CANCEL EXIT *********");
    }

    protected void onPostExecute(Void ignore) {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON POST EXECUTE ENTER *********");
        if (callbacks != null) {
            callbacks.onPostExecute();
        }
        isRunning = false;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON POST EXECUTE EXIT *********");
    }
}

public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    setRetainInstance(true);
}

public void onStart() {
    super.onStart();
}

public void onResume() {
    super.onResume();
}

public void onPause() {
    super.onPause();
}

public void onStop() {
    super.onStop();
}

public class ProgressIndicator extends Dialog {

public ProgressIndicator(Context context, int theme) {
    super(context, theme);
}

private ProgressBar progressBar;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.progress_indicator);
    this.setCancelable(false);
    progressBar = (ProgressBar) findViewById(R.id.progressBar);
    progressBar.getIndeterminateDrawable().setColorFilter(R.color.DarkBlue, android.graphics.PorterDuff.Mode.SCREEN);
}

@Override
public void show() {
    super.show();
}

@Override
public void dismiss() {
    super.dismiss();
}

@Override
public void cancel() {
    super.cancel();
}

public class MyActivity extends FragmentActivity implements BackgroundTaskCallbacks,{

private static final String KEY_CURRENT_PROGRESS = "current_progress";

ProgressIndicator progressIndicator = null;

private final static String TAG = MyActivity.class.getSimpleName();

private BackgroundTask task = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(//"set your layout here");
    initialize your views and widget here .............



    FragmentManager fm = getSupportFragmentManager();
    task = (BackgroundTask) fm.findFragmentByTag("login");

    // If the Fragment is non-null, then it is currently being
    // retained across a configuration change.
    if (task == null) {
        task = new BackgroundTask();
        fm.beginTransaction().add(task, "login").commit();
    }

    // Restore saved state
    if (savedInstanceState != null) {
        Log.i(TAG, "KEY_CURRENT_PROGRESS_VALUE ON CREATE :: "
                + task.isRunning());
        if (task.isRunning()) {
            progressIndicator = new ProgressIndicator(this,
                    R.style.TransparentDialog);
            if (progressIndicator != null) {
                progressIndicator.show();
            }
        }
    }
}

@Override
protected void onPause() {
    // TODO Auto-generated method stub
    super.onPause();

}

@Override
protected void onSaveInstanceState(Bundle outState) {
    // save the current state of your operation here by saying this 

    super.onSaveInstanceState(outState);
    Log.i(TAG, "KEY_CURRENT_PROGRESS_VALUE ON SAVE INSTANCE :: "
            + task.isRunning());
    outState.putBoolean(KEY_CURRENT_PROGRESS, task.isRunning());
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
    }
    progressIndicator = null;
}


private void performOperation() {

            if (!task.isRunning() && progressIndicator == null) {
                progressIndicator = new ProgressIndicator(this,
                        R.style.TransparentDialog);
                progressIndicator.show();
            }
            if (task.isRunning()) {
                task.cancel();
            } else {
                task.start();
            }
        }


@Override
protected void onDestroy() {
    super.onDestroy();
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
    }
    progressIndicator = null;
}

@Override
public void onPreExecute() {
    Log.i(TAG, "CALLING ON PRE EXECUTE");
}

@Override
public void onCancelled() {
    Log.i(TAG, "CALLING ON CANCELLED");
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();

}

public void onPostExecute() {
    Log.i(TAG, "CALLING ON POST EXECUTE");
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
        progressIndicator = null;
    }
}

@Override
public void doInBackground() {
    // put your code here for background operation
}

}


1

Dikkate alınması gereken bir şey, AsyncTask sonucunun yalnızca görevi başlatan etkinlik için mevcut olup olmayacağıdır. Evetse, Romain Guy'ın cevabı en iyisidir. Uygulamanızın diğer aktiviteleri için mevcut olması gerekiyorsa, o onPostExecutezaman kullanabilirsiniz LocalBroadcastManager.

LocalBroadcastManager.getInstance(getContext()).sendBroadcast(new Intent("finished"));

Ayrıca, etkinlik duraklatıldığında yayın gönderildiğinde etkinliğin durumu doğru şekilde ele aldığından emin olmanız gerekir.


1

Bu göz at mesaja . Bu Gönderi, hem tek bir örnek uygulamada ekran döndürme gerçekleştiğinde uzun süre çalışan işlem ve bellek sızıntısı gerçekleştiren AsyncTask'ı içerir. Örnek uygulama kaynak dövme üzerinde mevcuttur


0

Çözümüm.

Benim durumumda, aynı içeriğe sahip bir AsyncTasks zincirim var. Etkinlik yalnızca ilkine erişebiliyordu. Çalışan herhangi bir görevi iptal etmek için aşağıdakileri yaptım:

public final class TaskLoader {

private static AsyncTask task;

     private TaskLoader() {
         throw new UnsupportedOperationException();
     }

     public static void setTask(AsyncTask task) {
         TaskLoader.task = task;
     }

    public static void cancel() {
         TaskLoader.task.cancel(true);
     }
}

Görev doInBackground():

protected Void doInBackground(Params... params) {
    TaskLoader.setTask(this);
    ....
}

Aktivite onStop()veya onPause():

protected void onStop() {
    super.onStop();
    TaskLoader.cancel();
}

0
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    final AddTask task = mAddTask;
    if (task != null && task.getStatus() != UserTask.Status.FINISHED) {
        final String bookId = task.getBookId();
        task.cancel(true);

        if (bookId != null) {
            outState.putBoolean(STATE_ADD_IN_PROGRESS, true);
            outState.putString(STATE_ADD_BOOK, bookId);
        }

        mAddTask = null;
    }
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
    if (savedInstanceState.getBoolean(STATE_ADD_IN_PROGRESS)) {
        final String id = savedInstanceState.getString(STATE_ADD_BOOK);
        if (!BooksManager.bookExists(getContentResolver(), id)) {
            mAddTask = (AddTask) new AddTask().execute(id);
        }
    }
}

0

ayrıca android: configChanges = "keyboardHidden | orientation | screenSize" öğesini de ekleyebilirsiniz

açık örneğinize yardımcı olmasını umuyorum

 <application
    android:name=".AppController"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:configChanges="keyboardHidden|orientation|screenSize"
    android:theme="@style/AppTheme">
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.