通常Android运行时只有一个线程,就是UI主线程,负责更新ui,也可以处理一些逻辑工作,但遇到复杂的工作,就不可以直接丢给主线程来处理,不然UI线程就会卡在那,导致系统无响应。

    android中多线程实现主要依靠Handler和AsyncTask。


Hander的例子

首先看一下将复杂操作直接放在UI进程的例子.这里的复杂计算是计算圆周率。

布局文件,界面上只有一个非常嘲讽的Button。

       


然后看MainActivity

package com.empty.threaddemo; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.util.Log; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView;  public class MainActivity extends Activity { 	private Button mButton; 	private Runnable mRunnable; 	private Handler mHandler; 	double result; 	private int count = 0;  	private int i = 0;  	public static final int FINISHED= 0x000001;   	private static final String TAG = "ThreadDemo";  	@Override 	protected void onCreate(Bundle savedInstanceState) { 		super.onCreate(savedInstanceState); 		setContentView(R.layout.activity_main); 		mButton=(Button)findViewById(R.id.button1); 		mButton.setOnClickListener(new MyButtonListener()); 	} 	@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; 	} 	class MyButtonListener implements OnClickListener{    		@Override   		public void onClick(View v) { 			result=getPI(100000000); 			new AlertDialog.Builder(MainActivity.this)   			.setTitle("result") 			.setMessage(""+result) 			.setIcon(android.R.drawable.ic_dialog_info) 			.setCancelable(true)  			.show();		 		}   	}  	public double getPI(int time) 	{  		float result=0;  		for(int i=1;i<=time;i++){	 			double value=4.0/(2*i-1);	 			if (i % 2 == 1) result+=value;	 			else result-=value;	 }	 		return result; 	}   } 


编译,运行,点Button,然后发现被坑了...



点击按钮后,UI线程就被阻塞用于计算PI,由于耗时太久,系统直接无响应了。


下面使用Handler来改进,先复习一下Handler的用法:

1)在Activity或Activity的Widget中开发Handler类的对象,并重写handleMessage方法。
2)在新启动的线程中调用sendEmptyMessage或者sendMe ssage方法向Handler发送消息。
3)Handler类的对象用handleMessage方法接收消息,然后根据消息的不同执行不同的操作。

MainActivity改写之后变成这样:

 
  

package com.empty.threaddemo;   import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.util.Log; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView;  public class MainActivity extends Activity { 	private Button mButton; 	private Runnable mRunnable; 	private Handler mHandler; 	double result; 	private int count = 0;  	private int i = 0;  	public static final int FINISHED= 0x000001;   	private static final String TAG = "ThreadDemo";  	@Override 	protected void onCreate(Bundle savedInstanceState) { 		super.onCreate(savedInstanceState); 		setContentView(R.layout.activity_main); 		mButton=(Button)findViewById(R.id.button1); 		mButton.setOnClickListener(new MyButtonListener());  		mHandler =  new Handler() 		{ 			@Override   			public void handleMessage(Message msg) {   				if (msg.what == FINISHED) {   					new AlertDialog.Builder(MainActivity.this)   					.setTitle("Result") 					.setMessage(""+result) 					.setIcon(android.R.drawable.ic_dialog_info) 					.setCancelable(true)  					.show();  				}   				super.handleMessage(msg);   			}   		}; 		new MyThread().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; 	} 	class MyButtonListener implements OnClickListener{    		@Override   		public void onClick(View v) { 			new AlertDialog.Builder(MainActivity.this)   			.setTitle("爽!") 			.setIcon(android.R.drawable.ic_dialog_info) 			.setCancelable(true)  			.show();		 		}   	}  	public double getPI(int time) 	{  		float result=0;  		for(int i=1;i<=time;i++){	 			double value=4.0/(2*i-1);	 			if (i % 2 == 1) result+=value;	 			else result-=value;	 }	 		return result; 	} 	public class MyThread extends Thread {   		public void run() {   			Message msg = new Message();  			msg.what = FINISHED; 			result=getPI(100000000); 			mHandler.sendMessage(msg);  		}   	}     }


程序运行的过程是:当Activity启动的时候,初始化了一些View,同时创建了一个handler用于处理消息,最后启动了一个Thread,计算PI放在了Thread中而不是占用UI线程,所以Button一直可以与用户进行交互。当计算完成的时候,Handler会接受到来自Thread的Message,Handler处理消息,更新UI,即弹出对话框。

         


使用AsyncTask

先大概认识下Android.os.AsyncTask类:
* android的类AsyncTask对线程间通讯进行了包装,提供了简易的编程方式来使后台线程和UI线程进行通讯:后台线程执行异步任务,并把操作结果通知UI线程。
* AsyncTask是抽象类.AsyncTask定义了三种泛型类型 Params,Progress和Result。
  * Params 启动任务执行的输入参数,比如HTTP请求的URL。
  * Progress 后台任务执行的百分比。
  * Result 后台执行任务最终返回的结果,比如String,Integer等。
* AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,开发者需要实现这些方法。
  * 1) 继承AsyncTask
  * 2) 实现AsyncTask中定义的下面一个或几个方法
* onPreExecute(), 该方法将在执行实际的后台操作前被UI 线程调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条,或者一些控件的实例化,这个方法可以不用实现。
* doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台处理工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
* onProgressUpdate(Progress...),在publishProgress方法被调用后,UI 线程将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。
* onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI 线程调用,后台的计算结果将通过该方法传递到UI 线程,并且在界面上展示给用户.
* onCancelled(),在用户取消线程操作的时候调用。在主线程中调用onCancelled()的时候调用。
为了正确的使用AsyncTask类,以下是几条必须遵守的准则:
  1) Task的实例必须在UI 线程中创建
  2) execute方法必须在UI 线程中调用
  3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法,需要在UI线程中实例化这个task来调用。
  4) 该task只能被执行一次,否则多次调用时将会出现异常
doInBackground方法和onPostExecute的参数必须对应,这两个参数在AsyncTask声明的泛型参数列表中指定,第一个为doInBackground接受的参数,第二个为显示进度的参数,第第三个为doInBackground返回和onPostExecute传入的参数。

修改上面的例子,用AsyncTask来实现。

修改一下布局文件,添加一个进度条。

            

然后是MainActivity.

package com.empty.threaddemo;   import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.util.Log; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast;  public class MainActivity extends Activity { 	private Button mButton; 	private Handler mHandler; 	private ProgressBar pb; 	private TextView resultView; 	double result; 	private int TIME= 10000;  	private int i = 0;  	public static final int FINISHED= 0x000001;   	private static final String TAG = "ThreadDemo";  	@Override 	protected void onCreate(Bundle savedInstanceState) { 		super.onCreate(savedInstanceState); 		setContentView(R.layout.activity_main); 		mButton=(Button)findViewById(R.id.button1); 		mButton.setOnClickListener(new MyButtonListener()); 		pb=(ProgressBar)findViewById(R.id.pb); 		resultView=(TextView)findViewById(R.id.textView1); 		CalculateTask dTask = new CalculateTask();            dTask.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; 	} 	class MyButtonListener implements OnClickListener{    		@Override   		public void onClick(View v) { 			new AlertDialog.Builder(MainActivity.this)   			.setTitle("爽!") 			.setIcon(android.R.drawable.ic_dialog_info) 			.setCancelable(true)  			.show();		 		}   	}  	 	 class CalculateTask extends AsyncTask{   	        //后面尖括号内分别是参数(例子里是线程休息时间),进度(publishProgress用到),返回值 类型   	           	        @Override   	        protected void onPreExecute() {   	            //第一个执行方法   	        	Toast.makeText(getApplicationContext(), "Begin calculate!", Toast.LENGTH_SHORT).show(); 	            super.onPreExecute();   	        }   	           	        @Override   	        protected String doInBackground(Integer... params) {   	            //第二个执行方法,onPreExecute()执行完后执行   	            for(int i=1;i<=TIME;i++){   	            	double value=4.0/(2*i-1);	 	    			if (i % 2 == 1) result+=value;	 	    			else result-=value; 	    			 	    			publishProgress((int) (100*(i*1.0/TIME)));  	    			}	  	            return ""+result;   	        }   	   	        @Override   	        protected void onProgressUpdate(Integer... progress) {   	            //这个函数在doInBackground调用publishProgress时触发,虽然调用时只有一个参数   	            //但是这里取到的是一个数组,所以要用progesss[0]来取值   	            //第n个参数就用progress[n]来取值   	        	pb.setProgress(progress[0]); 	           resultView.setText(progress[0]+"%");   	            super.onProgressUpdate(progress);   	        }   	   	        @Override   	        protected void onPostExecute(String r) {   	            //doInBackground返回时触发,换句话说,就是doInBackground执行完后触发   	            //这里的result就是上面doInBackground执行后的返回值,所以这里是"执行完毕"   	            //setTitle(result); 	            resultView.setText(r); 	            super.onPostExecute(r);   	        }   	           	    }    }

运行结果:

    


运行过程:Activity初始化之后,直接开一个创建一个task在后台跑,一边跑一边通过 onProgressUpdate方法更新UI,与此同时,UI线程还是可以响应Button。

最后当task运行完之后回调onPostExecute方法显示结果。


Thread中的run和start

run()方法相当于调用你那个类的一个方法了,即使你没有实现Runnable接口写个run方法进行调用,与调用run方法是一样的,并不会开启一个线程,就相当于还是在主线程中执行任务,开启线程必须用Start方法,他自己去调run方法。


参考

Android中AsyncTask的简单用法-http://blog.csdn.net/cjjky/article/details/6684959