- 测试Handler的使用
- Hander使用的小DEMO
- 测试AsyncTask的使用
一,.测试Handler的使用
1.相关API
Message :消息
可理解为线程间通讯的数据单元, 可通过message携带需要的数据Handler : 处理器
创建对象: Message.obtain(what)
封装数据
public int what //id 标识
public int arg1
public int arg2
public Object obj
Handler是Message的处理器,同时也负责消息的发送和移除的工作MessageQueue : 消息队列
发送即时消息: sendMessage(Message msg)
发送延时消息: sendMessageDelayed(Message msg, long time)
处理消息: handleMessage(Message msg) (回调方法)
移除还未处理的消息: removeMessages(int what)
Looper(钩子) : 循环器用来存放通过Handler发送的消息
它是一个按Message的when排序的优先级队列
负责循环取出Message Queue里面的当前需要处理的Message
交给对应的Handler进行处理
处理完后, 将Message缓存到消息池中, 以备复用
Message
public int what;//标识(id)
public int arg1;//保存int数据
public int arg2;//保存int数据
public Object obj;//保存任意数据
long when;//记录应该被处理的时间值
Handler target;//用来处理消息的Handler对象,就是发送消息的handler
Runnable callback;//用来处理消息的回调器(一般不用)
Message next;//指向下一个message用来形成一个链表
private static Message sPool;//用来缓存处理过的数据
Message obtain()//它利用了Message中消息池(sPool)
Handler :发送消息,处理消息,移除消息
sendMessage(Message msg)
sendMessageDelayed(msg,0);
sendEmptyMessage(int what)
sendMessageDelayed(what,0);
sendEmptyMessage(what , 0)//发送不带数据的消息
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg,delayMillis);
sendMessageDelayed(Message,msg,long delayMillis)
sendMessageAtTime(msg,SystemClock.uptimeMillis() + delayMillis);
sendMessageAtTime(Message msg,long uptimeMillis)
enqueueMessage(queue,msg,uptimeMillis);//将消息添加到消息队列
enqueueMessage(MessageQueue queue,Message msg,long uptimeMillis);
msg.target = this;//保存发送消息的Handler对象
queue.enqueueMessage(msg,uptimeMillis);//调用消息队列保存消息对象
removeMessages(int what) //移除消息
mQueue.removeMessages(this, what, null);//调用消息队列移除它内部的指定what的消息
handleMessage(Message msg) //处理消息的回调方法
public void dispatchMessage(Message msg) {
if(msg.callback!=null){//如果消息自己可以处理,让消息自己处理
handleCallback(msg);
}else{
if(mCallback != null){//如果handler对象中有回调监听器,调用回调器来处理消息
if(mCallback.handleMessage(msg)){
return;
}
}
handleMessage(msg);//让Handler的handleMessage()来处理
}
}
MessageQueue :存储消息的以message的when排序优先级队列
enqueueMessage(Message msg,long when):将message添加到队列中
msg.when = when;//指定消息应该被处理的时间
for(;;){//将当前消息对象保存到消息队列中的一个合适的位置
prev = p;
p = p.next;
if(p==null || when < p.when){
break;
}
if(needWake && p.isAsynchronous()){
needWake = false;
}
}//最终的结果是:消息队列是按when来排序的
nativeWake(mPtr);//通过本地方法实现对处理等待状态的底层线程
next()取出一个合适的Message对象,可能不会立即返回
nativePollOnce(mPtr,nextPollTimeoutMillis);//本地方法,会导致可能处理等待状态,但不会阻塞主线程
Message msg = mMssages;//取出消息队列中的第一个消息
return msg;//返回
Loop:从MessageQueuek中获取当前需要处理的消息,并交给Handler处理
loop():核心方法
final Looper me = myLooper();//得到looper对象
final MessageQueue queue = me.mQueue;//得到消息队列对象
for(;;){//无限循环
Message msg = queue.next();//从消息队列中取出消息
msg.target.dispatchMessage(msg);//调用Handler去分发并处理消息
msg.recycle();//回收利用Message
}
Runnable对象的run()什么时候在分线程执行?
将Runable传给Thread的构造方法:
new Thread(new Runnable(){
public void run(){//在分线程执行
}
}).start();
2.功能实现:
- 点击GET请求获取: 显示提示正在加载的进度条, 分线程请求网络
- 得到数据后, 将数据显示到输入框中, 同时隐藏进度条
3.代码实现
package com.example.atguigu_05;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
/**
* 测试Handler的基本使用
*
* @author Xiaocici
* 1.创建Handler成员变量对象,并重写其handleMessage()
* 2.在分/主线程创建Message对象
* 3.使用handler对象发送Message
* 4.在handleMessage()中处理消息
*/
public class HandlerTestActivity extends Activity {
private ProgressBar pb_handler1_loading;
private EditText et_handler1_result;
// 1.创建Handler成员变量对象,并重写其handleMessage()
private Handler handler = new Handler() {// 在主线程执行
public void handleMessage(android.os.Message msg) {
if (msg.what == 1) {
// 4.在handleMessage()中处理消息
String result = msg.toString();
et_handler1_result.setText(result);
pb_handler1_loading.setVisibility(View.INVISIBLE);
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
pb_handler1_loading = (ProgressBar) findViewById(R.id.pb_handler1_loading);
et_handler1_result = (EditText) findViewById(R.id.et_handler1_result);
}
/*
* 1.主线程,显示提示视图(ProgressDialog/ProgressBar) 2.分线程,联网请求,并得到响应数据
* 3.主线程,显示数据/隐藏提示视图
*/
public void getSubmit1(View v) {
// 1.主线程,显示提示视图(ProgressDialog/ProgressBar)
pb_handler1_loading.setVisibility(View.VISIBLE);
// 2.分线程,联网请求,并得到响应数据
new Thread() {
public void run() {
String path = "http://192.168.51.65:8080/web/?name=Tom&age=12";
try {
final String result = requestToString(path);
// 3.主线程,显示数据
runOnUiThread(new Runnable() {
public void run() {
et_handler1_result.setText(result);
pb_handler1_loading.setVisibility(View.INVISIBLE);
}
});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
}.start();
}
public void getSubmit2(View v) {
// 1).主线程,显示提示视图(ProgressDialog/ProgressBar)
pb_handler1_loading.setVisibility(View.VISIBLE);
// 2).分线程,联网请求,并得到响应数据
new Thread() {
public void run() {
String path = "http://192.168.51.65:8080/web/?name=Tom&age=12";
try {
final String result = requestToString(path);
// 3).主线程,显示数据
// 2.在分/主线程创建Message对象
Message message = Message.obtain();
message.what = 1;
message.obj = result;
// 3.使用handler对象发送Message
handler.sendMessage(message);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
}.start();
}
/**
* 请求服务器端,得到返回的结果字符串
*
* @param path
* :
* @return
* @throws Exception
*/
public String requestToString(String path) throws Exception {
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.connect();
InputStream is = connection.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = -1;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
baos.close();
is.close();
String result = baos.toString();
return result;
}
}
二.Hander使用的小DEMO
1.功能描述
1). 初始时
显示10,可以通过点击按钮改变其值
2). 点击“自动增加”
每隔1S上面的文本数值增加1,但最大显示20并作出提示
3). 点击“自动减少”
每隔1S上面的文本数值减少1,但最小显示1并作出提示
4). 点击“暂停”
上面的数值文本不再变化
2.代码实现:
package com.example.atguigu_05;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
/**
* 使用Handler的小DEMO
*
* @author Xiaocici
* 1.手动增加/减少
* 2.自动增加/减少
* 3.限制数字的最大和最小值【1,20】
* 4.限制Button的可操作性
*/
public class HandlerDemoActivity extends Activity implements OnClickListener {
private static final int WHAT_INCREASE = 1;
private static final int WHAT_DECREASE = 2;
private TextView tv_demo_number;
private Button btn_demo_increase;
private Button btn_demo_decrease;
private Button btn_demo_pause;
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
//得到当前显示的数值
int number = Integer.parseInt(tv_demo_number.getText().toString());
switch (msg.what) {
case WHAT_INCREASE:
//限制number<=20
if(number==20){
Toast.makeText(HandlerDemoActivity.this, "已经达到最大值", 0).show();
btn_demo_pause.setEnabled(false);
return;
}
number++;
tv_demo_number.setText(number+"");
//发送增加的延迟消息
handler.sendEmptyMessageDelayed(WHAT_INCREASE, 1000);
break;
case WHAT_DECREASE:
//限制number>=1
if(number==1){
Toast.makeText(HandlerDemoActivity.this, "已经达到最小值", 0).show();
btn_demo_pause.setEnabled(false);
return;
}
number--;
tv_demo_number.setText(number+"");
//发送减少的延迟消息
handler.sendEmptyMessageDelayed(WHAT_DECREASE, 1000);
break;
default:
break;
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_demo);
init();
}
public void init() {
tv_demo_number = (TextView) findViewById(R.id.tv_demo_number);
btn_demo_increase = (Button) findViewById(R.id.btn_demo_increase);
btn_demo_decrease = (Button) findViewById(R.id.btn_demo_decrease);
btn_demo_pause = (Button) findViewById(R.id.btn_demo_pause);
btn_demo_decrease.setOnClickListener(this);
btn_demo_increase.setOnClickListener(this);
btn_demo_pause.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v == btn_demo_increase) {// 自动增加 what=1
//限制button可操作性
btn_demo_decrease.setEnabled(true);
btn_demo_increase.setEnabled(false);
btn_demo_pause.setEnabled(true);
//停止减少(移除未处理的减少的消息)
handler.removeMessages(WHAT_DECREASE);
// 发消息
handler.sendEmptyMessage(WHAT_INCREASE);
} else if (v == btn_demo_decrease) {// 自动减少 what=2
//限制button可操作性
btn_demo_decrease.setEnabled(false);
btn_demo_increase.setEnabled(true);
btn_demo_pause.setEnabled(true);
//停止增加(移除未处理的增加的消息)
handler.removeMessages(WHAT_INCREASE);
// 发消息
handler.sendEmptyMessage(WHAT_DECREASE);
} else if (v == btn_demo_pause) {// 暂停
//限制button可操作性
btn_demo_decrease.setEnabled(true);
btn_demo_increase.setEnabled(true);
btn_demo_pause.setEnabled(false);
//停止增加/减少(移除未处理的减少/增加的消息)
handler.removeMessages(WHAT_DECREASE);
handler.removeMessages(WHAT_INCREASE);
}
}
}
三.测试AsyncTask的使用
1.AsyncTask的理解
在没有AsyncTask之前, 我们用Handler+Thread就可以实现异步任务的功能需求
AsyncTask是对Handler和Thread的封装, 使用它更编码更简洁,更高效
AsyncTask封装了ThreadPool, 比直接使用Thread效率要高
2.相关API
AsyncTask: 简化Handler处理多线程通信的问题
AsyncTask<Params, Progress, Result>
Params 启动任务执行的输入参数,比如HTTP请求的URL。
Progress 后台任务执行的百分比。
Result 后台执行任务最终返回的结果,比如String。
execute(Params... params)
启动任务, 开始任务的执行流程
void onPreExecute()
在分线程工作开始之前在UIThread中执行,一般用来显示提示视图
Result doInBackground(Params... params)
在workerThread中执行, 完成任务的主要工作,通常需要较长的时间
void onPostExecute(Result result)
在doInBackground()执行完后在UIThread中执行,一般用来更新界面
publishProgress(Progress... values) : 在分线程中, 发布当前进度
void onProgressUpdate(Progress... values) : 在主线程中更新进度
3.过程分析图
4.代码实现
package com.example.atguigu_05;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.View;
public class AsyncTastTestActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async_tast_test);
}
private File apkFile;
private ProgressDialog dialog;
public void downloadApk(View v){
//启动异步任务处理
new AsyncTask<Void, Integer, Void>() {
//1.主线程,显示提示视图
protected void onPreExecute() {
dialog = new ProgressDialog(AsyncTastTestActivity.this);
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.show();
//准备用于保存APK文件的File对象:/storage/sdcard/Android/package_name/files/xxx.apk
apkFile = new File(getExternalFilesDir(null),"update.apk");
};
//2.分线程,联网请求
@Override
protected Void doInBackground(Void... params) {
try {
// 1).得到连接对象
String path = "http://192.168.51.65:8080/web/ActivityDemo.apk";
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 2).设置
//connection.setRequestMethod("GET");默认
connection.setConnectTimeout(5000);
connection.setReadTimeout(1000);
// 3).连接
connection.connect();
// 4).请求并得到响应码200
int responseCode = connection.getResponseCode();
if(responseCode==200){
//设置dialog的最大进度
dialog.setMax(connection.getContentLength());
// 5).得到包含APK文件数据的InputStream
InputStream is = connection.getInputStream();
// 6).创建指向apkFile的FileOutputStream
FileOutputStream fos = new FileOutputStream(apkFile);
// 7).边读边写
byte[] buffer = new byte[1024];
int len = -1;
while((len=is.read(buffer))!=-1){
fos.write(buffer,0,len);
// 8).显示下载进度
//dialog.incrementProgressBy(len);
//在分线程中发布当前进度
publishProgress(len);
//休息一会(模拟网速慢 时)
//Thread.sleep(50);
SystemClock.sleep(50);
}
fos.close();
is.close();
}
// 9).下载完成,关闭,进入3.
connection.disconnect();
}catch(Exception e){
e.printStackTrace();
}
return null;
}
//3.主线程,更新界面(在publishProgress()后执行)
protected void onPostExecute(Void result) {
dialog.dismiss();
installAPK();
};
//在主线程中更新进度
protected void onProgressUpdate(Integer[] values) {
dialog.incrementProgressBy(values[0]);
};
}.execute();
}
/**
* 启动安装APK
*/
private void installAPK(){
Intent intent = new Intent("android.intent.action.INSTALL_PACKAGE");
intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
startActivity(intent);
}
}