Bài tập 34: đa tiến trình trong Android (Multi-Threading)

– Chúc mừng các bạn đã vượt qua được 33 cửa ải bài tập Android.

-Tôi thật sự vui mừng xúc động khi các bạn đã bền bỉ theo dõi từng bài, để tới được đây thì Tôi cam đoan rằng các bạn đã phải tốn rất nhiều công sức và thời gian, các bạn phải thức ngày đêm để làm được những bài tập trước đó.

– Để đáp lại những cố gắng của các bạn, trong bài tập này Tôi sẽ cung cấp cho các bạn một kiến thức hay, kiến thức mới và cực kỳ khó để các bạn quen với nỗi khổ đau để về sau có bị khổ nữa thì cũng quen rồi sẽ không còn thấy khổ (Tôi viết theo phạm trù triết học, chỉ có lập trình quá khổ mới hiểu).

– Như Tôi đã từng nói Intent là linh hồn của Android, trong mọi ngõ nghách hẻm hay mặt tiền của Android thì Intent vẫn tồn tại như chưa từng được tồn tại.

– Còn đa tiến trình (Multi – Threading)? nó cũng vậy, nó cũng giống như kỹ thuật truyền huyết mạch của từng ứng dụng Android, đặc biệt là những ứng dụng Vô Đối. Ví dụ như bạn cần cập nhật giao diện lúc thời gian thực, bạn cần kết nối internet hay làm những giao tác nào đó mà phải phân ra nhiểu tiểu trình để chạy. Để xử lý được đa tiến trình thì bạn phải có một tư duy lập trình logic thật tốt, nếu không tốt thì phải (Lấy Cần Cù Bù Thông Minh).

– Kỹ thuật đa tiến trình rất khó mà không khó (nếu bạn hiểu).

– Trong bài này Tôi sẽ hướng dẫn các bạn xử lý đa tiến trình với Handler class và AsyncTask class.

Bài ví dụ cập nhật ProgressBar lúc runtime: Progressbar sẽ cập nhật từ 0% tới 100% như hình bên dưới.

mul1– Trước tiên Tôi muốn nói về cách tạo đa tiến trình trong Java trước để các bạn dễ dàng áp dụng vào trong Android (vì Android dùng Java để coding).

– Trong java có 2 cách tạo đa tiến trình:

Cách 1: Ta implements interface Runnable

Sau đó ta Override phương thức run() này, khi tiến trình được Start thì hàm run sẽ được thực thi.

[code language=”java”]

public class MyRunable implements Runnable {

@Override
public void run() {
System.out.println
(Thread.currentThread().getName()+” …start!”);
}
public static void main(String[] args) {
for(int i=0;i<5;i++)
{
Thread t1=new Thread(new MyRunable());
t1.start();
}
}
}

[/code]

Ở trên Tôi dùng hàm main để Test tiến trình, Tôi tạo ra 5 tiến trình bằng cách dùng vòng lặp for.

Thread.currentThread().getName() là trả về tên của tiến trình hiện tại đang thực thi

Thread t1=new Thread(new MyRunable()); để tạo 1 tiến trình

gọi phương thức start để kích hoạt tiến trình.
t1.start(); Khi gọi hàm này thì phương thức run của MyRunnable sẽ được thực thi.

Chú ý rằng tiến trình rất khó kiểm soát, mỗi lần chạy sẽ mỗi khác nhau nên rất khó Debug, nó lệ thuộc vào hệ điều hành.

Ví dụ lần 1 bạn start thì có kết quả có thể là như sau:

mul2

– ở lần chạy tiếp theo thì chưa chắc bạn thấy được kết quả như trên nữa.

Cách 2: Kế thừa trực tiếp từ lớp Thread

[code language=”java”]

public class MyThread extends Thread {
@Override
public void run() {
super.run();
System.out.println(getName()+” … Start”);
}
public static void main(String[] args) {
for(int i=0;i<5;i++)
{
MyThread t=new MyThread();
t.start();
}
}
}

[/code]

– Ta thấy cách 2 dùng trực tiếp Thread, nên ta tạo 1 Thread từ MyThread và gọi start là tiến trình này sẽ được thực thi.

– Thường thì người ta hay sử dụng cách 1, do cách 1 có thể chia sẻ được các đối tượng qua lại giữa các tiến trình.

Giờ ta quay trở lại ví dụ cập nhật ProgressBar trong Android.

Trong bài ví dụ này Tôi dùng Handler class để xử lý. Chú ý rằng Handler class lại có 2 cách dùng.

Ở đây Tôi dùng sendMessage của Handler class để xử lý đa tiến trình, trong ví dụ kế tiếp Tôi sẽ dùng using Post  để xử lý.

Mô hình của Handler MessageQueue:

mul3

cách viết coding cho Handler class dùng Message:

mul4

– Chi tiết bài ví dụ:

– XML Layout :

[code language=”xml”]

[/code]

– Tới đây thì việc thiết kế layout là vô cùng đơn giản đối với các bạn rồi, nên Tôi không nói nhiều về nó nữa.

– Ta chuyển qua coding của MainActivity

[code language=”java”]

package tranduythanh.com;

import java.util.concurrent.atomic.AtomicBoolean;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

public class MainActivity extends Activity {
ProgressBar bar;
//khai báo handler class để xử lý đa tiến trình
Handler handler;
//dùng AtomicBoolean để thay thế cho boolean
AtomicBoolean isrunning=new AtomicBoolean(false);
//boolean
Button btnstart;
TextView lblmsg;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bar=(ProgressBar) findViewById(R.id.progressBar1);
btnstart=(Button) findViewById(R.id.btnstart);
btnstart.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
doStart();
}
});
//viết lệnh cho handler class để nhận thông điệp
//gửi về từ tiến trình con
//mọi thông điệp sẽ được xử lý trong handleMessage
//từ tiến trình con ta gửi Message về cho main thread
handler=new Handler(){
public void handleMessage(Message msg) {
super.handleMessage(msg);
//msg.arg1 là giá trị được trả về trong message
//của tiến trình con
bar.setProgress(msg.arg1);
lblmsg.setText(msg.arg1+”%”);
}
};
lblmsg=(TextView) findViewById(R.id.textView1);
}
public void doStart()
{
bar.setProgress(0);
isrunning.set(false);
//tạo 1 tiến trình CON
Thread th=new Thread(new Runnable() {
@Override
public void run() {
//vòng lặp chạy 100 lần
for(int i=1;i<=100 && isrunning.get();i++)
{
//cho tiến trình tạm ngừng 100 mili second
SystemClock.sleep(100);
//lấy message từ Main thread
Message msg=handler.obtainMessage();
//gán giá trị vào cho arg1 để gửi về Main thread
msg.arg1=i;
//gửi lại Message này về cho Main Thread
handler.sendMessage(msg);
}
}
});
isrunning.set(true);
//kích hoạt tiến trình
th.start();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;

}

[/code]

Bây giờ bạn tiến hành chạy ứng dụng và có kết quả như mong muốn. Chú ý rằng ta phải dùng đa tiến trình, nếu như chỉ dùng vòng lặp thông thường thì ứng dụng có vẻ như bị TREO, nó chỉ hiển thị kết quả ra khi đã thực hiện xong vòng lặp, còn ở đây ta dùng tiến trình thì nó sẽ thực hiện theo thời gian thực. Ta chỉ có thể cập nhật giao diện lúc Runtime ở Main Thread.

Bạn có thể tải coding mẫu ở đây:

http://www.mediafire.com/download/ogqfj1lfziy9d2s/LearnMultiThreading_usingMessage.rar

Bài kế tiếp Tôi sẽ làm tiếp một ví dụ về Using Message của Handler class, trong ví dụ này Tôi sẽ cho vẽ các Button lúc runtime, các bạn chú ý theo dõi để hiểu thêm về cách sử dụng Handler class trong việc xử lý đa tiến trình.

chúc các bạn thành công.

34 thoughts on “Bài tập 34: đa tiến trình trong Android (Multi-Threading)”

      1. Dễ lắm bạn.
        1) Down về
        2) BỎ vào cái chỗ cài Eclipse cho tiện nhé / hay chỗ mấy file .apk build ra
        3) Vào IDE
        4) Bật File -> Import -> General -> Existing Projects into Workspace -> Next
        5) Chỗ Select Root Directory -> Browse -> tìm đến thư mục chứa project ( folder được extract ra chứ ko fai file .zip nhé )
        6) Next hay OK gì đó là xong
        7) Có thể lúc load vào thì thấy Project có dấu x màu đỏ thì bấm chuột phải vào Project đó , chọn Properties -> chọn Android -> trong Project build target -> tick vào cái hàng đầu tiên -> Apply -> xong ^^
        Chúc bạn thành công !

  1. Cám ơn thầy nhiều ạ, khi nào có thể thầy làm bài lấy rss, json cho bọn e nha thầy, cám ơn thầy rất nhiều ạ

  2. hu hu hu thầy ơi. Sao mãi mà không có bài viết mới vậy ạ. Bọn em đang chờ đón những kiến thức hay từ thầy ạ

  3. cảm ơn thầy rất nhiều. nhờ thầy mà em học thêm được rất nhiều kiến thức bổ ích.
    em mong được học những bài tiếp theo của thầy

  4. Đang chờ bài viết về Handler class và AsyncTask class mà lâu quá thầy ạ, các bài viết của thầy thật sự hay và hữu ích lắm. Cảm ơn thầy

  5. thật sự thì tôi chỉ mất có ~2h để xem các kiến thức của anh đã truyền đạt, nó mang nhiều ý nghĩa vì những ví dụ gần gũi và trực quan. Thật ra nếu anh làm clip Youtube và upload mã nguồn như ở đây thì sẽ rất tuyệt vì anh có thể giải thích được nhiều hơn.

  6. Biến isRunning dùng để tránh trường hợp khi tiến trình đang cập nhật trạng thái progressBar, người dùng bấm btnStart lần nữa thì sẽ có thêm tiến trình khác cập nhất progressBar. Vì vậy trong phương thức OnClick() cần kiểm tra trạng thái isRunning if(isRunning.compareAndSet(false, true)){
    doStart();
    }
    và khi i = 100 phải set lại isRunning.set(false);

    Cảm ơn thầy!

  7. thưa thầy, thầy có thế giải thích rõ hơn ý nghĩa của “isrunning.get()” for (int i = 1; i <= 100 && isrunning.get(); i++) được không. và khi e ấn nút start lần 2 khi progressbar đang chạy thì nó dựt dựt giống như có progressbar khác đang chạy theo.em cảm ơn!

  8. thầy ơi cho em hỏi:
    em có 1 cái AsyncTast và 1 method
    method và AsyncTast đều được gọi ra trong onCreate của Activity_Main
    cái method của em cần dữ liệu từ AsyncTast nên khi chạy chương trình bị lỗi
    em nghĩ là lỗi này là do ta tạo ra 1 cái AsyncTast thì nó sẽ chạy song song với method nên method chưa lấy được dữ liệu.
    thầy có cách nào để chạy xong cái AsyncTast thì mới chạy cái method ko ạ?
    (method ko bỏ vào onPostExcute của AsyncTast được)

Leave a Reply