Android 之异步任务(AsyncTask,Handler,Message,looper)
AsyncTask: 3个类型(Params,Progress和Result),4个步骤(onPreExecute(),doInBackground(Params…),onProgressUpdate(Progress…), onPostExecute(Result) )
AsyncTask直接继承于Object类,位置为android.os.AsyncTask。要使用AsyncTask工作我们要提供三个泛型参数,并重载几个方法(至少重载一个)。
AsyncTask定义了三种泛型类型 Params,Progress和Result。可以为Void表示无类型
- Params 启动任务执行的输入参数,比如HTTP请求的URL。
- Progress 后台任务执行的百分比。
- Result 后台执行任务最终返回的结果,比如String。
使用过AsyncTask 的同学都知道一个异步加载数据最少要重写以下这两个方法:
- doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。
- onPostExecute(Result) 相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回
有必要的话你还得重写以下这三个方法,但不是必须的:
- onProgressUpdate(Progress…) 可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
- onPreExecute() 这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
- onCancelled() 用户调用取消时,要做的操作
使用AsyncTask类,以下是几条必须遵守的准则:
- Task的实例必须在UI thread中创建;
- execute方法必须在UI thread中调用;
- 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法;
- 该task只能被执行一次,否则多次调用时将会出现异常;
一个最简单的异步任务例子:从网上下载一副图片
package com.example.android_03asynctask; import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils; import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView; public class MainActivity extends Activity { private Button button;
private ImageView imageView;
private String image_Path="http://pic14.nipic.com/20110522/7411759_164157418126_2.jpg";
private ProgressDialog progressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button=(Button)this.findViewById(R.id.button1);
imageView=(ImageView)this.findViewById(R.id.imageView1);
imageView=(ImageView)this.findViewById(R.id.imageView1);
progressDialog=new ProgressDialog(this);
progressDialog.setTitle("提示信息");
progressDialog.setMessage("正在下载....");
button.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
//在UI主线程中不能直接访问网络,所以下面的写法是不正确的,必须使用异步任务
/* HttpClient httpClient=new DefaultHttpClient();
HttpGet httpGet=new HttpGet(image_Path);
try {
httpClient.execute(httpGet);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}*/ //执行异步任务操作
new myTask().execute(image_Path);
}
});
} /**
* 1.声明异步类继承AsyncTask,含有三个参数
* params 要执行的任务,一般为网络路径url
* progress 进度的刻度 一般为 Void,表示没有类型
* result任务执行的返回值
* 2.
* @author Administrator
*
*/
public class myTask extends AsyncTask<String, Void, Bitmap>{ //任务执行之前的操作
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
progressDialog.show();
} //主要完成耗时操作
@Override
protected Bitmap doInBackground(String... params) {
// TODO Auto-generated method stub
//使用网络连接类HttpClient完成对网络数据的提取
HttpClient httpClient=new DefaultHttpClient();
HttpGet httpGet=new HttpGet(params[]);//从可变参数取得第一个参数
Bitmap bitmap=null;
try {
HttpResponse httpResponse=httpClient.execute(httpGet);
if(httpResponse.getStatusLine().getStatusCode()==){
HttpEntity httpEntity=httpResponse.getEntity();//获取结果实体
byte[] data=EntityUtils.toByteArray(httpEntity);//将结果实体转换成字节数组
bitmap=BitmapFactory.decodeByteArray(data, , data.length);//将数据转换成bitmap对象 } } catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return bitmap;
} //主要是更新UI操作
@Override
protected void onPostExecute(Bitmap result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
imageView.setImageBitmap(result);
progressDialog.dismiss();
}
}
@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;
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
上面的实例中并未显示进度条上的刻度,对上个例子完善如下:
package com.example.android_03asynctsk2; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils; import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView; public class MainActivity extends Activity {
private Button button;
private ImageView imageView;
private String image_Path = "http://pic14.nipic.com/20110522/7411759_164157418126_2.jpg";
private ProgressDialog progressDialog; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) this.findViewById(R.id.button1);
imageView = (ImageView) this.findViewById(R.id.imageView1);
progressDialog = new ProgressDialog(this);
progressDialog.setTitle("提示");
progressDialog.setMessage("正在下载....");
progressDialog.setCancelable(false);// 使屏幕失去焦点,直至下载完成才恢复焦点
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);// 设置进度条为横向的
button.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
new myTask().execute(image_Path); }
});
} public class myTask extends AsyncTask<String, Integer, Bitmap> { @Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
progressDialog.show();
} @Override
protected Bitmap doInBackground(String... params) {
// TODO Auto-generated method stub
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(params[]);// 从可变参数取得第一个参数
Bitmap bitmap = null;
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
InputStream inputStream=null;
try {
HttpResponse httpResponse = httpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == ) {
inputStream=httpResponse.getEntity().getContent();//得到输入流
long file_Length=httpResponse.getEntity().getContentLength();//获得文件总长度
int len=,total_length=;
byte[] data=new byte[];//每次读取的内容
while((len=inputStream.read(data))!=-){
total_length+=len;
int value=(int)((total_length/(float)file_Length)*);
publishProgress(value);//发布刻度到onProgressUpdate
byteArrayOutputStream.write(data, , len);
}
byte []result=byteArrayOutputStream.toByteArray();
bitmap=BitmapFactory.decodeByteArray(result, , result.length);//将数据转换成bitmap对象
} } catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
finally{
if(inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return bitmap;
} @Override
protected void onProgressUpdate(Integer... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
progressDialog.setProgress(values[]);
} @Override
protected void onPostExecute(Bitmap result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
imageView.setImageBitmap(result);
progressDialog.dismiss();
} } @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;
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
Handler:异步处理大师,发送、处理消息,
Handler扮演了往MQ上添加消息和处理消息的角色(只处理由自己发出的消息),即:
通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。
handler创建时会关联一个looper,默认的构造方法将关联当前线程的looper,不过这也是可以set的。
andriod提供了Handler 和 Looper 来满足线程间的通信。
Handler先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(MessageExchange)。
1)Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列)。
2)Handler: 你可以构造Handler对象来与Looper沟通,以便push新消息到MessageQueue里;或者接收Looper从Message Queue取出)所送来的消息。
3) Message Queue(消息队列):用来存放线程放入的消息。
4)线程:UIthread 通常就是main thread,而Android启动程序时会替它建立一个MessageQueue。
1.Handler创建消息
每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中引入了消息池。
Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。
使用消息池的好处是:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。
消息池提高了消息对象的复用,减少系统垃圾回收的次数。消息的创建流程如图1所示。
2.Handler发送消息
主要接受子线程发送的数据, 并用此数据配合主线程更新UI.
解释: 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程,默认有一个消息队列) , 主线程为管理界面中的UI控件,进行事件分发,
比如说, 你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。 如果此时需要一个耗时的操作,例如:
联网读取数据, 或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,,如果你放在主线程中的话,界面会
出现假死现象,如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 "强制关闭".
这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程是线程不安全的,也就是说,
更新UI只能在主线程中更新,子线程中操作是危险的.
这个时候,Handler就出现了.,来解决这个复杂的问题 , 由于Handler运行在主线程中(UI线程中), 它与子线程可以通过Message对象来传递数据,
这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据) , 把这些消息放入主线程队列中,配合主线程进行更新UI。
UI主线程初始化第一个Handler时会通过ThreadLocal创建一个Looper,该Looper与UI主线程一一对应。
使用ThreadLocal的目的是保证每一个线程只创建唯一一个Looper。之后其他Handler初始化的时候直接获取第一个Handler创建的Looper。
Looper初始化的时候会创建一个消息队列MessageQueue。至此,主线程、消息循环、消息队列之间的关系是1:1:1。
Handler、Looper、MessageQueue的初始化流程如图2所示:
图1 图2 图3
一个最简单的异步任务例子:从网上下载一副图片(handler+message)
package com.example.android_03handler; import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils; import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView; public class MainActivity extends Activity {
private Button button;
private ImageView imageView;
private String image_Path = "http://pic14.nipic.com/20110522/7411759_164157418126_2.jpg";
private final int IS_FINISHED=;
private ProgressDialog progressDialog;
/**
* Handler必须开启一个子线程
* 该子线程必须通过Message传递数据给Handler
*/
private Handler handler=new Handler(){ @Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
byte[] data=(byte[]) msg.obj;//将message发送回来的数据强制转换成字节数组
Bitmap bitmap=BitmapFactory.decodeByteArray(data, , data.length);
imageView.setImageBitmap(bitmap);
if(msg.what==IS_FINISHED){
progressDialog.dismiss();
}
} }; public class myThread implements Runnable{ //完成耗时任务操作
@Override
public void run() {
// TODO Auto-generated method stub
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(image_Path);
HttpResponse httpResponse =null;
try {
httpResponse = httpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == ) {
byte[] data=EntityUtils.toByteArray(httpResponse.getEntity());
Message message=Message.obtain();
message.obj=data;
message.what=IS_FINISHED;
handler.sendMessage(message); }
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
} }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) this.findViewById(R.id.button1);
imageView = (ImageView) this.findViewById(R.id.imageView1);
progressDialog=new ProgressDialog(this);
progressDialog.setTitle("提示");
progressDialog.setMessage("downloading.....");
progressDialog.setCancelable(false);
button.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
new Thread(new myThread()).start();
progressDialog.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.main, menu);
return true;
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
message简单代码(不建议使用new生成消息,建议使用message.obtain()或者Handler.obtainMessage(....))
package com.example.android_03message; import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button; public class MainActivity extends Activity {
private Button button;
private Handler handler=new Handler(){ @Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
int arg1=msg.arg1;
int arg2=msg.arg2;
int what=msg.what;
Object obj=msg.obj;
System.out.println("-->"+arg1+"-->"+arg2+"-->"+what+"-->"+obj);
Bundle bundle=msg.getData();
System.out.println(bundle.getStringArray("str").length);
} };
public class myThread implements Runnable{ @Override
public void run() {
// TODO Auto-generated method stub
//第一种方式
/*Message message=Message.obtain();
message.arg1=1;
message.arg2=2;
message.what=3;
message.obj="mlj";
handler.sendMessage(message);*/
//第二种方式,源码中提示,默认执行了message.target=handler表明将消息交给某个handler去发送,
//因此与第一种发送消息方式不同
/* Message message=Message.obtain(handler);
message.arg1=1;
message.arg2=2;
message.what=3;
message.obj="mlj";
message.sendToTarget();*/
//第三种方式Message.obtain(handler,what);
/* Message message=Message.obtain(handler,3);
message.arg1=1;
message.arg2=2;
message.obj="mlj";
message.sendToTarget();*/
//第四种方式Message.obtain(handler,what,obj);
/*Message message=Message.obtain(handler,3,"mlj");
message.arg1=1;
message.arg2=2;
message.sendToTarget();*/
//第五种方式Message.obtain(handler,what,arg1,arg2,obj);
Message message=Message.obtain(handler,,,,"mlj");
//在传基本数据之外还可以通过setData传一些复杂数据
Bundle bundle=new Bundle();
bundle.putStringArray("str", new String []{"mlj","mlj","mlj"});
message.setData(bundle);
message.sendToTarget(); } }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) this.findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
new Thread(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.main, menu);
return true;
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
handler的send(在线程内部发送消息方式)和post(new Runnable()...)两种发送消息方式的简单代码
package com.example.android_03handler_message; import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button; public class MainActivity extends Activity implements OnClickListener{
private Button button;
private Button button2;
private Handler handler=new Handler(){ @Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
System.out.println("-->"+msg.what);
} };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) this.findViewById(R.id.button1);
button2 = (Button) this.findViewById(R.id.button2);
button.setOnClickListener(this);
button2.setOnClickListener(this);
} @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;
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
} @Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.button1:
new Thread(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
//handler.sendEmptyMessage(3);
//handler.sendEmptyMessageAtTime(4, 1000);
handler.sendEmptyMessageDelayed(, );
}
}).start();
break;
case R.id.button2:
handler.post(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
handler.sendEmptyMessageDelayed(, );
}
});
break;
default:
break;
}
}
}
Looper:
字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程。所谓Looper线程就是循环工作的线程。
在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务,这就是Looper线程。
使用Looper类创建Looper线程很简单:
public class LooperThread extends Thread {
@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare(); // ...其他处理,如实例化handler // 开始循环处理消息队列
Looper.loop();
}
}
Looper.prepare()之后线程就升级为Looper线程了,线程中有一个Looper对象,它的内部维护了一个消息队列MQ。注意,一个Thread只能有一个Looper对象,
prepare()背后的工作方式一目了然,其核心就是将looper对象定义为ThreadLocal
调用loop方法后,Looper线程就开始真正工作了,它不断从自己的MQ中取出队头的消息(也叫任务)执行。
除了prepare()和loop()方法,Looper类还提供了一些有用的方法,比如:
Looper.myLooper()得到当前线程looper对象:
public static final Looper myLooper() {
// 在任意线程调用Looper.myLooper()返回的都是那个线程的looper
return (Looper)sThreadLocal.get();
}
getThread()得到looper对象所属线程:
public Thread getThread() {
return mThread;
}
quit()方法结束looper循环:
public void quit() {
// 创建一个空的message,它的target为NULL,表示结束循环消息
Message msg = Message.obtain();
// 发出消息
mQueue.enqueueMessage(msg, );
}
简单的looper代码:
package com.example.android_08looper; import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView; public class MainActivity extends Activity { private Button button1;
private TextView textView1;
private myHandler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1=(Button)this.findViewById(R.id.button1);
textView1=(TextView)this.findViewById(R.id.textView1);
//使用无参构造方法时handler并未与looper关联,但是为什么可以发送并接收消息呢?
//原因是Activity中默认有一个Looper对象来处理子线程发送的消息,其实相当于一下内容
//Looper looper=Looper.myLooper();//从UI主线程中得到looper
//mHandler=new myHandler(looper);
Looper looper=Looper.myLooper();
mHandler=new myHandler();
button1.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
new Thread(new myThread()).start();//在主线程中开启一个子线程
}
});
} public class myThread implements Runnable{ @Override
public void run() {
// TODO Auto-generated method stub
Message message=Message.obtain();
message.obj="mlj";
mHandler.sendMessage(message);
} }
public class myHandler extends Handler{ public myHandler(Looper looper) {
super(looper);
// TODO Auto-generated constructor stub
}
public myHandler() { } @Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
textView1.setText("-接收消息->>"+msg.obj);
} }
@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;
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
在以上例子中,如果handler的实例化脱离了UI主线程,而在子线程中实例化的话,就无法使用Activity中默认的looper对象,
就需要手工开启Looper,代码如下:
package com.example.android_08looper; import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView; public class MainActivity extends Activity { private Button button1;
private TextView textView1;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1=(Button)this.findViewById(R.id.button1);
textView1=(TextView)this.findViewById(R.id.textView1);
new Thread(new myThread()).start();
button1.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
Message message=Message.obtain();
message.obj="mlj";
mHandler.sendMessage(message);
}
});
} public class myThread implements Runnable{ @Override
public void run() {
// TODO Auto-generated method stub
Looper.prepare();
mHandler=new Handler(){ @Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
//textView1.setText("--->>接收UI主线程的消息"+msg.obj);//在子线程中无法更新UI所以改成以下语句
System.out.println("--->>接收UI主线程的消息"+msg.obj);
} };
Looper.loop();
} }
@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;
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
总结几点:
1.每个线程有且最多只能有一个Looper对象,它是一个ThreadLocal
2.Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行
3.Looper使一个线程变成Looper线程。
---------------------
综合案例(图文混排):客户端(AsyncTask,Handler,Message,listView)+服务器端,未缓存
服务器端最终返回给客户端的是jsonString,内容是:
{"products":[{"proprice":"","proaddress":"河南","proimage":"1.jpg","proname":"苹果","proid":"4a6c8e"},{"proprice":"","proaddress":"北京","proimage":"1.jpg","proname":"猕猴桃","proid":"5fd850"},{"proprice":"","proaddress":"广州","proimage":"1.jpg","proname":"香蕉","proid":"7c39b7"},{"proprice":"","proaddress":"广州","proimage":"1.jpg","proname":"梨","proid":""},{"proprice":"","proaddress":"北京","proimage":"1.jpg","proname":"西瓜","proid":"be3314"},{"proprice":"","proaddress":"上海","proimage":"1.jpg","proname":"桃子","proid":"c84833"}]}
客户端源文件目录如图:
步骤:
第一步:在布局文件中添加listView
<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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.android_08handler_product.MainActivity" > <ListView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" >
</ListView> </RelativeLayout>
第二步:在清单文件中添加网络授权
<uses-permission android:name="android.permission.INTERNET"/>
第三步:定义一个公共类CommonURL.java,存放客户端访问的网络地址
package com.example.android_08handler_product; public class CommonURL { //访问服务器产品url
static String Pro_URL="http://122.206.79.193:8080/xianfengProject/servlet/JsonAction?action_flag=more";
//访问服务器产品图片url
static String Pro_ImgURL="http://122.206.79.193:8080/xianfengProject/upload/";
}
第四步:编写主程序
注:图文混排时,不建议文字和图片在同一线程中出现或解决,一般是先处理文字再处理图片。
4.1 定义listView控件并取得
4.2为listView定义适配器(全局的)并在oncreate()方法中初始化,通常分以下几步
1.声明Context context,LayoutInflater layoutInflater两个变量并在构造方法中取值:this.**=**;
2.提供void setData()方法,由于取json数据时,数据类型是map类型,因此在适配器中需定义全局变量List<Map<String,Object>> list=null,
并在该方法中设置 this.list=list;
3. 完成适配器中除getView()方法以外的其他方法;
4.为适配器定义布局文件item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" > <ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="16dp"
android:src="@drawable/ic_launcher" /> <TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toRightOf="@+id/imageView1"
android:text="TextView" /> <TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView1"
android:layout_below="@+id/textView1"
android:text="TextView" /> <TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView2"
android:layout_below="@+id/textView2"
android:text="TextView" /> </RelativeLayout>
5. 完成适配器中getView()方法;
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View view=null;
if(convertView==null){
view=layoutInflater.inflate(R.layout.item, null);
}
else
view=convertView;
TextView name=(TextView)view.findViewById(R.id.textView1);
TextView address=(TextView)view.findViewById(R.id.textView2);
TextView price=(TextView)view.findViewById(R.id.textView3);
final ImageView imageView=(ImageView)view.findViewById(R.id.imageView1);
//接下来需要给name address price赋值成从服务器上取下来的数据,这就需要定义异步任务
name.setText(list.get(position).get("proname").toString());
address.setText(list.get(position).get("proaddress").toString());
price.setText(list.get(position).get("proprice").toString());
//图片待会儿再处理
return view;
}
由于在5中需要对name address price image赋值,这些值是从服务器取下来的json数据并解析后传回来的,这就需要定义异步任务AsyncTask了
4.3 定义异步任务
1.通常需要定义一个ProgressDialog并在oncreate方法中初始化
dialog=new ProgressDialog(this);
dialog.setTitle("提示");
dialog.setMessage("正在下载,请稍后......");
2.定义异步任务,实现3个方法
public class myTask extends AsyncTask<String, Void, List<Map<String,Object>>>{ @Override
protected void onPreExecute() {
// TODO Auto-generated method stub
dialog.show();
super.onPreExecute();
} @Override
protected void onPostExecute(List<Map<String, Object>> result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
adapter.setData(result);
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();
dialog.dismiss(); } @Override
protected List<Map<String, Object>> doInBackground(String... params) {
// TODO Auto-generated method stub
//list 服务器返回的最终经过解析后的json数据
List<Map<String, Object>> list=new ArrayList<Map<String,Object>>();
//连接网络 获取json数据并解析
try {
HttpClient client=new DefaultHttpClient();
//HttpGet httpGet=new HttpGet(params[0]);//不建议使用 因为大小受限制
HttpPost httpPost=new HttpPost(params[]);
HttpResponse response=client.execute(httpPost);
if(response.getStatusLine().getStatusCode()==){
String jsonString=EntityUtils.toString(response.getEntity(), "utf-8");
//以上获取数据
//以下解析数据
JSONObject jsonObject=new JSONObject(jsonString);//返回的数据key为products,Value为数组,数组中的每个元素又是jsonObject
JSONArray jsonArray=jsonObject.getJSONArray("products");
for (int i = ; i < jsonArray.length(); i++) {
JSONObject jsonObject2=jsonArray.getJSONObject(i);
Map<String,Object> map=new HashMap<String, Object>();
Iterator<String> iterator=jsonObject2.keys();
while(iterator.hasNext()){
String key=iterator.next();
Object val=jsonObject2.get(key);
map.put(key, val);
}
list.add(map);
}
} } catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return list;
} }
@Override
3.在oncreate()方法中开启任务,传递的参数是CommonURL中的地址
new myTask().execute(CommonURL.Pro_URL);
在onpostExcute()方法执行时,通过适配器的setData方法填充适配器,并为listView绑定数据源
protected void onPostExecute(List<Map<String, Object>> result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
adapter.setData(result);
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();
dialog.dismiss(); }
之后 就得回到第四步4.2的最后一小步,即更新UI
name.setText(list.get(position).get("proname").toString());
address.setText(list.get(position).get("proaddress").toString());
price.setText(list.get(position).get("proprice").toString());
----------接下来得用接口回调来加载图片---------------
需要专门定义一个用来处理图片的类DownLoadImg.java,该类包括:
一个用来传递图片地址的构造方法,一个提供得到图片方法的接口,和一个下载图片、开启子线程返回消息给handler的方法(其参数是回调接口的一个对象)
该类接收activity传过来的图片路径并方法中开启一个子线程中下载图片作为消息返回给handler处理,handler的处理方式是通过回调对象获得该图片的引用,
然后在activity中调用该方法时,需要new一个接口对象作为参数,该参数是一个匿名内部类,在该类的实现接口方法中即可获得图片
因为该类需要开启一个子线程,得到图片
接口回调:定义一个接口,该接口提供了一个得到图片的方法getDrawable,方法参数为Drawable类型
public interface ImgCallBack{
public void getDrawable(Drawable drawable);
}
最终代码:
MainActivity:
package com.example.android_08handler_product; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map; import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONObject; import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView; import com.example.android_08handler_product.DownLoadImg.ImgCallBack; public class MainActivity extends Activity { private ListView listView;
private ProgressDialog dialog;
private myAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView=(ListView)this.findViewById(R.id.listView1);
dialog=new ProgressDialog(this);
dialog.setTitle("提示");
dialog.setMessage("正在下载,请稍后......");
adapter=new myAdapter(this);
new myTask().execute(CommonURL.Pro_URL);
} public class myAdapter extends BaseAdapter{ private Context context;
private LayoutInflater layoutInflater;
private List<Map<String,Object>> list=null;//存放最终解析过的json数据
public myAdapter(){ }
public myAdapter(Context context){
this.context=context;
layoutInflater=layoutInflater.from(context);
}
public void setData(List<Map<String,Object>> list){
this.list=list;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return list.size();
} @Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return list.get(position);
} @Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View view=null;
if(convertView==null){
view=layoutInflater.inflate(R.layout.item, null);
}
else
view=convertView;
TextView name=(TextView)view.findViewById(R.id.textView1);
TextView address=(TextView)view.findViewById(R.id.textView2);
TextView price=(TextView)view.findViewById(R.id.textView3);
final ImageView imageView=(ImageView)view.findViewById(R.id.imageView1);
//接下来需要给name address price赋值成从服务器上取下来的数据,这就需要定义异步任务
name.setText(list.get(position).get("proname").toString());
address.setText(list.get(position).get("proaddress").toString());
price.setText(list.get(position).get("proprice").toString());
DownLoadImg downLoadImg=new DownLoadImg(CommonURL.Pro_ImgURL+list.get(position).get("proimage").toString());
downLoadImg.loadImg(new ImgCallBack() { @Override
public void getDrawable(Drawable drawable) {
// TODO Auto-generated method stub
imageView.setImageDrawable(drawable);
}
});
return view;
} }
public class myTask extends AsyncTask<String, Void, List<Map<String,Object>>>{ @Override
protected void onPreExecute() {
// TODO Auto-generated method stub
dialog.show();
super.onPreExecute();
} @Override
protected void onPostExecute(List<Map<String, Object>> result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
adapter.setData(result);
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();
dialog.dismiss(); } @Override
protected List<Map<String, Object>> doInBackground(String... params) {
// TODO Auto-generated method stub
//list 服务器返回的最终经过解析后的json数据
List<Map<String, Object>> list=new ArrayList<Map<String,Object>>();
//连接网络 获取json数据并解析
try {
HttpClient client=new DefaultHttpClient();
//HttpGet httpGet=new HttpGet(params[0]);//不建议使用 因为大小受限制
HttpPost httpPost=new HttpPost(params[]);
HttpResponse response=client.execute(httpPost);
if(response.getStatusLine().getStatusCode()==){
String jsonString=EntityUtils.toString(response.getEntity(), "utf-8");
//以上获取数据
//以下解析数据
JSONObject jsonObject=new JSONObject(jsonString);//返回的数据key为products,Value为数组,数组中的每个元素又是jsonObject
JSONArray jsonArray=jsonObject.getJSONArray("products");
for (int i = ; i < jsonArray.length(); i++) {
JSONObject jsonObject2=jsonArray.getJSONObject(i);
Map<String,Object> map=new HashMap<String, Object>();
Iterator<String> iterator=jsonObject2.keys();
while(iterator.hasNext()){
String key=iterator.next();
Object val=jsonObject2.get(key);
map.put(key, val);
}
list.add(map);
}
} } catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return list;
} }
@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;
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
DownLoadImg
package com.example.android_08handler_product; import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL; import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message; public class DownLoadImg { private String img_path;
public DownLoadImg(String img_path) {
// TODO Auto-generated constructor stub
this.img_path=img_path;
} public void loadImg(final ImgCallBack back){
final Handler handler=new Handler(){ @Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
back.getDrawable((Drawable)msg.obj);
}
};
new Thread(new Runnable() { @Override
public void run() {
InputStream is;
try {
is = new URL(img_path).openStream();
Drawable drawable=Drawable.createFromStream(is, "");
Message message=Message.obtain();
message.obj=drawable;
handler.sendMessage(message);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// TODO Auto-generated method stub }
}).start();
}
//接口的回调方式
public interface ImgCallBack{
public void getDrawable(Drawable drawable);
}
}
Android 之异步任务(AsyncTask,Handler,Message,looper)的更多相关文章
- Android:异步处理之Handler、Looper、MessageQueue之间的恩怨(三)
前言 如果你在阅读本文之前,你不知道Handler在Android中为何物,我建议你先看看本系列的第一篇博文<Android:异步处理之Handler+Thread的应用(一)>:我们都知 ...
- Android多线程分析之三:Handler,Looper的实现
Android多线程分析之三:Handler,Looper的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处 在前文<Android多 ...
- Android Handler处理机制 ( 三 ) ——Handler,Message,Looper,MessageQueue
在android中提供了一种异步回调机制Handler,使用它,我们可以在完成一个很长时间的任务后做出相应的通知 handler基本使用: 在主线程中,使用handler很简单,new一个Handle ...
- Android Handler处理机制 ( 二 ) ——Handler,Message,Looper,MessageQueue
Android是消息驱动的,实现消息驱动有几个要素: 消息的表示:Message 消息队列:MessageQueue 消息循环,用于循环取出消息进行处理:Looper 消息处理,消息循环从消息队列中取 ...
- Android Handler处理机制 ( 一 )(图+源码分析)——Handler,Message,Looper,MessageQueue
android的消息处理机制(图+源码分析)——Looper,Handler,Message 作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习 google大牛们的设计思想. ...
- 从Handler+Message+Looper源代码带你分析Android系统的消息处理机制
PS一句:不得不说CSDN同步做的非常烂.还得我花了近1个小时恢复这篇博客. 引言 [转载请注明出处:http://blog.csdn.net/feiduclear_up CSDN 废墟的树] 作为A ...
- Android中异步类AsyncTask的理解
这里有两种解释的方法,各有侧重点: 第一种解释: Async Task 简介:AsyncTask的特点是任务在主线程之外运行,而回调方法是在主线程中执行,这就有效地避免了使用Handler带来的麻烦 ...
- Android:异步处理之Handler+Thread的应用(一)
前言 很久很久以前就听说了,每一个android的应用程序都会分别运行在一个独立的dalvik虚拟机进程中,而在每个虚拟机在启动时会运行一个UI主线程(Main Thread),而为啥叫UI主线程而不 ...
- Android:异步处理之Handler+Thread的应用
担心原文消失,做此记录,感谢 https://www.cnblogs.com/net168/p/4075126.html 前言 很久很久以前就听说了,每一个android的应用程序都会分别运行在一个独 ...
随机推荐
- 新版TP-Link无线路由器怎么设置
TP-Link路由器的设置和无线WIFI的设置.. -------------- 一.准备工作: 1.首先是路由器的安装,将路由器电源接上,并通电,然后网线的连接.如果是拨号上网用户,请将猫引出的网线 ...
- go web 第三天 学习笔记 --mysql
CREATE TABLE `userinfo` ( `uid` INT() NOT NULL AUTO_INCREMENT, `username` VARCHAR() NULL DEFAULT NUL ...
- Catalan Number 卡特兰数
内容部分来自以下博客: Cyberspace_TechNode 邀月独斟 一个大叔 表示感谢! Catalan数的引入: 一个长度为2N的序列,里面有N个+1,N个-1 它的任意前缀和均非负,给定N, ...
- 数据结构之合并链表STL
#include <iostream> #include <list> using namespace std; int main() { int n, m; while (c ...
- 【Vue】Vue中的父子组件通讯以及使用sync同步父子组件数据
前言: 之前写过一篇文章<在不同场景下Vue组件间的数据交流>,但现在来看,其中关于“父子组件通信”的介绍仍有诸多缺漏或者不当之处, 正好这几天学习了关于用sync修饰符做父子组件数据双向 ...
- 程序员也有春天之HTTP/2.0配置
哎呀,一不小心自己的博客也是HTTP/2.0了,前段时间对网站进行了https迁移并上了CDN,最终的结果是这酱紫的(重点小绿锁,安全标示以及HTTP/2.0请求). 科普 随着互联网的快速发展,HT ...
- 处理 Vue 单页面应用 SEO 的另一种思路
vue-meta-info 官方地址: monkeyWangs/vue-meta-info (设置vue 单页面meta info信息,如果需要单页面SEO,可以和 prerender-spa-plu ...
- Linux平台 Oracle 12cR2 RAC安装Part2:GI配置
Linux平台 Oracle 12cR2 RAC安装Part2:GI配置 三.GI(Grid Infrastructure)安装 3.1 解压GI的安装包 3.2 安装配置Xmanager软件 3.3 ...
- Javascript的内容摘要
JS简介和变量 {JS的三种方式} 1 HTML中内嵌JS(不提倡使用) <button onclick="javascript:alert ...
- SQL菜鸟学习札记(二)
五月份一直在写SQL,之后写了一个期末大作业的项目,现在才有时间把之前遇到的各种奇怪的问题整理出来.下一部分札记应该是大作业中使用到的SQL的整理. 一.UPDATE SET语句后面可以并列赋值. 之 ...