ui线程与工作线程的通信有两种常用的方式
1:AsyncTask(异步任务)
特点:框架成熟,按照框架要求填写代码即可。将工作线程(耗时操作)放在doinbackground方法中,在dopostexcture方法中更新ui界面
要求,理解三个泛型和四个步骤
2:Handler方式
2-1:用handler发送和处理Message,要求设计好Message消息类
2-2:用handler发送和处理Runnable对象,用handler的post方法,在工作线程中修改UI控件,虽然handler的post方法写工作线程中,但实际是在UI线程中执行的。
例子:从网络上下载一幅图片加载到手机activity的imageview上
先上效果图
第一种方式
异步任务:
package com.hnkjwlxy.ch03_ansyctask;
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.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class MainActivityone extends Activity implements View.OnClickListener {
private Button btn_download;
private ImageView iv_img;
private String IMAGE_PATH="https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1168991478,3075700071&fm=26&gp=0.jpg";
private ProgressDialog progressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bi);
initView();
progressDialog=new ProgressDialog(this);
progressDialog.setTitle("提示信息");
progressDialog.setMessage("正在下载,请等待");
//没有下载完成前不允许取消对话框
progressDialog.setCancelable(false);
//风格为线条的形式
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
}
private void initView() {
btn_download = (Button) findViewById(R.id.btn_download);
iv_img = (ImageView) findViewById(R.id.iv_img);
btn_download.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_download:
//启动异步任务
new DownImageAsyncTask().execute(IMAGE_PATH);
break;
}
}
//创建下载图片的异步内部类
/** 泛型1:代表下载的路径
*/泛型3:代表后台子线程结束后返回位图对象
//三个参数
//String:下载图片的地址
//Integer:子线程的下载进度
//Bitmap:位图对象
class DownImageAsyncTask extends AsyncTask<String,Integer,Bitmap>{
@Override
/**
* String...
* UI线程在启动工作线程时,可以传入多个参数,
* 此次的string参数是一个数组,是字符串数组
*/
protected Bitmap doInBackground(String... strings) {
Bitmap bitmap=null;
byte[] image=new byte[]{};
byte[] data=new byte[1024];
//获取传入的下载路径,trim()去空格;
try{
String path=strings[0].trim();
URL url=new URL(path);
HttpURLConnection httpURLConnection=(HttpURLConnection)url.openConnection();
//httpURLConnection.getInputStream()获取网络输入流
//构建输入流
InputStream inputStream=httpURLConnection.getInputStream();
//字节数组输出流
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
//获得图片大小--输入流的大小
int filelength=httpURLConnection.getContentLength();
int totallength=0,length=0;//1:总共下载的大小,2:当前下载的大小
Log.i("Tag",filelength+"filelength");
while((length=inputStream.read(data))!=-1){
totallength+=length;
//计算下载进度
int progress=(int)((totallength)/(float)(filelength)*100);
//构建图片,将网络输入流内容写入data字节数组中
byteArrayOutputStream.write(data,0,length);
//将下载进度反馈给onprogressupdate方法
publishProgress(progress);
}
image=byteArrayOutputStream.toByteArray();
bitmap=BitmapFactory.decodeByteArray(image,0,image.length);
inputStream.close();
byteArrayOutputStream.close();
//利用网络输入流构建位图对象
// bitmap= BitmapFactory.decodeStream(httpURLConnection.getInputStream());
}catch (Exception e){
Log.i("tag",e.getMessage());
}
return bitmap;
}
@Override
//在doinbackground方法执行前执行的方法,通常做一些准备工作,例如将progressdialog显示出来
protected void onPreExecute() {
super.onPreExecute();
progressDialog.show();//显示进度对话框
}
//doinbackground执行完之后,该方法执行
//该方法参数即为doinbackground方法的返回
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
iv_img.setImageBitmap(bitmap);
//后台子线程执行完之后,更新UI
//销毁对话框
progressDialog.dismiss();
}
//异步框架向UI线程反馈后台线程的执行进度
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
//参数values是数组
progressDialog.setProgress(values[0]);
}
}
}
第二种方式
Handler
1:发送和处理Message
2:发送和处理runnable对象
工作线程利用handler发送message/runnable对象给消息队列,主线程用looper(消息循环器)从消息队列中取出队首消息进行处理
Message:线程之间通信的信息载体
如果线程之间的信息载体比较复杂,建议使用setData和getData传送Bundler对象
如果线程之间的信息载体比较简单,建议直接使用Message类中的arg1,arg2,obj,what. 设计Message消息类需要思考的:
工作线程有两个信息要传递给ui线程 下载进度,下载完成的位图
what表示哪一种消息
下载进度 what:1; 通过arg1传递下载进度
下载完成的位图的字节数组 what:2;setData/getData传递位图
package com.hnkjwlxy.message;
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.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class MainActivity extends Activity implements View.OnClickListener {
private Button btn_bb;
private ImageView img_img;
private ProgressDialog pd;
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
//把下载进度进行更新
case 1:
pd.show();
pd.setProgress(msg.arg1);//更新进度,进度放在message的arg1属性里面的
break;
case 2:
//下载完成,把位图呈现在imageview控件上
pd.dismiss();//销毁进度对话框
//bundle对象可以传递字节数组
Bundle bundle=msg.getData();//从消息中过去数据
byte[] image=bundle.getByteArray("myimage");//从bundle获取key为myimage的字节数组
//从子数组中构建位图
Bitmap bitmap= BitmapFactory.decodeByteArray(image,0,image.length);
img_img.setImageBitmap(bitmap);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
btn_bb = (Button) findViewById(R.id.btn_bb);
img_img = (ImageView) findViewById(R.id.img_img);
pd=new ProgressDialog(this);
pd.setTitle("提示信息");
pd.setMessage("正在下载,请稍后");
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
btn_bb.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_bb:
new DownImageTread().start();//启动工作线程
break;
}
}
//工作线程
class DownImageTread extends Thread{
@Override
public void run() {
super.run();
String path="https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1168991478,3075700071&fm=26&gp=0.jpg";
try{
URL url=new URL(path);
HttpURLConnection connection=(HttpURLConnection)url.openConnection();
InputStream inputStream=connection.getInputStream();
//字节数组输出流,在内存中创建一个字节数组的缓存区,所有发送到输出流的数据,都保存在该缓存区
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
byte[] data=new byte[1024];//字节数组
byte[] image=new byte[]{};//字节数组对象,{}相当于构造函数
//文件大小是通过网络连接对象
int filesize=connection.getContentLength();
Log.i("filesize",filesize+"");
int length=0,totallength=0;
while((length=inputStream.read(data))!=-1){
totallength+=length;
int prograss=(int)(totallength/(float)filesize)*100;
//将字节数组data中的数组放入字节数组缓存区
byteArrayOutputStream.write(data,0,length);
//Message对象不能new,只能获得
// 两种方法获得:
// 方式1:Message.obtain();
Message message=Message.obtain();
message.what=1;
message.arg1=prograss;
handler.sendMessage(message);
Thread.sleep(100);
}
image=byteArrayOutputStream.toByteArray();
//将字节数组缓存区中的内容构建为字节数组
Message message=Message.obtain();
message.what=2;
Bundle bundle=new Bundle();
bundle.putByteArray("myimage",image);
message.setData(bundle);
handler.sendMessage(message);
//关闭流
inputStream.close();
byteArrayOutputStream.close();
}catch (Exception ex){
Log.i("tag",ex.getMessage());
}
}
}
}
利用runnable对象传递
package com.hnkjwlxy.message;
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.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class MainActivity1 extends Activity implements View.OnClickListener {
private Button btn_bb;
private ImageView img_img;
private ProgressDialog pd;
private Handler handler=new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
btn_bb = (Button) findViewById(R.id.btn_bb);
img_img = (ImageView) findViewById(R.id.img_img);
pd=new ProgressDialog(this);
pd.setTitle("提示信息");
pd.setMessage("正在下载,请稍后");
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
btn_bb.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_bb:
new DownImageTread().start();//启动工作线程
break;
}
}
//工作线程
class DownImageTread extends Thread{
@Override
public void run() {
super.run();
String path="https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1168991478,3075700071&fm=26&gp=0.jpg";
try{
URL url=new URL(path);
HttpURLConnection connection=(HttpURLConnection)url.openConnection();
InputStream inputStream=connection.getInputStream();
//字节数组输出流,在内存中创建一个字节数组的缓存区,所有发送到输出流的数据,都保存在该缓存区
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
final byte[] data=new byte[1024];//字节数组
//文件大小是通过网络连接对象
int filesize=connection.getContentLength();
Log.i("filesize",filesize+"");
int length=0,totallength=0;
while((length=inputStream.read(data))!=-1){
totallength+=length;
final int prograss=(int)(totallength/(float)filesize)*100;
//将字节数组data中的数组放入字节数组缓存区
byteArrayOutputStream.write(data,0,length);
//
handler.post(new Runnable() {
@Override
public void run() {
pd.show();
pd.setProgress(prograss);
}
});
//Message对象不能new,只能获得
// 两种方法获得:
// 方式1:Message.obtain();
//方式2:handler.obtainMessage()
Thread.sleep(100);
}
final byte[] image=byteArrayOutputStream.toByteArray();
//将字节数组缓存区中的内容构建为字节数组
handler.post(new Runnable() {
@Override
public void run() {
pd.dismiss();
Bitmap bitmap=BitmapFactory.decodeByteArray(image,0,image.length);
img_img.setImageBitmap(bitmap);
}
});
inputStream.close();
byteArrayOutputStream.close();
}catch (Exception ex){
Log.i("tag",ex.getMessage());
}
}
}
}
ANR对话框 :Application Not Response 规定如果5秒没有响应,会出现ANR对话框,会强制要求退出APP
1、耗时操作不能放在线程里(网络下载,数据库操作,文件读写)
2、非ui线程不能操作UI控件(进度条,进度对话框,图片加载)