Android’de, kodu eşzamansız (yani UiThread’e paralel olarak) yürütmenin pek çok yolu vardır. AsyncTask sınıfı kullanmak, programcı için mekanizması en kolay olanıdır ve nispeten az miktarda kurulum ve kod gerektirir.
Yeni geliştiriciler AsyncTask’ı kullanımında aşağıdaki gibi bir yöntemi uyguluyor olabilirler;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new MyAsyncTask().execute(); } public class MyAsyncTask extends AsyncTask<Void, Void, String> { @Override protected String doInBackground(Void... params) { // Arkaplan işlemleri return result; } @Override protected void onPostExecute(String result) { Log.d("MyAsyncTask", "Sonuç: " + result); } } } |
- direkt ya da sıkı sıkıya bağlı kod bileşenleri
- bağlı olunacak yapılara/yapılardan teker teker kayıt olmak/kaydı silmek
- kod tekrarı
- testin zorlaşması
- hata çıkma ihtimalinin artması
Doğru kullanım açısından değerlendirecek olursak, Nesneye Yönelik Programlamanın temel ilkelerinden Esnek Bağ Tasarım Prensibi (Loose Coupling), göz önüne alınmalıdır. Böylece AsyncTask sınıfının Activity ile olan sıkı bağımlılığını nasıl kontrol altında tutabileceğimiz konusunu çözebiliriz. Sonuçta tasarlanmış olan bir sistemimiz var ve içerisinde ise değişimini izlemek istediğimiz bir sınıf mevcut. Activity ile AsyncTask sınıflarımızı birbirinden ayırmak ve en önemlisi iki sınıf arası iletişimin bir de arayüz üzerinden referansları aracılığı ile sağlanması, sınıflar arası izolasyonun da sağlanması açısından iyi bir yol olacaktır.
Bu yazıda AsyncTask’ın diğer etkinlikler için aynı zamanda tekrar kullanılabilir olmasını sağlamak için nasıl tek bir sınıf olarak oluşturulabileceğini anlatacağım . AsyncTask , ana parçayı (kullanıcı arabirimi) engellemeden web’den veya sunucudan veri indirme gibi belirli bir görevi yürütmenize olanak tanır. Şimdi, Nesne Tabanlı Programlama kavramlarının ve mekanizmalarının, yeniden kullanılabilir bir kod yazmak için yararlarından faydalanmalıyız elbette. Burada yapacağımız şey, tek bir AsyncTask oluşturup onu ayrı bir sınıfa koymak ve istediğimiz Activity içinde kullanmaktır. Temel sorun, AsyncTask işleminin sonucunun ele alınması ile birlikte gelir . AsyncTask sınıfının özel sınıfa nasıl genişletilebileceğine bir göz atalım ve arka planda yapılması gereken işlemlere ait sonuçların nasıl UI tarafına geçireleceğine bir bakalım.
Şimdi bu mimariyi bir Activity ile AsyncTask görevi arasında nasıl kullanacağımızı belirleyelim;
Her iki sınıf arası iletişimi kurmak için işe basit bir interface (arayüz) tasarımı ile başlayalım; Tasarlayacağımız Interface, AsyncTask sınıfının geri bildirim metodlarını referans alacaktır. Interface tasarımında bulunan <T> tipi sayesinde sınıflar arası iletilecek verinin tip bağımsızlığını sağlamaktadır. İstenilirse belileyeceğiniz tiplere göre düzenleme yapabilirsiniz.
ITaskEventListener.java
1 2 3 4 5 6 7 |
public interface ITaskEventListener<T> { void Started(); // Görev Başlatıldı... void onUpdate(T value); // Görev Devam Ediyor, Güncelleme var... void Completed(T response); // Görev Tamamlandı. } |
MyAsyncTask.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
public class MyAsyncTask extends AsyncTask<Integer, Integer, String> { ITaskEventListener taskListener; Context mContext; public MyAsyncTask(Context context){ mContext=context; taskListener=(ITaskEventListener)mContext; } @Override protected String doInBackground(Integer... params) { for (int count=0; count <= params[0]; count++) { try { Thread.sleep(1000); publishProgress(count); } catch (InterruptedException e) { e.printStackTrace(); } } return "Görev Tamamlandı."; } @Override protected void onPostExecute(String result) { if(mContext!=null){ taskListener.Completed(result); } } @Override protected void onPreExecute() { if(mContext!=null) { taskListener.Started(); } } @Override protected void onProgressUpdate(Integer... values) { if(mContext!=null) { taskListener.onUpdate(values[0]); } } } |
Yukarıda bulunan MyTaskAsync sınıfımızda ITaskEventListener interface sınıfına ait bir referans tutucunun bulunması dikkat etmemiz gereken noktadır. Kurucu metoda (constructor) parametre olarak gelen context nesnesi ile, eş zamansız sınıfımızı çağıran Activity’nin referansının alınmasını sağlayacaktır. Bu context nesnesinin ITaskEventListener tipine dönüştürülmesi, ilgili Activity’nin ITaskEventListener arayüzünden kalıtılmış (implements) olmasından dolayıdır ki her iki sınıf arası iletişim işte bu taşıyıcı referans üzerinden gerçekleşecektir.
Öncelikle örnek uygulamamızın arayüzünü arkaplanda çalışan iş parçacığının durumunu gösterecek şekilde dizayn edelim.
MainActivity.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.sample.oruvar.asynctaskcallbacksample.MainActivity"> <LinearLayout android:layout_width="360dp" android:layout_height="wrap_content" android:orientation="vertical"> <ProgressBar android:id="@+id/progressBar1" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:indeterminate="false" android:max="10" android:padding="4dip" > </ProgressBar> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" > </TextView> <Button android:id="@+id/btnStart" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Görev Başlat" > </Button> </LinearLayout> </android.support.constraint.ConstraintLayout> |
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
public class MainActivity extends AppCompatActivity implements ITaskEventListener { ProgressBar progressBar; TextView textView; Button btnStart; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); progressBar=(ProgressBar)findViewById(R.id.progressBar1); textView=(TextView)findViewById(R.id.textView1); btnStart=(Button)findViewById(R.id.btnStart); btnStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startAsyncTask(); } }); } public void startAsyncTask(){ int data=10; MyAsyncTask myTask=new MyAsyncTask(this); myTask.execute(data); } @Override public void Started() { textView.setText("Görev Başlatılıyor..."); } @Override public void onUpdate(Object value) { progressBar.setProgress((int)value); textView.setText("Görev Çalışıyor..."+value.toString()); } @Override public void Completed(Object response) { textView.setText(response.toString()); btnStart.setText("Tekrar Başlat"); } } |
Activity sınıfında dikkat edilmesi gereken “ITaskEventListener” interface ‘den kalıtılmalıdır.
Örneğimizi test ettiğimizde çalışmasını sorunsuz olarak izleyebilirsiniz.
Yukarıdaki örneğimizin kaynak kodlarını indirebilirsiniz. AsyncTaskCallBackSample