android中的线程

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

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


Hander的例子

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

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

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >


    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_marginRight="20dp"
        android:layout_marginTop="30dp"
        android:text="求点我!" />

</RelativeLayout>


然后看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,然后发现被坑了...

android中的线程_第1张图片


点击按钮后,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,即弹出对话框。

android中的线程_第2张图片         android中的线程_第3张图片


使用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来实现。

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

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <ProgressBar
        android:id="@+id/pb"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/button1"
        android:layout_marginTop="20dp" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginTop="26dp"
        android:text="独孤求点" />

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/button1"
        android:layout_alignBottom="@+id/button1"
        android:layout_marginLeft="54dp"
        android:layout_toRightOf="@+id/button1"
        android:text="TextView" />

</RelativeLayout>

然后是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<Integer, Integer, String>{  
	        //后面尖括号内分别是参数(例子里是线程休息时间),进度(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);  
	        }  
	          
	    }  

}

运行结果:

android中的线程_第4张图片    android中的线程_第5张图片


运行过程: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

你可能感兴趣的:(android中的线程)