Category Archives: 6. Xử lý đa tiến trình trong Android

Bài 42: Đáp án mẫu đề thi cuối kỳ Android


Dưới đây là đáp án mẫu cho 2 đề thi cuối kỳ Android của lớp 13DTH1LT2

Đề 1:LTDiDong_12DTH1LT3_DeThiCuoiKy_2013_MD001

Đề 2:LTDiDong_12DTH1LT3_DeThiCuoiKy_2013_MD002

Ở đây Tôi chỉ giải câu 1 của các đề trên, câu 2 Sqlite các bạn tự làm vì nó rất dễ, các bài tập trước đó đều đã làm qua.

Câu 1 – đề 1:

s1Tôi sẽ xử lý lệnh vẽ Button và căn lề Button theo vị trí xuất hiện (tính từ vị trí 0)

– XML Layout cho câu hỏi này là:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/LinearLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context=".MainActivity" >

<LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content" >

<Button
 android:id="@+id/btnDrawButton"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Draw Button:" />

<EditText
 android:id="@+id/editNumber"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_weight="1"
 android:ems="10" />

</LinearLayout>

<ScrollView
 android:id="@+id/scrollView1"
 android:layout_width="match_parent"
 android:layout_height="wrap_content" >

<LinearLayout
 android:id="@+id/layout_draw_button"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical" >

</LinearLayout>

 </ScrollView>

&nbsp;

</LinearLayout>

Source xử lý:


package tranduythanh.com;

import java.util.Random;
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.graphics.Point;
import android.view.Display;
import android.view.Gravity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;

public class MainActivity extends Activity {

Handler handlerMain;
 AtomicBoolean atomic=null;
 LinearLayout layoutdevebutton;
 Button btnOk;
 EditText edtOk;
 int sizehalf=0;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);

 //lấy LinearLayout chứa Button ra
 layoutdevebutton=(LinearLayout) findViewById(R.id.layout_draw_button);
 final Random rd=new Random();
 btnOk=(Button) findViewById(R.id.btnDrawButton);
 edtOk=(EditText) findViewById(R.id.editNumber);
 handlerMain=new Handler()
 {
 @Override
 public void handleMessage(Message msg) {
 // TODO Auto-generated method stub
 super.handleMessage(msg);
 //Nhận nhãn của Button được gửi về từ tiến trình con
 int v=rd.nextInt(100);
 String nhan_button=v+"";
 //khởi tạo 1 Button
 Button b=new Button(MainActivity.this);
 //thiết lập nhãn cho Button
 b.setText(nhan_button);
 //thiết lập kiểu Layout : Width, Height
 LayoutParams params=new
 LayoutParams(LayoutParams.WRAP_CONTENT,
 LayoutParams.WRAP_CONTENT);
 if(msg.arg1%2==0)
 params.gravity=Gravity.LEFT;
 else
 params.gravity=Gravity.RIGHT;
 //thiết lập layout cho Button

 b.setWidth(sizehalf);
 b.setLayoutParams(params);
 //đưa Button vào layoutdevebutton
 layoutdevebutton.addView(b);

 }
 };
 btnOk.setOnClickListener(new OnClickListener() {

@Override
 public void onClick(View arg0) {
 // TODO Auto-generated method stub
 doStart();
 }
 });
 }
 @Override
 protected void onResume() {
 // TODO Auto-generated method stub
 super.onResume();
 //lấy màn hình điện thoại đang sử dụng
 Display display = getWindowManager().getDefaultDisplay();
 Point size = new Point();
 display.getSize(size);
 sizehalf=size.x/2;
 }
 private void doStart()
 {
 layoutdevebutton.removeAllViews();
 atomic=new AtomicBoolean(false);
 final int sobutton=Integer.parseInt(edtOk.getText()+"");
 Thread thCon=new Thread(new Runnable() {

 @Override
 public void run() {
 // TODO Auto-generated method stub

 for(int i=0;i<sobutton && atomic.get();i++)
 {
 //nghỉ 200 mili second
 SystemClock.sleep(200);
 //lấy message từ Main Thread
 Message msg=handlerMain.obtainMessage();
 //gán dữ liệu cho msg Mainthread, lưu vào biến obj
 //chú ý ta có thể lưu bất kỳ kiểu dữ liệu nào vào obj
 msg.arg1=i;
 //gửi trả lại message cho Mainthread
 handlerMain.sendMessage(msg);
 }
 }
 });
 atomic.set(true);
 //thực thi tiến trình
 thCon.start();
 }

}

– Câu 1 – đề 2:

s3

 

Xml Layout:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/LinearLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context=".MainActivity" >

<LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content" >

<Button
 android:id="@+id/btnDrawButton"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Draw View:" />

<EditText
 android:id="@+id/editNumber"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_weight="1"
 android:ems="10" />

</LinearLayout>

<ScrollView
 android:id="@+id/scrollView1"
 android:layout_width="match_parent"
 android:layout_height="wrap_content" >

<LinearLayout
 android:id="@+id/layout_draw_button"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical" >

</LinearLayout>

 </ScrollView>

&nbsp;

</LinearLayout>

Xử lý source code:


package tranduythanh.com;

import java.util.Random;
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.graphics.Point;
import android.view.Display;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;

public class MainActivity extends Activity {

Handler handlerMain;
 AtomicBoolean atomic=null;
 LinearLayout layoutdevebutton;
 Button btnOk;
 EditText edtOk;
 int sizehalf=0;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);

 //lấy LinearLayout chứa Button ra
 layoutdevebutton=(LinearLayout) findViewById(R.id.layout_draw_button);
 final Random rd=new Random();
 btnOk=(Button) findViewById(R.id.btnDrawButton);
 edtOk=(EditText) findViewById(R.id.editNumber);
 handlerMain=new Handler()
 {
 @Override
 public void handleMessage(Message msg) {
 // TODO Auto-generated method stub
 super.handleMessage(msg);
 //Nhận nhãn của Button được gửi về từ tiến trình con
 int v=rd.nextInt(100);
 String nhan_button=v+"";
 //khởi tạo 1 Button
 View vv=null;
 if(msg.arg1%2==0)
 {
 vv=new Button(MainActivity.this);
 ((Button) vv).setText(nhan_button);
 }
 else
 {

 vv=new EditText(MainActivity.this);
 ((EditText) vv).setText(nhan_button);
 }

 LayoutParams params=new
 LayoutParams(LayoutParams.MATCH_PARENT,
 LayoutParams.WRAP_CONTENT);
 vv.setLayoutParams(params);

 //đưa Button vào layoutdevebutton
 layoutdevebutton.addView(vv);

 }
 };
 btnOk.setOnClickListener(new OnClickListener() {

@Override
 public void onClick(View arg0) {
 // TODO Auto-generated method stub
 doStart();
 }
 });
 }
 @Override
 protected void onResume() {
 // TODO Auto-generated method stub
 super.onResume();
 Display display = getWindowManager().getDefaultDisplay();
 Point size = new Point();
 display.getSize(size);
 sizehalf=size.x/2;
 }
 private boolean isPrime(int n)
 {
 if(n<2)return false;
 for(int i=2;i<=Math.sqrt(n);i++)
 if(n%i==0)return false;
 return true;
 }
 private void doStart()
 {
 layoutdevebutton.removeAllViews();
 atomic=new AtomicBoolean(false);
 final int sobutton=Integer.parseInt(edtOk.getText()+"");
 Thread thCon=new Thread(new Runnable() {

 @Override
 public void run() {
 // TODO Auto-generated method stub

 for(int i=0;i<sobutton && atomic.get();i++)
 {
 //nghỉ 200 mili second
 SystemClock.sleep(200);
 //lấy message từ Main Thread
 Message msg=handlerMain.obtainMessage();
 //gán dữ liệu cho msg Mainthread, lưu vào biến obj
 //chú ý ta có thể lưu bất kỳ kiểu dữ liệu nào vào obj
 msg.arg1=i;
 //gửi trả lại message cho Mainthread
 handlerMain.sendMessage(msg);
 }
 }
 });
 atomic.set(true);
 //thực thi tiến trình
 thCon.start();
 }

}

Các bạn có thể tải đáp án mẫu tại đây:

Đáp án mẫu câu 1 đề 1: https://www.mediafire.com/download/ob6mc2webu37usb/ThiCuoiKy_De1_Cau1.rar

Đáp án mẫu câu 1 đề 2: http://www.mediafire.com/download/w3rn3thi7bqh1gk/ThiCuoiKy_De2_Cau1.rar

Bài 39: Kết hợp AsyncTask và Handler class


Trong bài này Tui sẽ đưa ra 1 ví dụ cuối cùng về cách xử lý đa tiến trình, bài này tương đối phức tạp, bạn phải tập trung làm đi làm lại để hiểu thêm.

Giao diện chính của chương trình như sau:

async5– Mô tả sơ sơ: Khi nhấn nút Start, chương trình sẽ vẽ bên màn hình bên trái điện thoại là danh sách các số ngẫu nhiên, sau khi vẽ xong (hoặc là đồng thời) chương trình sẽ vẽ danh sách các số là số nguyên tố nằm bên màn hình trái vào màn hình bên phải.

– Bạn tạo project như hình dưới đây:

async6

– Giao diện hơi phức tạp 1 xíu, ở đây Tui chia màn hình ra làm hai, bạn xem XML Resource:


<LinearLayout xmlns:android="http://splashurl.com/ot4qqf8
 xmlns:tools="http://splashurl.com/qzxkn3a
 android:id="@+id/LinearLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context=".MainActivity" >

<LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content" >

<Button
 android:id="@+id/btnstart"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Start:" />

<EditText
 android:id="@+id/editnumber"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_weight="1"
 android:ems="10"
 android:hint="enter number of here"
 android:inputType="number" >

<requestFocus />
 </EditText>
 </LinearLayout>

<TableLayout
 android:layout_width="fill_parent"
 android:layout_height="wrap_content" >

<TableRow
 android:id="@+id/tableRow1"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content" >

<TextView
 android:id="@+id/textView1"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_weight="1"
 android:background="#008000"
 android:gravity="center"
 android:text="Random Number:"
 android:textColor="#FFFFFF" />

<TextView
 android:id="@+id/textView2"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_weight="1"
 android:background="#8000FF"
 android:gravity="center"
 android:text="List Prime:"
 android:textColor="#FFFFFF" />
 </TableRow>

<TableRow
 android:id="@+id/tableRow2"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >

<ScrollView
 android:id="@+id/scrollView1"
 android:layout_width="wrap_content"
 android:layout_weight="1"
 android:layout_height="wrap_content" >

<LinearLayout
 android:id="@+id/llrandomnumber"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical" >
 </LinearLayout>
 </ScrollView>

<ScrollView
 android:id="@+id/scrollView2"
 android:layout_width="wrap_content"
 android:layout_weight="1"
 android:layout_height="wrap_content" >

<LinearLayout
 android:id="@+id/llprimenumber"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical" >

</LinearLayout>
 </ScrollView>
 </TableRow>
 </TableLayout>

</LinearLayout>

– Xử lý coding, bạn xem MyAsyncTask: 

Class này Tui kế thừa từ: AsyncTask<Integer, Integer, ArrayList<Integer>>

Integer ở đối số 1: Được truyền vào khi ta thực thi tiến trình.

Integer ở đối số 2: Được truyền vào để tiến hành cập nhật giao diện trong hàm publishProgress

ArrayList<Integer>: Kết quả trả về sau khi thực hiện xong tiến trình.

Việc đưa control động cũng tương tự như các bài trước.


package tranduythanh.com;

import java.util.ArrayList;
import java.util.Random;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.SystemClock;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast;

public class MyAsyncTask extends
 AsyncTask<Integer, Integer, ArrayList<Integer>>
{
 private LinearLayout llrandom,llprime;
 private Random rd=new Random();
 private Activity context;
 public MyAsyncTask(Activity context)
 {
 //lấy các control trong MainThread
 this.llrandom=(LinearLayout)
 context.findViewById(R.id.llrandomnumber);
 this.llprime=(LinearLayout)
 context.findViewById(R.id.llprimenumber);;
 this.context=context;
 }
 //Begin - can use UI thread here
 protected void onPreExecute() {
 super.onPreExecute();
 Toast.makeText(context, "Start here",
 Toast.LENGTH_SHORT).show();
 //khi bắt đầu thực thi tiến trình thì tiến hành xóa toàn bộ control bên trong
 this.llrandom.removeAllViews();
 this.llprime.removeAllViews();
 }
 protected ArrayList<Integer> doInBackground(Integer... params) {
 int step=1;
 ArrayList<Integer>list=new ArrayList<Integer>();
 //vòng lặp chạy hết n số button truyền vào
 int n=params[0];
 while(isCancelled()==false && step<=n)
 {
 step++;
 SystemClock.sleep(100);
 //lấy số ngẫu nhiên
 int x=rd.nextInt(100)+1;
 //gọi cập nhật giao diện
 publishProgress(x);
 //nếu là số nguyên tố thì lưu vào danh sách
 if(isPrime(x))
 list.add(x);
 }
 //trả về danh sách số nguyên tố
 return list;
 }
 @Override
 protected void onProgressUpdate(Integer... values) {
 super.onProgressUpdate(values);
 //lấy giá trị truyền vào trong doinbackground
 Integer item=values[0];
 //tạo Button với Text có giá trị là số ngẫu nhiên
 Button btn=new Button(context);
 btn.setWidth(100);
 btn.setHeight(30);
 btn.setText(item+"");
 //đưa button lên view bên trái màn hình
 this.llrandom.addView(btn);
 }
 public void doRawPrime(int x)
 {
 //hàm vẽ các Button là chứac các số nguyên tố
 Button btn=new Button(context);
 btn.setWidth(100);
 btn.setHeight(30);
 btn.setText(x+"");
 //đưa Button vào view bên phải màn hình
 this.llprime.addView(btn);
 }
 //hàm kiểm tra số nguyên tố
 public boolean isPrime(int x)
 {
 if(x<2)return false;
 for(int i=2;i<=Math.sqrt(x);i++){
 if(x%i==0)return false;}
 return true;
 }
 @Override
 protected void onPostExecute(ArrayList<Integer> result) {
 super.onPostExecute(result);
 //sau khi tiến trình kết thúc thì hàm này sẽ được thực thi
 //lấy về danh sách các số nguyên tố
 final ArrayList<Integer>list=result;
 //tiến hành dùng Handler class để thực hiện
 final Handler handler=new Handler();
 Thread th=new Thread(new Runnable() {
 public void run() {
 //lặp để vẽ các Button là số nguyên tố
 for(int i=0;i<list.size();i++){
 final int x=list.get(i);
 SystemClock.sleep(100);
 handler.post(new Runnable() {
 public void run() {
 doRawPrime(x);}
 });
 }
 handler.post(new Runnable() {
 public void run() {
 Toast.makeText(context, "Finish",
 Toast.LENGTH_SHORT).show();
 }
 });
 }
 });
 th.start();
 }}//end MyAsyncTask

Mục đích của bài này là Tui muốn nói rằng chúng ta có thể kết hợp các kỹ thuật xử lý đa tiến trình với nhau.

Ở đây Tui có 1 câu hỏi nhỏ là, nếu trong hàm onPostExecute Tui muốn sử dụng lại MyAsyncTask thì viết coding như thế nào là hớp lý (ý của tui là chỉ sử dụng 1 class MyAsyncTask, nhưng ta có thể thông qua nó để tạo nhiều tiến trình khác nhau với những chức năng khác nhau). Bạn hãy suy nghĩ và làm thử.

Tiếp tục xem MainActivity:


package tranduythanh.com;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends Activity {
 Button btnstart;
 MyAsyncTask task;
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 btnstart=(Button) findViewById(R.id.btnstart);

 btnstart.setOnClickListener(new
 View.OnClickListener() {
 public void onClick(View arg0) {
 doStart();
 }
 });
 }
 public void doStart()
 {
 String s=((EditText)
 this.findViewById(R.id.editnumber))
 .getText().toString();
 //lấy số lượng từ EditText
 int n=Integer.parseInt(s);
 task=new MyAsyncTask(this);
 task.execute(n);
 }
}

– Bài này có 1 chút phức tạp, bạn cố gắng đọc hiểu.

– Bạn có thể tải coding mẫu ở đây: http://www.mediafire.com/download/1m270fb1zpdw628/LearnMultiThreading_AsyncTask_Handler.rar

– Bài kết tiếp Tui sẽ trình bày về BroadCastReceiver và cách xử lý khi có tin nhắn gửi tới, bạn có thể ứng dụng nó để viết phần mềm Xử Lý Tin Nhắn Rác. Bạn chú ý theo dõi.

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

Bài 38: Lấy kết quả trả về sau khi thực hiện đa tiến trình bằng AsyncTask


Ở bài tập 37 Tui đã trình bày về cách xử lý đa tiến trình bằng AsyncTask nhưng không quản lý kết quả trả về, trong một số tình huống khi xử lý đa tiến trình chúng ta phải quản lý kết quả thực thi đa tiến trình, vì vậy Tui muốn làm 1 ví dụ cụ thể để các bạn tiện theo dõi và làm theo.

Ở đây tui làm 1 bài đơn giản: Dùng AsyncTask để cứ sau 150 miliseconds thì cập nhật 1 số Fibonacci vào ListView, sau khi kết thúc quá trình cập nhập Fibonacci thì chương trình sẽ hiển thị Tổng giá trị của dãy số Fibonacci này.

– Bạn xem hình Tui chụp dưới này:

async3

– Ở bài này ta cần biết:

1) Số Fibonacci là số gì?

2) Truyền số lượng vào MyAsyncTask như thế nào?

3) Cập nhật vào ListView ?

4) Và lấy kết quả trả về sau khi kết thúc tiến trình?

————————————————————————-

1) Số fibonacci là số fibonacci, vô cùng đơn giản …. các bạn xem ở đây: http://vi.wikipedia.org/wiki/D%C3%A3y_Fibonacci

– Bạn tạo Project như hình dưới đây:

async4

– XML Layout như sau:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/LinearLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context=".MainActivity" >

<LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content" >

<Button
 android:id="@+id/button1"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="OK" />

<EditText
 android:id="@+id/editText1"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_weight="1"
 android:ems="10" >

<requestFocus />
 </EditText>

<TextView
 android:id="@+id/textView1"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:background="#008000"
 android:textColor="#FFFFFF"
 android:textSize="25sp" />

</LinearLayout>

<ListView
 android:id="@+id/listView1"
 android:layout_width="match_parent"
 android:layout_height="wrap_content" >
 </ListView>
</LinearLayout>

– MyAsyncTask Tui sử dụng cả 3 đối số:


package tranduythanh.com;

import java.util.ArrayList;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.SystemClock;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
/**
 * AsyncTask<Integer, Integer, ArrayList<Integer>>
 * Integer: Truyền vào lúc thực thi tiến trình
 * Integer: Giá trị để Cập nhật giao diện
 * ArrayList<Integer>: Kết quả trả về sau khi kết thúc tiến trình
 * @author thanhtran
 *
 */
public class MyAsyncTask extends
 AsyncTask<Integer, Integer, ArrayList<Integer>> {

Activity activityCha;
 ListView lv=null;
 ArrayList<Integer>arrCuaListview=new ArrayList<Integer>();
 ArrayAdapter<Integer>adapterCuaListview=null;
 public MyAsyncTask(Activity act)
 {
 //lấy MainActivity
 activityCha=act;
 //Lấy ListView của MainActivity
 lv=(ListView) activityCha.findViewById(R.id.listView1);
 //Adapter cho Listview
 adapterCuaListview=new ArrayAdapter<Integer>
 (activityCha,
 android.R.layout.simple_list_item_1,
 arrCuaListview);
 //gán Adapter cho listview
 lv.setAdapter(adapterCuaListview);
 }
 /**
 * Số fibonacci thứ n trong chuỗi Fibonacci
 * @param n
 * @return
 */
 public int fib(int n)
 {
 if(n<=2)return 1;
 return fib(n-1)+fib(n-2);
 }
 @Override
 protected void onPreExecute() {
 // TODO Auto-generated method stub
 super.onPreExecute();
 //muốn khởi tạo gì đó trước khi thực thi thì có thể làm ở đây
 Toast.makeText(activityCha, "Bắt đầu chạy tiến trình ",
 Toast.LENGTH_LONG).show();

 }
 @Override
 protected ArrayList<Integer> doInBackground(Integer... arg0) {
 // TODO Auto-generated method stub
 //vậy thì khi bắt đầu thực thi đối số 1 sẽ ở đây: arg0
 int so=arg0[0];
 //khai báo ArrayList lưu trữ tập các số nguyên
 ArrayList<Integer> arrTongCacSoFib=new ArrayList<Integer>();
 for(int i=1;i<=so;i++)
 {
 SystemClock.sleep(150);
 //lấy số fibonacci tại vị trí thứ i
 int f=fib(i);
 //lưu vào danh sách
 arrTongCacSoFib.add(f);
 //cập nhập số fibonacci lên listview
 publishProgress(f);
 }
 //trả về danh sách, nó sẽ được lưu trữ trong hàm
 //onPostExecute
 return arrTongCacSoFib;
 }
 @Override
 protected void onProgressUpdate(Integer... values) {
 // TODO Auto-generated method stub
 super.onProgressUpdate(values);
 //lấy giá trị truyền từ publishProgress
 arrCuaListview.add(values[0]);
 //cập nhật lại giao diện
 adapterCuaListview.notifyDataSetChanged();
 }
 @Override
 protected void onPostExecute(ArrayList<Integer> result) {
 // TODO Auto-generated method stub
 super.onPostExecute(result);
 //result chính là ArrayList lưu trữ chuỗi Fibonacci
 int tong=0;
 //vòng lặp để cộng dồn lại
 for(int v : result)
 {
 tong+=v;
 }
 Toast.makeText(activityCha, "Tong ="+tong, Toast.LENGTH_LONG).show();
 //cập nhật lên TextView
 TextView txt=(TextView) activityCha.findViewById(R.id.textView1);
 txt.setText(tong+"");
 }
}

– Trong coding trên thì phần ListView và Adapter bạn đã được học từ lâu rồi nên không cần giải thích thêm nhiều.

– Tiếp tục vào MainActivity:


package tranduythanh.com;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends Activity {

@Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 Button b=(Button) findViewById(R.id.button1);
 b.setOnClickListener(new OnClickListener() {

@Override
 public void onClick(View arg0) {
 // TODO Auto-generated method stub
 doStart();
 }
 });
 }
 private void doStart()
 {
 //tạo 1 tiến trình
 MyAsyncTask tt=new MyAsyncTask(this);

 EditText edt=(EditText) findViewById(R.id.editText1);
 //lấy giá trị nhập từu EditText
 int so=Integer.parseInt(edt.getText()+"");
 //thực thi tiến trình với đối số truyền vào là so
 //nó được dùng trong đối số của doInBackground
 tt.execute(so);
 }
 @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;
 }

}

– Bạn thấy Tui gọi lệnh : tt.execute(so); đối số [so] sẽ được truyền vào doinBackground….

– Trong doInBackground, bạn thấy kết quả trả về là ArrayList<Integer> chính là do đối số thứ 3 ta khai báo là ArrayList<Integer>. Thông qua đây để ta lưu trữ được kết quả trong suốt quá trình mà tiến trình thực thi.

– Bạn cố gắng đọc hiểu, chúc bạn thành công!

– Bạn có thể tải coding mẫu ở đây: http://www.mediafire.com/download/e4g8a1cy67lqafq/LearnMultiThreading_GetResult_AsyncTask.rar

– Bài kế tiếp Tui sẽ kết hợp Handler class và AsyncTask class để tạo một Project cập nhập Control lúc runtime, bài này hơi phức tạp, bạn chú ý theo dõi.

Bài 37: Xử lý đa tiến trình bằng AsyncTask


Các bài tập 34, 35,36 Tui đã trình bày về xử lý đa tiến trình dùng Handler class. Trong bài tập này Tui muốn trình bày một kỹ thuật mới đó là cách xử lý bằng AsyncTask. Nó hơi hơi phức tạp 1 xí, nhưng cũng không có khó khăn gì … vấn đề là bạn hãy làm đi làm lại thật nhiều lần để hiểu thấu đáo về kỹ thuật này. Khi đã hiểu rồi thì bạn không cần dùng Handler class nữa mà nên dùng luôn AsyncTask. Đặc biệt khi xử lý đa tiến trình mà lại muốn kiểm tra kết quả trả về của tiến trình thì ta phải dùng AsyncTask.

Tui sẽ trình bày sơ qua về lý thuyết của AsyncTask trước khi vào những ví dụ cụ thể:

Bạn xem hình chụp 1 sau (bạn nhìn vào các đường mũi tên trỏ các parameter):

mul9

và hình số 2 sau như (nhìn vào các đường chỉ parameter):

mul10

– Tui sẽ giải thích kỹ về vấn đề này:

Trong AsyncTask<Params, Progress, Result>  có 3 đối số là các Generic Type:

Params: Là giá trị ((biến) được truyền vào khi gọi thực thi tiến trình và nó sẽ  được truyền vào doInBackground

Progress: Là  giá trị (biến) dùng để update giao diện diện lúc tiến trình thực thi, biến này sẽ được truyền vào hàm onProgressUpdate.

Result: Là biến dùng để lưu trữ kết quả trả về sau khi tiến trình thực hiện xong.

Những đối số nào không sử dụng trong quá trình thực thi tiến trình thì ta thay bằng Void.

Thông thường trong 1 AsyncTask sẽ chứa 4 hàm, đó là :

onPreExecute() : Tự động được gọi đầu tiên khi tiến trình được kích hoạt.

doInBackground(): Được thực thi trong quá trình tiến trình chạy nền, thông qua hàm này để ta gọi hàm onProgressUpdate để cập nhật giao diện (gọi lệnh publishProgress). Ta không thể cập nhật giao diện trong hàm doInBackground().

onProgressUpdate (): Dùng để cập nhật giao diện lúc runtime

onPostExecute(): Sau khi tiến trình kết thúc thì hàm này sẽ tự động sảy ra. Ta có thể lấy được kết quả trả về sau khi thực hiện tiến trình kết thúc ở đây.

Trong 4 hàm trên thì hàm doInBackground() bắt buộc phải tồn tại, còn các hàm khác có thể khuyết, nhưng theo Tui các bạn nên sử dụng đầy đủ 4 hàm đã nêu.

Đối với AsyncTask thì ta cần tạo một lớp kế thừa từ AsyncTask, sau đó từ MainActivity ta gọi hàm execute() của tiến trình này là OK.

Sau đây Tui sẽ làm một ví dụ cập nhật ProgressBar bằng AsyncTask (tui đã làm bằng Handler class trong bài 34):

async1

– Bài ví dụ trên chỉ đơn giản là cập nhật Progressbar (không quản lý kết quả trả về), nên đối số 1 và đối số 3 ta để Void, đối số 2 ta để Integer.

– Bạn tạo một Project như hình chụp dưới đây (với MyAsyncTask kết thừa từ AsyncTask):

async2

– Layout XML tương tự như bài 34:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/LinearLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context=".MainActivity" >

<TextView
 android:id="@+id/textView1"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:gravity="center"
 android:text="0%"
 android:textSize="30sp" />

<ProgressBar
 android:id="@+id/progressBar1"
 style="?android:attr/progressBarStyleHorizontal"
 android:layout_width="match_parent"
 android:layout_height="wrap_content" />

<Button
 android:id="@+id/button1"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_gravity="center"
 android:text="Start" />

</LinearLayout>

– Bạn xem Tui giải thích source code MyAsyncTask dưới này:


package tranduythanh.com;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.SystemClock;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class MyAsyncTask
extends AsyncTask<Void, Integer, Void> {
 //khai báo Activity để lưu trữ địa chỉ của MainActivity
 Activity contextCha;
 //constructor này được truyền vào là MainActivity
 public MyAsyncTask(Activity ctx)
 {
 contextCha=ctx;
 }
 //hàm này sẽ được thực hiện đầu tiên
 @Override
 protected void onPreExecute() {
 // TODO Auto-generated method stub
 super.onPreExecute();
 Toast.makeText(contextCha, "onPreExecute!",
 Toast.LENGTH_LONG).show();
 }
 //sau đó tới hàm doInBackground
 //tuyệt đối không được cập nhật giao diện trong hàm này
 @Override
 protected Void doInBackground(Void... arg0) {
 for(int i=0;i<=100;i++)
 {
 //nghỉ 100 milisecond thì tiến hành update UI
 SystemClock.sleep(100);
 //khi gọi hàm này thì onProgressUpdate sẽ thực thi
 publishProgress(i);
 }
 return null;
 }
 /**
 * ta cập nhập giao diện trong hàm này
 */
 @Override
 protected void onProgressUpdate(Integer... values) {
 // TODO Auto-generated method stub
 super.onProgressUpdate(values);
 //thông qua contextCha để lấy được control trong MainActivity
 ProgressBar paCha=(ProgressBar) contextCha
 .findViewById(R.id.progressBar1);
 //vì publishProgress chỉ truyền 1 đối số
 //nên mảng values chỉ có 1 phần tử
 int giatri=values[0];
 //tăng giá trị của Progressbar lên
 paCha.setProgress(giatri);
 //đồng thời hiện thị giá trị là % lên TextView
 TextView txtmsg=(TextView)
 contextCha.findViewById(R.id.textView1);
 txtmsg.setText(giatri+"%");
 }
 /**
 * sau khi tiến trình thực hiện xong thì hàm này sảy ra
 */
 @Override
 protected void onPostExecute(Void result) {
 // TODO Auto-generated method stub
 super.onPostExecute(result);
 Toast.makeText(contextCha, "Update xong roi do!",
 Toast.LENGTH_LONG).show();
 }
 /**
 * tui làm 2 hàm ví dụ này về params trong Android
 */
 public void goividu()
 {
 viduParamschoham();
 viduParamschoham(5);
 viduParamschoham(5,6);
 viduParamschoham(5,6,5,6,7,8,9,0,0);
 }
 /**
 * dấu ... dùng khai báo param
 * tức là ta truyền bao nhiêu đối số cũng được
 * ds trở thành mảng 1 chiều
 * @param ds
 */
 public void viduParamschoham(int ... ds)
 {
 //test chơi...
 int pt0=ds[0];//có lỗi nếu như không truyền đối số nào
 for(int n:ds)
 {
 System.out.println(n);
 }
 //hoặc
 for(int i=0;i<ds.length;i++)
 {
 System.out.println(ds[i]);
 }
 }
}

– Ở đoạn code trên có 2 hàm cuối cùng là Tui viết thêm vào để giải thích cho việc sử dụng params trong Java, bạn cố gắng đọc để hiểu.

– Tiến trình MyASyncTask sẽ được thực thi khi bên MainActivity ta gọi phương thức: .execute()

source code MainActivity:


package tranduythanh.com;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

Button btnStart;
 //khai báo MyAsyncTask
 MyAsyncTask mytt;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 btnStart=(Button) findViewById(R.id.button1);
 btnStart.setOnClickListener(new OnClickListener() {

 @Override
 public void onClick(View arg0) {
 // TODO Auto-generated method stub
 doStart();
 }
 });

 }
 private void doStart()
 {
 //truyền this (chính là MainActivity hiện tại) qua Child Thread
 mytt =new MyAsyncTask(this);
 //Kích hoạt Tiến trình
 //khi gọi hàm này thì onPreExecute của mytt sẽ thực thi trước
 mytt.execute();
 }
 @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;
 }

}

– Vô cùng đơn giản….. mọi thứ đẩy về cho Child Thread.

– Bạn có thể tải coding mẫu ở đây: http://www.mediafire.com/download/9or85fvafatp14o/LearnMultiThreading_ProgressBar_AsyncTask.rar

– Trong bài kế tiếp Tui sẽ làm 1 ví dụ AsyncTask nhưng có quản lý kết quả trả về, bài này tương đối phức tạp, các bạn chú ý theo dõi

– Chúc các bạn thành công!

Bài 36: Update ListView At runtime by Handler class using post


Tiếp tục với xử lý đa tiến trình, trong bài tập này Tôi dùng phương thức post của Handler class để xử lý đa tiến trình, bài tập này đơn giản là cập nhật danh sách các số ngẫu nhiên vào ListView. Màn hình chính của chương trình như sau:

mul7– Khi bấm nút “Update thử“, chương trình sau  150 miliseconds sẽ tự động thêm các số ngẫu nhiên vào Listview bên dưới, khi nào thêm đủ số lượng như đã nhập từ EditText thì sẽ thông báo “Xong rồi đó…“.

– Ta tạo một Project tên là gì đó cũng được, giả sử như hình Tui chụp bên dưới:

mul8

Ở trên Tui làm đại giao diện như vậy, bạn có thể thiết kế lại theo ý mình.

Một số lệnh về ListView thì Tui đã trình bày kỹ trong các bài tập trước rồi nên ở đây Tui chỉ nói phần using post.

Tui đã giải thích trong từng dòng lệnh, ở đây bạn để ý là tui không có dùng message mà tui dùng post. Có thể cách này sẽ dễ hơn cách dùng message, Nếu như bạn hiểu 2 cách thì thì sử dụng loại nào cũng được:


package tranduythanh.com;

import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;

import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends Activity {

//khai báo Handler để quản lý Main Thread
 Handler hangido=new Handler();
 ListView lvgido;
 //Tạo đối tượng ArrayList để lưu trữ danh sách các số nguyên
 ArrayList<Integer>arr=new
 ArrayList<Integer>();
 //khai báo Adapter để gán vào listview
 ArrayAdapter<Integer> adapter=null;
 //khai báo Atomic thay thế cho boolean
 AtomicBoolean ab=new AtomicBoolean(false);

Button btnOk;
 EditText edtOK;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 lvgido =(ListView) findViewById(R.id.listView1);
 btnOk=(Button) findViewById(R.id.button1);
 edtOK=(EditText) findViewById(R.id.editText1);
 //khởi tạo đối tượng adapter
 adapter=new ArrayAdapter<Integer>(this,
 android.R.layout.simple_list_item_1, arr);
 //gán adapterc cho listview
 lvgido.setAdapter(adapter);

btnOk.setOnClickListener(new OnClickListener() {

@Override
 public void onClick(View arg0) {
 // TODO Auto-generated method stub
 doStart();
 }
 });
 }
 private void doStart()
 {
 //lấy số lượng số được nhập từ edittext
 final int so=Integer.parseInt(edtOK.getText()+"");
 //tạo đối tượng có khả năng sinh số ngẫu nhiên
 final Random rd=new Random();
 ab.set(false);
 //tạo tiến trình con
 Thread thCon=new Thread(new Runnable() {

@Override
 public void run() {
 //vòng lặp để thực hiện cập nhập giao diện
 for(int i=0;i<so && ab.get();i++)
 {
 //cho tiến trình ngừng 150 milisecond
 SystemClock.sleep(150);
 //gọi phương thức post để gửi message ra main Thread
 hangido.post(new Runnable() {
 @Override
 public void run() {
 // TODO Auto-generated method stub
 //hàm này thuộc Main Thread để có thể cập nhập giao diện
 //rd.nextInt(100) để trả về số ngẫu nhiên từ 0-->99
 capnhapgiaodien(rd.nextInt(100));
 }
 });
 }
 //kết thúc vòng lặp thì gửi tiếp message ra Main Thread
 //để xác lập đã kết thúc tiến trình
 hangido.post(new Runnable() {

@Override
 public void run() {
 // TODO Auto-generated method stub
 //gọi hàm hiển thị kết thúc ở Main Thread
 thongbaoketthuccapnhat();
 }
 });
 }
 });
 ab.set(true);
 //khởi tạo Tiến trình
 thCon.start();
 }
 private void capnhapgiaodien(int so)
 {
 //đưa dữ liệu mới gửi từ Child Thread gửi về vào arr
 arr.add(so);
 //cập nhập lại Listview:
 adapter.notifyDataSetChanged();
 }
 private void thongbaoketthuccapnhat()
 {
 //thông báo đã kết thúc
 Toast.makeText(this, "Xong rồi đó....", Toast.LENGTH_LONG).show();
 }
 @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;
 }

}

Bạn hãy so sanh 2 bài tập trước (dùng Message) với bài tập này để hiểu sâu hơn về Handler class. Bạn nhớ là khi sử dụng đa tiến trình thì chương trình cứ update UI bình thường, bạn vẫn có thể thao tác với các control khác trên giao diện.

Bạn có thể tải source nguồn ở đây:

http://www.mediafire.com/download/suaj1x7eswqmpou/LearnMultiThreading_usingPost_UpdateListView.rar

Trong các bài tập kết tiếp Tui sẽ trình bày cách xử lý đa tiến trình bằng AsyncTask, AsyncTask rất hay và quan trọng. Các bạn nhớ chú ý theo dõi.

chúc bạn thành công!

Bài 35 : Vẽ Button lúc Runtime, dùng Using Message của Handler class


Ở bài tập 34 , Tôi đã trình bày về cách xử lý đa tiến trình dùng Using Message của Handler class, trong bài này cũng với Handler class nhưng Tôi sẽ dùng Using Post để xử lý đa tiến trình. Cụ thể Tôi sẽ cho vẽ các Button động lúc Runtime.

Ví dụ như màn hình dưới đây:

mul5– Khi người sử dụng nhập giá trị vào EditText và nhấn nút DrawButton thì chương trình sẽ vẽ Button đủ số lượng nhập, nếu nó vượt quá màn hình điện thoại thì ta phải cho phép Scroll.

– Các bạn tạo một Project và layout giao diện như hình dưới đây:

mul6

– Tôi trình bày coding MainActivity ở dưới đây:


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.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;

public class MainActivity extends Activity {

Handler handlerMain;
 AtomicBoolean atomic=null;
 LinearLayout layoutdevebutton;
 Button btnOk;
 EditText edtOk;

@Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 //lấy LinearLayout chứa Button ra
 layoutdevebutton=(LinearLayout) findViewById(R.id.layout_draw_button);
 btnOk=(Button) findViewById(R.id.btnDrawButton);
 edtOk=(EditText) findViewById(R.id.editNumber);
 handlerMain=new Handler()
 {
 @Override
 public void handleMessage(Message msg) {
 // TODO Auto-generated method stub
 super.handleMessage(msg);
 //Nhận nhãn của Button được gửi về từ tiến trình con
 String nhan_button=msg.obj.toString();
 //khởi tạo 1 Button
 Button b=new Button(MainActivity.this);
 //thiết lập nhãn cho Button
 b.setText(nhan_button);
 //thiết lập kiểu Layout : Width, Height
 LayoutParams params=new
 LayoutParams(LayoutParams.MATCH_PARENT,
 LayoutParams.WRAP_CONTENT);
 //thiết lập layout cho Button
 b.setLayoutParams(params);
 //đưa Button vào layoutdevebutton
 layoutdevebutton.addView(b);
 }
 };
 btnOk.setOnClickListener(new OnClickListener() {

@Override
 public void onClick(View arg0) {
 // TODO Auto-generated method stub
 doStart();
 }
 });
 }
 private void doStart()
 {
 atomic=new AtomicBoolean(false);
 final int sobutton=Integer.parseInt(edtOk.getText()+"");
 Thread thCon=new Thread(new Runnable() {

 @Override
 public void run() {
 // TODO Auto-generated method stub
 for(int i=0;i<sobutton && atomic.get();i++)
 {
 //nghỉ 200 mili second
 SystemClock.sleep(200);
 //lấy message từ Main Thread
 Message msg=handlerMain.obtainMessage();
 //gán dữ liệu cho msg Mainthread, lưu vào biến obj
 //chú ý ta có thể lưu bất kỳ kiểu dữ liệu nào vào obj
 msg.obj="Button thứ "+i;
 //gửi trả lại message cho Mainthread
 handlerMain.sendMessage(msg);
 }
 }
 });
 atomic.set(true);
 //thực thi tiến trình
 thCon.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;
 }

}

Bạn hãy cố gắng làm lại bài tập 34, 35 để hiểu cách sử dụng Handler class với Using Message. Bài kế tiếp Tôi sẽ sử dụng Using Post của Handler class để demo đa tiến trình trong việc cập nhật giao diện lúc runtime.

Bạn có thể tải coding mẫu đầy đủ của bài này ở đây: http://www.mediafire.com/download/cpc753fpr9726f9/LearnMultiThreading_usingMessage_DrawButton.rar

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

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.


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();
 }
 }
}

Ở 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


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();
 }
 }
}

– 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 :


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/LinearLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context=".MainActivity" >

<TextView
 android:id="@+id/textView1"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="#FFFFFF"
 android:gravity="center"
 android:text="0%"
 android:textColor="#008000"
 android:textSize="25sp" />

<ProgressBar
 android:id="@+id/progressBar1"
 style="?android:attr/progressBarStyleHorizontal"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:max="100"
 android:minHeight="50dp" />
 <Button
 android:id="@+id/btnstart"
 android:layout_width="105dp"
 android:layout_height="wrap_content"
 android:layout_gravity="center"
 android:text="Start" />
</LinearLayout>

– 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


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;

}

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.