2011-10-21 15 views
6

Pracuję teraz z dość powszechną sytuacją - pobierz niektóre dane przez Internet, a następnie zaktualizuj widok, aby go wyświetlić. Oczywiście chcę zrobić pobieranie w tle, a następnie zaktualizować widok w głównym wątku UI. Teraz patrząc na mój kod, trochę martwię się o moją Aktywność i jej elementy interfejsu użytkownika, zanim zostaną zaktualizowane. Oto esencja tego, co mam na myśli:Jak mogę sprawdzić, czy mój kontekst jest nadal ważny?

Thread update = new Thread() { 
    public void run() { 
     final Data newData = requestData();      
     if (newData != null) { 
      post(new Runnable() { 
       public void run() { 
        Toast.makeText(MyClass.this, "I'll do things here that depend on my context and views being valid", Toast.LENGTH_SHORT).show(); 
       } 
      }); 
     } 
    } 
}; 
update.start(); 

Wydaje się możliwe, że podczas pobierania danych działanie może zostać zniszczone. Co się wtedy stanie? Czy moja wątek będzie nadal wykonywany? Czy w końcu spróbuję uzyskać dostęp do martwych obiektów?

Zwykle robię to przez AsycTask, ale praca wydawała się dość prosta, tym razem po prostu wstawiać wątki - wątki uruchamiające. Czy poprawię sytuację, używając zamiast tego AsyncTask?

Odpowiedz

2

Jeśli używasz klas anonimowych, będą one miały wewnętrzne odniesienie do klasy zewnętrznej, więc nie jest tak, że staje się nagle niedostępne, ponieważ inne odniesienia zostały usunięte. AsyncTask tak naprawdę niczego nie zmienia, używa podobnej mechaniki do powiadamiania o wynikach.

Możesz użyć loaders, są one zaprojektowane tak, aby być zsynchronizowane z cyklem życia aktywności. Są one dostępne tylko od wersji Android 3.0, ale możesz używać support package do pracy z nimi na dowolnym urządzeniu z wersji 1.6 lub nowszej.

Istnieje jeszcze prostsze rozwiązanie, można po prostu użyć pola boolowskiego, które wskazuje, czy aktywność została usunięta. Powinieneś ustawić to pole na onPause() (lub gdy myślisz, że nie będziesz już potrzebować powiadomień) i sprawdzać je, gdy pokazujesz tosty. Nie musisz nawet używać synchronizacji, ponieważ to pole jest ograniczone do głównego wątku, więc jest całkowicie bezpieczne. Nawiasem mówiąc, jeśli zmienisz to pole gdzie indziej niż w onDestroy(), nie zapomnij dodać instrukcji, która resetuje twoje pole z powrotem w metodzie odpowiednika.

public class MyActivity extends Activity { 
    private boolean activityDestroyed = false; 

    @Override 
    protected void onDestroy() { 
     activityDestroyed = true; 
    } 

    private void updateData() { 
     new Thread() { 
      @Override 
      public void run() { 
       final Data newData = requestData();      
       if (newData == null) return;            

       runOnUiThread(new Runnable() { 
        public void run() { 
         if (activityDestroyed) return; 
         Toast.makeText(MyActivity.this, "Blah", 
           Toast.LENGTH_SHORT).show(); 
        } 
       }); 
      } 
     }.start(); 
    } 
} 
+0

To prawdopodobnie nie będzie kompilacji, musisz albo uczynić ją statyczną, albo musisz dodać MyActivity.this. – EboMike

+0

Przeważnie skopiowałem kod z pytania tylko po to, aby podać ogólny pomysł, nie zamierzałem, aby kod w odpowiedzi był używany w rzeczywistych aplikacjach. Zaktualizowałem kod, teraz się on skompiluje (choć to nie jest naprawdę ważne). – Malcolm

+0

Mam zamiar wypróbować ten boolean na teraz, więc daję temu jeden znacznik wyboru. Mimo to, dziękuję za odpowiedzi, Kurtis i EboMike. Nie słyszałem nawet o ładowarkach, dopóki o nich nie wspomniałeś. – MaximumGoat

6

To, czego naprawdę chcesz użyć, to AsyncTaskLoader. To są moje nowe ulubione klasy w Androidzie API. Używam ich przez cały czas i zostały stworzone, aby rozwiązywać takie problemy. Nie musisz się martwić, kiedy zatrzymać pobieranie lub coś w tym stylu. Cała logika wątków została zaopiekowała się tobą, w tym nakazanie wątku, aby przestał, jeśli działanie zostało zamknięte. Po prostu powiedz, co chcesz zrobić w metodzie loadInBackground(). Zauważ, że jeśli tworzysz API o API niższym niż 3.0, nadal możesz uzyskać dostęp do wszystkich ładowarek za pośrednictwem Android Support Package.

+2

Oświadczenie o wątków rzeczywiście niedokładne, nie są one zatrzymane lub nawet przerwane chyba mówisz im, żeby to zrobili w ładowaczu.Domyślnie dzieje się tak, że detektor wyników zadania jest po prostu usuwany z ładowarek po ich zresetowaniu. Dlatego po wykonaniu zadania nie otrzymasz oddzwonienia w swoim fragmencie lub aktywności, ale nie zostanie ona anulowana. – Malcolm

+0

Ah, ok. Dobrze wiedzieć. –

0

Kurtis ma rację. Jednakże, jeśli naprawdę chcesz do utrzymania go prosty, można spróbować to:

class MyActivity extends Activity { 
    static MyActivity context; 

    @Override 
    public void onCreate(Bundle icicle) { 
     super.onCreate(icicle); 

     MyActivity.context = this; 
    } 

    @Override 
    public void onDestroy() { 
     super.onDestroy(); 

     MyActivity.context = null; 
    } 
} 

A potem po prostu użyć MyActivity.context w swojej klasie (i sprawdzić wartość null tam). Jeśli chcesz, aby toast nie pojawiał się nawet, gdy aplikacja jest w tle, użyj zamiast tego opcji onPause/onResume.

To znowu szybkie i leniwe podejście. AsyncTask lub AsyncTaskLoader to sposób robienia rzeczy.

+0

Myślę, że sprawdzanie [isDestroyed()] (http://developer.android.com/reference/android/app/Activity.html#isDestroyed()) na instancji Activity jest prostsze? –

+0

spowoduje to wyciek pamięci przy ustawianiu statycznego odwołania do aktywności –

11

Jeśli Context jest Activity, można sprawdzić, czy jest wykończeniowych lub zakończeniu metodą isFinishing():

if (context instanceof Activity) { 
    Activity activity = (Activity)context; 
    if (activity.isFinishing()) { 
     return; 
    } 
} 
Toast.makeText(context, "I'll do things here that depend on my context and views being valid", Toast.LENGTH_SHORT).show(); 
+0

Dzięki. Idealne rozwiązanie dla mnie. – hybrid

Powiązane problemy