首页 > 代码库 > AsyncTask、多线程及线程通信
AsyncTask、多线程及线程通信
AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单。相对来说AsyncTask更轻量级一些,适用于简单的异步处理,不需要借助线程和Handler即可实现。
AsyncTask是抽象类.AsyncTask定义了三种泛型类型 Params,Progress和Result。
Params 启动任务执行的输入参数,比如HTTP请求的URL。
Progress 后台任务执行的百分比。
Result 后台执行任务最终返回的结果,比如String。
AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,这些方法不应该由应用程序调用,开发者需要做的就是实现这些方法。
1) 子类化AsyncTask
2) 实现AsyncTask中定义的下面一个或几个方法
onPreExecute(), 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。
doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
onProgressUpdate(Progress...),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。
onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread。
为了正确的使用AsyncTask类,以下是几条必须遵守的准则:
1) Task的实例必须在UI thread中创建
2) execute方法必须在UI thread中调用
3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法
4) 该task只能被执行一次,否则多次调用时将会出现异常
doInBackground方法和onPostExecute的参数必须对应,这两个参数在AsyncTask声明的泛型参数列表中指定,第一个为doInBackground接受的参数,第二个为显示进度的参数,第第三个为doInBackground返回和onPostExecute传入的参数。
可以调用cancel(boolean)去取消一个任务,在cancel(boolean)调用后,isCancelled()会返回true,并且在doInBackground(Object[])调用后会立即调用onCancelled(Object),而不会去调用onPostExecute(Object)。在调用cancel(boolean)时,如果线程还没有开始执行,那么线程不会去运行;如果线程已经开始运行了,那么mayInterruptIfRunning 标志决定了线程会否应该被中断。因此我们在doInBackground(Object[])中应该定期的检查isCancelled()的值,以保证线程能够快速的中断。
package com.example.asynctask;import java.io.IOException;import org.apache.http.HttpResponse;import org.apache.http.client.ClientProtocolException;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import android.os.AsyncTask;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.ImageView;import android.widget.ProgressBar;import android.widget.Toast;import android.graphics.Bitmap;import android.graphics.BitmapFactory;public class Main_Activity extends Activity { private ImageView m_imageView; private Button m_button; private Button m_but; private ProgressBar m_proBar; Task task; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); m_imageView = (ImageView)findViewById(R.id.imageView); m_button = (Button)findViewById(R.id.download_btn); m_proBar = (ProgressBar)findViewById(R.id.progressBar); m_but = (Button)findViewById(R.id.cancel_btn); task = null; m_button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub task = new Task(); task.execute("http://www.baidu.com/img/shouye_b5486898c692066bd2cbaeda86d74448.gif"); } }); m_but.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub task.cancel(false); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main_, menu); return true; } class Task extends AsyncTask<String,Integer,Bitmap>{ @Override protected Bitmap doInBackground(String... params) {//处理后台执行的任务,在后台线程执行 // TODO Auto-generated method stub publishProgress(0);//将会调用onProgressUpdate(Integer... progress)方法 HttpClient hc = new DefaultHttpClient(); publishProgress(30); HttpGet hg = new HttpGet(params[0]); Bitmap bm = null; HttpResponse hr = null; try { Thread.currentThread().sleep(1000); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } if(isCancelled()) return null; try { hr = hc.execute(hg); bm = BitmapFactory.decodeStream(hr.getEntity().getContent()); } catch (ClientProtocolException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } if(isCancelled()) return null; publishProgress(100); return bm; } protected void onProgressUpdate(Integer... progress) {//在调用publishProgress之后被调用,在ui线程执行 m_proBar.setProgress(progress[0]); } protected void onPostExecute(Bitmap result) {//后台任务执行完之后被调用,在ui线程执行 if(result != null) { Toast.makeText(Main_Activity.this, "成功获取图片", Toast.LENGTH_LONG).show(); m_imageView.setImageBitmap(result); }else { Toast.makeText(Main_Activity.this, "获取图片失败", Toast.LENGTH_LONG).show(); m_proBar.setProgress(0); } publishProgress(0); } protected void onPreExecute () {//在 doInBackground(Params...)之前被调用,在ui线程执行 m_imageView.setImageBitmap(null); m_proBar.setProgress(0);//进度条复位 } protected void onCancelled () {//在ui线程执行 m_proBar.setProgress(0);//进度条复位 Toast.makeText(Main_Activity.this, "取消加载", Toast.LENGTH_LONG).show(); } };}
在程序中使用了网络,因此需要在AndroidManifest.xml添加
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
Handler
Looper 是消息队列的管理者,消息循环
Handler 是消息的处理者
Message 消息
MessageQueue 消息队列
Handler 只能够关联一个线程的Looper,一个Looper只能管理一个MessageQueue。可以通过关联的Handler向线程发送Message或者Runnable到消息队列中。
package com.example.handlemsg;import android.os.Bundle;import android.os.Handler;import android.os.HandlerThread;import android.os.Looper;import android.os.Message;import android.annotation.SuppressLint;import android.app.Activity;import android.util.Log;import android.view.Menu;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends Activity { TextView tv; Button but1; Button but2; MyHandler hd, subHd; HandlerThread sub; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView)findViewById(R.id.tv); but1 = (Button)findViewById(R.id.button1); but2 = (Button)findViewById(R.id.button2); hd = new MyHandler(); but1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub //给主线程自己发送一条消息 hd.sendEmptyMessage(0); } }); but2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub //新建一个带有Looper的线程 sub = new HandlerThread("sub-thread"); sub.start(); //给子线程关联Handler,并发送一条消息给子线程 subHd = new MyHandler(sub.getLooper()); subHd.sendEmptyMessage(0); //给子线程发送一个Runnable,在Runnable中发送一条消息给主线程 subHd.post(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub //发送一条消息给主线程 hd.sendEmptyMessage(0); } }); } }); //终止子线程 //subHd.getLooper().quit(); } @SuppressLint("HandlerLeak") class MyHandler extends Handler{ public MyHandler() { // TODO Auto-generated constructor stub super(); } public MyHandler(Looper looper) { // TODO Auto-generated constructor stub super(looper); } public void handleMessage(Message msg){ switch(msg.what) { case 0: Log.d(Thread.currentThread().getName(), "msg 0"); break; case 1: Log.d(Thread.currentThread().getName(), "msg 1"); break; default: Log.d(Thread.currentThread().getName(), "msg default"); break; } }//handlemsg }//MyHandler}
synchronized
1. synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。
2. synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。
对synchronized(this)的一些理解
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
四、当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
参考:http://blog.csdn.net/cjjky/article/details/7353390
AsyncTask、多线程及线程通信