Melakukan Tugas-tugas yang berulang pada Service

Selain melakukan tugas-tugas yang panjang pada service, kita mungkin juga ingin melakukan beberapa tugas yang berulang-ulang pada service. Misalnya, kita mungkin ingin menulis service untuk alarm jam ang berjalan secara persisten di background. Dalam hal ini, service kita akan perlu mengeksekusi secara periodik beberapa kode untuk men-cek apakah waktu yang dijadwalkan sudah dicapai atau belum sehingga alarm bisa dibunyikan. Untuk mengeksekusi blok kode yang dieksekusi berdasarkan interval waktu tertentu, kita bisa menggunakan class 'Timer' di dalam service kita, seperti latihan berikut di bawah ini.

Masih menggunakan project dan file yang sama dengan latihan sebelumnya, kita modifikasi dan tambahkan kode berikut dalam file "MyService.java", seperti berikut di bawah ini:
package com.example.services; 
import android.app.Service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;

public class MyService extends Service {
int counter = 0;
static final int UPDATE_INTERVAL = 1000;
private Timer timer = new Timer();

@Override
public IBinder onBind(Intent arg0) {
return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
/*kita ingin agar service ini terus berjalan hingga
di-stop secara eksplisit, sehingga akan mengembalikan/return sticky
Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show(); */

doSomethingRepeatedly();

try {
new DoBackgroundTask().execute(
new URL("https://phpisus.blogspot.com/somefiles.pdf"),
new URL("https://beritati.blogspot.com/somefiles.pdf"),
new URL("https://acollectionofjokes.blogspot.com/somefiles.pdf"),
new URL("https://diansano.blogspot.com/somefiles.pdf"));

}
catch (MalformedURLException e) {
e.printStackTrace();
}
return START_STICKY;
}

private void doSomethingRepeatedly() {
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
Log.d("MyService", String.valueOf(++counter));
}
}, 0, UPDATE_INTERVAL);
}

private int DownloadFile(URL url) {
try {
/*men-simulasi beberapa saat untuk download file*/
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*mengembalikan angka sembarang yang menyejikam ukuran file yang di-download*/
return 100;
}

private class DoBackgroundTask extends AsyncTask {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalBytesDownloaded = 0;
for (int i = 0; i < count; i++) {
totalBytesDownloaded += DownloadFile(urls[i]);
/*menghitung persentase yang sudah di-download dan
menunjukan progressnya **/
publishProgress((int) (((i+1) / (float) count) * 100));
}
return totalBytesDownloaded;
}

protected void onProgressUpdate(Integer... progress) {
Log.d("Downloading files", String.valueOf(progress[0]) + "% downloaded");
Toast.makeText(getBaseContext(), String.valueOf(progress[0]) + "% downloaded",
Toast.LENGTH_LONG).show();
}

protected void onPostExecute(Long result) {
Toast.makeText(getBaseContext(), "Downloaded " + result + " bytes",
Toast.LENGTH_LONG).show();
stopSelf();
}
}

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

if (timer != null) {
timer.cancel();
}
Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
}
}

Kemudian jalankan di emulator di Android Studio, tekan tombol "Start Service". Kalau kita amati di jendela 'Log', maka akan muncul seperti ini:
D/MyService: 1
D/MyService: 2
D/MyService: 3
D/MyService: 4
D/MyService: 5
D/MyService: 6
D/Downloading files: 25% downloaded
...
dst

Penjelasan

Dalam latihan ini, kita membuat object 'Timer' dan memanggil method 'scheduleAtFixedRate()' di dalam method 'doSomethingRepeatedly()' yang sudah kita definisikan:
private void doSomethingRepeatedly() {
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
Log.d("MyService", String.valueOf(++counter));
}
}, 0, UPDATE_INTERVAL);
}
Kita melewatkan suatu instans dari class 'TimerTask' ke method 'scheduleAtFixedRate()' sehingga kita bisa mengeksekusi blok kode di dalam method 'run()' secara berulang-ulang. Parameter kedua pada method 'scheduleAtFixedRate()' menentukan jumlah waktu, dalam milidetik, sebelum eksekusi pertada. Parameter ketiga menentukan jumlah waktu, dalam milidetik, antara eksekusi berikutnya.

Dalam latihan di atas, kita mencetak nilai counter setiap detik (1000 milidetik). Service yang dibuat secara berulang akan mencetak nilai 'counter' hingga service dihentikan:
@Override
public void onDestroy() {
super.onDestroy();

if (timer != null) {
timer.cancel();
}
Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
}
Untuk method 'scheduleAtFixedRate()', kode kita dieksekusi dengan interval waktu yang tetap, tidak peduli berapa lama setiap tugas dilakukan. Misalnya, bila kode di dalam method 'run()' berlangsung selama dua detik untuk selesai, maka tugas kedua akan mulai segera setelah tugas pertama selesai. Dengan cara yang sama, bila delay kita set tiga detik dan tugas tersebut hanya perlu waktu dua detik, maka tugas kedua akan menunggu selama satu detik sebelum mulai.

Selain itu, silahkan perhatikan bahwa kita memanggil method 'doSomethingRepeatedly()' secara langsung di dalam method 'onStartCommand()', tanpa perlu membungkusnya di dalam suatu subclass dari class 'AsyncTask'. Hal ini dikarenakan class 'TimerTask' itu sendiri mengimplementasikan interface 'Runnable', yang berarti sudah menyediakan untuk berjalan di thread yang terpisah.

No comments: