Android Süreçler ve Yönetimi

Android’de Thread ve Handler, çoklu iş parçacığı (multithreading) yönetiminde birlikte kullanılan iki temel yapıdır.

Android’de UI (kullanıcı arayüzü) işlemleri sadece ana thread’de (UI thread) yapılabilir. Arka plan işlemleri (ağ isteği, veritabanı işlemi, dosya okuma/yazma) için ayrı thread’ler açmanız gerekir. Ancak bu thread’ler UI’ı doğrudan güncelleyemez.

Handler, farklı thread’ler arasında mesaj ve runnable nesneleri göndermenizi/işlemenizi sağlayan bir mekanizmadır. Genellikle bir arka plan thread’inden UI thread’ine veri göndermek için kullanılır.

public class MainActivity extends AppCompatActivity {
    
    // UI thread'de oluşturulan Handler
    private Handler uiHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            // Bu kod UI thread'de çalışır
            switch (msg.what) {
                case 1:
                    String data = msg.getData().getString("key");
                    textView.setText(data);
                    break;
            }
        }
    };
    
    private void startBackgroundWork() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                // Bu kod arka plan thread'inde çalışır
                String result = doHeavyWork();
                
                // UI'ı güncellemek için Handler'a mesaj gönder
                Message message = new Message();
                message.what = 1;
                Bundle bundle = new Bundle();
                bundle.putString("key", result);
                message.setData(bundle);
                uiHandler.sendMessage(message);
            }
        }).start();
    }
}
public class MainActivity extends AppCompatActivity {
    
    private Handler uiHandler = new Handler(Looper.getMainLooper());
    
    private void startBackgroundWork() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                // Arka plan işlemi
                String result = doHeavyWork();
                
                // UI güncellemesini UI thread'e post et
                uiHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        // Bu kod UI thread'de çalışır
                        textView.setText(result);
                    }
                });
            }
        }).start();
    }
}

HandlerThread, kendi message queue’su ve looper’ı olan bir thread’dir. Uzun ömürlü arka plan işlemleri için idealdir.

public class MyActivity extends AppCompatActivity {
    
    private Handler backgroundHandler;
    private HandlerThread backgroundThread;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // HandlerThread oluştur ve başlat
        backgroundThread = new HandlerThread("MyBackgroundThread");
        backgroundThread.start();
        
        // Bu Handler arka plan thread'inde çalışır
        backgroundHandler = new Handler(backgroundThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                // Bu kod BACKGROUND thread'inde çalışır
                switch (msg.what) {
                    case 1:
                        doHeavyWork();
                        break;
                }
            }
        };
    }
    
    private void startBackgroundTask() {
        // Arka plan thread'ine mesaj gönder
        backgroundHandler.sendEmptyMessage(1);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Thread'i temizle
        backgroundThread.quitSafely();
    }
}

Looper, bir thread’in mesaj kuyruğunu (message queue) işleten döngüdür. Her thread’in bir looper’ı olmayabilir.

  • UI Thread: Otomatik olarak bir Looper’a sahiptir
  • Normal Thread: Varsayılan olarak Looper’ı yoktur
// Kendi Looper'ı olan thread oluşturma
class LooperThread extends Thread {
    public Handler mHandler;
    
    @Override
    public void run() {
        // Looper'ı hazırla
        Looper.prepare();
        
        // Handler'ı oluştur
        mHandler = new Handler(Looper.myLooper()) {
            @Override
            public void handleMessage(Message msg) {
                // Mesajları işle
            }
        };
        
        // Döngüyü başlat
        Looper.loop();
    }
}

Handler ile gecikmeli işlemler de yapabilirsiniz:

// 3 saniye sonra çalışacak işlem
uiHandler.postDelayed(new Runnable() {
    @Override
    public void run() {
        // Gecikmeli işlem
        textView.setText("3 saniye geçti");
    }
}, 3000);

// Zamanlayıcıyı iptal etme
uiHandler.removeCallbacksAndMessages(null);

Günümüz Android geliştirmesinde, raw Thread + Handler yerine coroutines tercih edilir:

lifecycleScope.launch(Dispatchers.IO) {
    val result = doHeavyWork()
    withContext(Dispatchers.Main) {
        textView.text = result
    }
}
BileşenGöreviÇalıştığı Thread
ThreadArka plan işlemini çalıştırırKendi thread’i
HandlerMesaj/runnable gönderir ve işlerOluşturulduğu thread
LooperMesaj kuyruğunu işletirBulunduğu thread
MessageVeri taşıyan nesne
RunnableÇalıştırılacak kod bloğuHandler’ın thread’i

Executors.newFixedThreadPool(), aslında new ThreadPoolExecutor()‘un önceden ayarlanmış, basit bir versiyonudur. Ancak arka planda bazı kritik farklar var.

Özelliknew ThreadPoolExecutor(...)Executors.newFixedThreadPool()
Core pool sizeİstediğiniz değeri verebilirsiniznThreads (3)
Max pool sizeİstediğiniz değeri verebilirsiniznThreads (3) (core ile aynı)
Keep alive timeSiz belirlersiniz (non-core thread’lerin boşta bekleme süresi)0 (sıfır) – çünkü max = core olduğu için non-core thread yok
Zaman birimiSiz seçersiniz (saniye, milisaniye vs.)TimeUnit.MILLISECONDS
Kuyruk tipiHerhangi bir BlockingQueue (örneğin PriorityBlockingQueue)Sınırsız LinkedBlockingQueue
Thread factoryİsteğe bağlı (özel thread isimlendirme vs.)Varsayılan DefaultThreadFactory
Reddetme politikasıSiz seçersiniz (AbortPolicyCallerRunsPolicy vs.)Varsayılan AbortPolicy (dolu kuyrukta hata fırlatır)

newFixedThreadPool() sınırsız bir kuyruk (LinkedBlockingQueue) kullanır.

  • Eğer n thread de meşgulse, gelecek yeni görevler kuyruğa eklenir.
  • Bellek riski: Görevler çok hızlı eklenir ve işlenemezse, kuyruk sonsuz büyüyebilir → OutOfMemoryError alabilirsiniz.

new ThreadPoolExecutor() ile sınırlı bir kuyruk (örneğin ArrayBlockingQueue(10)) belirleyerek bu riski kontrol altına alabilirsiniz.

newFixedThreadPool(n):

  • Sabit sayıda thread. İhtiyaçtan fazla thread açılmaz.
  • Görev sayısı artarsa sadece kuyruk büyür.

new ThreadPoolExecutor() ile örneğin şöyle bir konfigürasyon yapabilirsiniz:

corePoolSize = 2
maxPoolSize = 5
keepAliveTime = 30 saniye
Kuyruk = ArrayBlockingQueue(10)

Bu durumda:

  • 2 thread her zaman canlı.
  • Kuyruk dolana kadar görevler kuyruğa girer.
  • Kuyruk dolunca, max 5’e kadar yeni thread açılır.
  • 30 saniye boş kalan thread’ler sonlandırılır.

Bu esneklik newFixedThreadPool ile mümkün değildir.

  • Basit ve sabit sayıda işlem yapacaksanız (örneğin 3 ardışık network isteği) → Executors.newFixedThreadPool(3) yeterlidir.
  • Kuyruk taşması riski olan, kaynakları kontrol etmek istediğiniz durumlarda → new ThreadPoolExecutor() kullanarak kuyruğu sınırlandırın ve reddetme politikası ekleyin.
// 1. Hazır fixed thread pool:
ExecutorService fixed = Executors.newFixedThreadPool(3);

// 2. Aynı davranışı elle yazmak (newFixedThreadPool'un içi aslında budur):
ExecutorService manual = new ThreadPoolExecutor(
    3, 3,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>() // ← Sınırsız kuyruk!
);

// 3. Daha kontrollü, sınırlı kuyruklu versiyon:
ExecutorService controlled = new ThreadPoolExecutor(
    2, 4,
    30L, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(10), // ← En fazla 10 görev kuyruklanır
    new ThreadPoolExecutor.CallerRunsPolicy() // ← Kuyruk dolarsa çağıran thread işi yapar
);
  • Executors.newFixedThreadPool(n) – Kod sadeliği ve sabit sayıda thread için iyidir, ancak sınırsız kuyruk nedeniyle bellek taşmasına yol açabilir.
  • new ThreadPoolExecutor() – Tüm parametreleri kontrol etmenizi sağlar: thread sayıları, kuyruk tipi ve boyutu, bekleme süreleri, reddetme politikaları. Kritik sistemlerde ve Android’de (mobil cihazlarda sınırlı RAM nedeniyle) tercih edilmelidir.

ThreadPoolExecutor() için tanımlanan kuyruk dizisi dolduğunda bir reddetme politikası ekleyerek, kuyruk dolduğunda ne olacağını belirleyebilirsiniz:

mTaskQueue = new LinkedBlockingQueue<Runnable>(100);

mForBackgroundTasks = new ThreadPoolExecutor(
        NUMBER_OF_CORES,
        NUMBER_OF_CORES * 5,
        1,
        TimeUnit.SECONDS,
        mTaskQueue,
        new BackgroundThreadFactory(),
        new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
               
                //  Çağıran thread'in işi yapmasını bekle (backpressure)
                if (!executor.isShutdown()) {
                    try {
                        executor.getQueue().put(r); // Bu bloklayıcıdır!
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }
);

ya da Java’nın hazır politikalarından birini kullanabilirsiniz:

// Seçenek A: Kuyruk dolunca çağıran thread işi yapsın (backpressure oluşturur)
mForBackgroundTasks = new ThreadPoolExecutor(
        NUMBER_OF_CORES,
        NUMBER_OF_CORES * 5,
        1,
        TimeUnit.SECONDS,
        mTaskQueue,
        new BackgroundThreadFactory(),
        new ThreadPoolExecutor.CallerRunsPolicy() // ← Çağıran thread'de çalıştır
);

// Seçenek B: Kuyruk dolunca en eski görevi atıp yeni görevi ekle
mForBackgroundTasks = new ThreadPoolExecutor(
        NUMBER_OF_CORES,
        NUMBER_OF_CORES * 5,
        1,
        TimeUnit.SECONDS,
        mTaskQueue,
        new BackgroundThreadFactory(),
        new ThreadPoolExecutor.DiscardOldestPolicy() // ← En eski görevi sil
);

// Seçenek C: Sessizce reddet (görev kaybolur - burada dikkatli olmak gerekiyor!)
mForBackgroundTasks = new ThreadPoolExecutor(
        NUMBER_OF_CORES,
        NUMBER_OF_CORES * 5,
        1,
        TimeUnit.SECONDS,
        mTaskQueue,
        new BackgroundThreadFactory(),
        new ThreadPoolExecutor.DiscardPolicy() // ← Görevi yok say
);

Yorum bırakın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

3 + 20 =