Multithreading Di Android
Pada program yang saya buat di artikel Belajar Memakai Implicit Intent Di Android, sistem operasi Android akan terlihat seperti hang bila saya membaca file dengan ukuran besar. Tombol yang ada di perangkat keras juga tidak bisa dipakai, misalnya saya tidak membatalkan proses dan tidak bisa kembali ke menu utama. Mengapa demikian? Hal ini berkaitan dengan threading (concurrency). Setiap aplikasi Android dimulai dengan sebuah thread tunggal yang diberi nama ‘main’. Semua operasi yang berkaitan dengan UI seperti event ditangani oleh thread ini. Pada Swing, thread ini memiliki fungsi yang sama seperti event dispatch thread (EDT). Bedanya, di Swing, aplikasi tidak langsung mulai dari EDT melainkan thread non-UI (pengguna memakai
SwingUtilities.invokeLater()
atauSwingUtilities.invokeAndWait()
untuk mengerjakan kode program di EDT). Bila saya mengerjakan sebuah proses yang sangat lama di thread UI, maka ia tidak bisa bekerja menangani event UI. Ia akan menunggu hingga proses lama ini selesai baru bisa lanjut bekerja. Ini yang membuat aplikasi menjadi tidak responsif.
Bila pada aplikasi Java, saya menggunakan JVisualVM (bawaan JDK) untuk memantau thread yang ada di sebuah aplikasi, maka untuk Android, saya dapat menggunakan Android Device Monitor (bawaan Android SDK). Sebagai contoh, bila saya memilih aplikasi dan men-klik icon Update Threads, saya dapat melihat tampilan seperti pada gambar berikut ini di tab Threads:
Walaupun ada banyak thread yang dibuat, satu-satunya thread yang menjalankan kode program aplikasi saya saat ini adalah thread 1 dengan nama ‘main’. Thread lainnya adalah bawaan Android, misalnya thread ‘GC’ untuk membebaskan memori yang tidak dipakai lagi, thread ‘JDWP’ untuk keperluan debugging, dan sebagainya.
Sekarang, saya akan melakukan perubahan pada kode program di method
tampilkan()
agar ia mengerjakan tugasnya di thread terpisah:
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
| private void tampilkan(Intent data) { Thread.start( 'ProsesFile' ) { TextView output = (TextView) findViewById(OUTPUT_VIEW_ID) Uri uri = data.getData() byte [] bytes = getContentResolver().openInputStream(uri).bytes StringBuilder hexdump = new StringBuilder() StringBuilder ascii = new StringBuilder() int i for (i = 0 ; i < bytes.length; i++) { hexdump. append (String.format( "%02x" , bytes[i])) hexdump. append ( ' ' ) ascii. append (Character.isLetterOrDigit(bytes[i]) ? ( char ) bytes[i] : '.' ) if ((i > 0 ) && (((i + 1 ) % 8 ) == 0 )) { hexdump. append (ascii.toString()) hexdump. append ( 'n' ) ascii = new StringBuilder() } output.post({ output.setText( "Memproses byte $i dari ${bytes.length}" ) } as Runnable) Thread.sleep( 1 ) } if (ascii) { ( 1 ..( 8 - ((i - 1 ) % 8 ))). each { hexdump. append ( ' ' ) } hexdump. append (ascii.toString()) } output.post({ output.setText(hexdump.toString()) Intent sendIntent = new Intent(Intent.ACTION_SEND) sendIntent.putExtra(Intent.EXTRA_TEXT, hexdump.toString()) sendIntent.setType( 'text/plain' ) shareActionProvider.setShareIntent(sendIntent) } as Runnable) } } |
Pada kode program di atas, saya memakai
Thread.start()
dari Groovy untuk membuat sebuah thread baru dengan nama ‘ProsesFile’. Selama berada di dalam thread baru ini, saya tidak boleh mengerjakan method yang berhubungan dengan UI secara langsung. Hal ini karena method pada UI tidak thread-safe. Oleh sebab itu, untuk menjadwalkan kode program agar dikerjakan oleh thread ‘Main’ (yang menangani UI), saya menggunakan post()
.
Bila saya menjalankan aplikasi, ia akan lebih responsif. Saya juga akan menemukan sebuah thread baru dengan nama ‘ProsesFile’ bila memantau aplikasi melalui Android Device Monitor, seperti yang terlihat pada gambar berikut ini:
Masalah lain yang saya hadapi adalah penggunaan memori yang besar karena memproses isi file sekaligus. Oleh sebab itu, saya akan menggunakan seek bar sehingga saya bisa menampilkan isi file per kilobyte sesuai dengan posisi seek bar. Agar lebih mudah dalam merancang UI, saya akan menggunakan designer bawaan Android Studio. Untuk itu, saya membuat folder baru bernama
layout
di lokasi src/main/res
. Setelah itu, saya men-klik kanan pada folder layout
dan memilih New, Layout Reource Folder. Saya mengisi dialog yang muncul denganmainscreen
pada File name dan RelativeLayout
pada Root element. Saya kemudian merancang UI sehingga terlihat seperti berikut ini:
Saya perlu mengubah method
onCreate()
untuk membaca layout dari XML sehingga isinya menjadi seperti:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| public class MainActivity extends Activity implements SeekBar.OnSeekBarChangeListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState) setContentView(R.layout.mainscreen) ((SeekBar) findViewById(R.id.posisi)).onSeekBarChangeListener = this if (intent.data) { tampilkan(intent) } } ... @Override void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {} @Override void onStartTrackingTouch(SeekBar seekBar) {} @Override void onStopTrackingTouch(SeekBar seekBar) {} } |
Kali ini saya perlu men-share variabel
bytes
yang berisi data lengkap yang sudah dibaca agar dapat diakses dari thread berbeda. Karena ia bytes
hanya ditulis sekali, setelah itu dibaca oleh beberapa thread berbeda, saya boleh saja men-share variabel ini dengan cara yang tidak thread-safe. Akan tetapi, agar lebih aman, saya akan menggunakan ReadWriteLock
sehingga pada saat variabel ini ditulis oleh sebuah thread, maka thread lain yang ingin membaca harus menunggu sampai penulisan selesai. Groovy menyediakan AST transformation@WithWriteLock
dan @WithReadLock
untuk keperluan tersebut. Sebagai contoh, saya bisa menambahkannya seperti:
1
2
3
4
5
6
7
8
9
10
11
12
13
| public class MainActivity extends Activity implements SeekBar.OnSeekBarChangeListener { private byte [] bytes ... @Override @WithReadLock void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {} @WithWriteLock private void tampilkan(Intent data) { ... } } |
Sekarang, fungsi dari kode program
tampilkan()
hanya untuk membaca isi file saja:
1
2
3
4
5
6
7
8
9
10
11
| @WithWriteLock private void tampilkan(Intent data) { Thread.start( 'BacaFile' ) { Uri uri = data.getData() byte [] bytes = getContentResolver().openInputStream(uri).bytes SeekBar seekBar = (SeekBar) findViewById(R.id.posisi) seekBar.post({ seekBar. max = bytes.length / 1024 }) } } |
Kode program di
onProgressChanged
akan dikerjakan setiap kali pengguna menggeser seek bar. Karena proses untuk menghasilkan hexdump cukup lama, maka saya akan mengerjakannya pada sebuah thread terpisah, misalnya:
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
| @Override @WithReadLock void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { Thread.start( 'ProsesHexdump' ) { TextView output = (TextView) findViewById(R.id.output) StringBuilder hexdump = new StringBuilder() StringBuilder ascii = new StringBuilder() int i = progress * 1024 int max = Math. min (i + 1024 , bytes.length) for (; i < max ; i++) { hexdump. append (String.format( "%02x " , bytes[i])) ascii. append (Character.isLetterOrDigit(bytes[i]) ? ( char ) bytes[i] : '.' ) if ((i > 0 ) && (((i + 1 ) % 8 ) == 0 )) { hexdump. append (ascii.toString()) hexdump. append ( 'n' ) ascii = new StringBuilder() } } if (ascii) { ( 1 ..( 8 - (i % 8 ))). each { hexdump. append ( ' ' ) } hexdump. append (ascii.toString()) } output.post({ String hasil = hexdump.toString() output.setText(hasil) Intent sendIntent = new Intent(Intent.ACTION_SEND) sendIntent.putExtra(Intent.EXTRA_TEXT, hasil) sendIntent.setType( 'text/plain' ) shareActionProvider.setShareIntent(sendIntent) } as Runnable) } } |
Sekarang, setiap kali membaca file, thread ‘BacaFile’ akan dibuat. Setiap kali seek bar digeser, thread‘ProsesHexDump’ akan tercipta. Thread baru ini juga akan menunggu hingga ‘BacaFile’ selesai dikerjakan bila thread tersebut masih bekerja.
Casino - Jtm Hub
BalasHapusCasino - 울산광역 출장마사지 The best place to play slots with real 김제 출장샵 money. Get a quick and easy 아산 출장샵 payment and experience from the casino 파주 출장마사지 directly. Rating: 4.3 · 22 votes 청주 출장샵