Handler的学习
最简单的handleMode
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button);
text = (TextView) findViewById(R.id.tv_tv);
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what == 1001){
//消息发送过来了 更改UI
text.setText("在handler里面更改成功");
}
}
};
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
//用handler发送一个空消息,携带一个what参数
handler.sendEmptyMessage(1001);
}
}).start();
}
});
}
Handler常见发送消息方法
Message.obtain();//这里可以获取到一个Message对象而不用new出来,因为在下层它有个缓存池,如果没有创建,那么会自动创建一个Message对象回来。
handler.sendEmptyMessage(1001);
Message message = Message.obtain();//这里可以获取到一个Message对象
message.what = 1000;
message.arg1 = 1001;
message.arg2 = 1002; //arg 存储int值
message.obj = MainActivity.this;//存储一个对象
handler.sendMessageAtTime(message, SystemClock.uptimeMillis() + 3000);//延迟系统时间+3秒发送消息
handler.sendMessageDelayed(message, 3000);//延迟绝对时间 3秒发送消息
//handler.post(new Runnable(run))
小程序,下载文件,界面的progressbar用Handler来发送消息实时更新进度
package com.example.synnection.handlermode;
import android.nfc.Tag;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class DwonLoadDemo extends AppCompatActivity {
private static final String APP_URL = "http://download.sj.qq.com/upload/connAssitantDownload/upload/MobileAssistant_1.apk";;
private static final String TAG ="DownLoadDemoActivity";
private ProgressBar progressBar ;
/*
异步下载文件更新进度条的步骤
主线程
点击按键
发起下载
开启子线程下载
下载完成后通知主线程
主线程更新进度条
*/
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what ==100){
int currentPro = msg.arg1;
progressBar.setProgress(currentPro);
Log.i(TAG,"更新进度条ing");
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_download);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 下载文件
downLoad(APP_URL);
}
});
}
private void downLoad(final String appUrl) {
//创建下载路径
// Environment.getExternalStorageDirectory() 获取扩展存储设备的资源路径
String downLoadFolderName = Environment.getExternalStorageDirectory() + File.separator + "123" + File.separator;
//创建文件夹
File file = new File(downLoadFolderName);
//判断文件夹是否存在,不存在则创建
if(!file.exists()){
file.mkdir();
}
//创建文件名
final File appFile = new File(downLoadFolderName+"hello.apk");
//判断文件是否存在,如存在则删除
if(appFile.exists()){
appFile.delete();
Log.i(TAG,"删除成功");
}
if(appFile!=null){
Log.i(TAG,"文件创建成功");
}
/**
* 在子线程中下载文件
*/
new Thread(new Runnable(){
@Override
public void run() {
try {
URL url = new URL(appUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
int fileMaxLength = conn.getContentLength();//获取文件的总长度
InputStream is = conn.getInputStream();//获取输入流
byte[] b = new byte[1024*1024 ];//每次读取大小
int len = 0;//当前的长度
int currentDwonLoadLen = 0;
OutputStream os = new FileOutputStream(appFile);//写入appFile文件中
while ((len =is.read(b)) != -1){
//每次写入b,从下标0,写到len
os.write(b,0,len);
//当前下载的大小
currentDwonLoadLen += len;
//当前进度条的长度
int currentprogress = currentDwonLoadLen*100 /fileMaxLength;
Log.i(TAG,"currentprogress="+currentprogress);
//获取Message对象 发送消息通知UI更新进度条
Message message = handler.obtainMessage();
message.what = 100;
message.arg1 = currentprogress;
handler.sendMessage(message);
//关闭流
os.close();
is.close();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
做完出现过几个问题。一个读写 网络 权限没加
一个anr 一点击按钮卡死。
因为把 Message message = handler.obtainMessage();放在了子线程外面也就是在while循环外面
每次发送消息都用的同一个message 导致程序anr卡死
讲道理,放while循环里面每次调用的也是同一个message对象,因为下层是用了一个缓存池来保存的,那为什么放外面会anr呢?
暂时没理解
小程序用Handler实现倒计时
public class CountDownTime extends AppCompatActivity {
//倒计时间隔
public static final int COUNT_OUT = 2000;
//倒计时的标记
public static final int COUNT_OUT_FLAG = 1001;
//倒计时初始值
public static final int COUNT_OUT_MAX = 10;
/**
* 使用Handler倒计时
* 创建Handler
* 发送消息,改变textview值
*/
private TextView mCountDownTextView;
//发送一个延迟消息让handler处理更新Ui//有可能会内存泄漏
// Handler handler = new Handler(){
// @Override
// public void handleMessage(Message msg) {
// super.handleMessage(msg);
//mCountDownTextView.setText(String.valueOf(COUNT_OUT_MAX //-1));
// }
// };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_countdown);
mCountDownTextView = (TextView) findViewById(R.id.tv_countdwon);
//获取一个消息实例
Message message = handler.obtainMessage();
message.what = COUNT_OUT_FLAG;
message.arg1 = COUNT_OUT;
//发送消息到handlerMessage里处理
handler.sendMessageDelayed(message,2000);
}
}
问题:上面的Handler有可能会造成内存泄漏,因为这里的Handler持有Activity的强引用,有可能此Activity被销毁了,这里的Handler还是持有引用。
解决:
让这里的Handler实例变为static静态的,然后再让Handler持有该Activity的弱引用,在该Activity销毁时,该Handler也一同销毁。
public class CountDownTime extends AppCompatActivity {
//倒计时间隔
public static final int COUNT_OUT = 2000;
//倒计时的标记
public static final int COUNT_OUT_FLAG = 1001;
//倒计时初始值
public static final int COUNT_OUT_MAX = 10;
//记录当前倒数最小值
public static int countMin = COUNT_OUT_MAX;
private MyWeakHandler handler;
/**
* 使用Handler倒计时
* 创建Handler
* 发送消息,改变textview值
*/
private TextView mCountDownTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_countdown);
mCountDownTextView = (TextView) findViewById(R.id.tv_countdwon);
//获取一个消息实例
handler = new MyWeakHandler(CountDownTime.this);
Message message = handler.obtainMessage();
message.what = COUNT_OUT_FLAG;
message.arg1 = COUNT_OUT_MAX;
//发送消息到handlerMessage里处理
handler.sendMessage(message);
}
/**
* 自定义一个类 继承Handler 实现Handler的弱引用
*/
private static class MyWeakHandler extends Handler {
CountDownTime mActivity;
MyWeakHandler(CountDownTime activity) {
//创建弱引用的实例
WeakReference weakReference = new WeakReference<>(activity);
//获取当前弱引用对应的上下文
mActivity = (CountDownTime) weakReference.get();
}
//重写handlerMessage方法 实现倒计时
@Override
public void handleMessage(Message msg) {
//处理发送过来的消息,
if (msg.what == COUNT_OUT_FLAG) {
//拿到外部类控件 更改值
mActivity.mCountDownTextView.setText(String.valueOf(countMin));
//循环倒计时数字为0,否则再次发送延迟消息更新UI
if (countMin > 0) {
Message message = mActivity.handler.obtainMessage();
message.what = COUNT_OUT_FLAG;
countMin = countMin - 1;
mActivity.handler.sendMessageDelayed(message, COUNT_OUT);
}
}
}
}
用Handler来实现打地鼠的小程序
public class Diglett extends AppCompatActivity {
public static final String TAG = "diglett";
public static final int RANDOM_TOME = 1000;
public static final int Flag = 1009;
public int mRandomTime =2000;
public int mRandomPosition =0;
public int mClickNumber = 0;
public static final int MAX_COUNT= 10;
private DigletttHandler mHandler;
/**
* 打地鼠游戏:
* 创建一个二位数组代表随即的地鼠xy位置
* 点击地鼠图片后隐藏图片,发送延迟消息 更换图片的位置 显示图片 同时记录打到地鼠的最大数目
*/
private TextView mTextView;
private Button mStartGame;
private ImageView mImageView;
//代表地鼠将要出现的位置 一维x轴坐标 二维y轴坐标
private int[][] mPositions = new int[][] {
{123,323},{142,245},
{323,123},{52,382},
{323,155},{32,85},
{223,223},{242,45},
{379,146},{442,55}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_diglett);
initView();
initData();
initListener();
}
private void initView() {
mTextView = (TextView) findViewById(R.id.text_view);
mStartGame = (Button) findViewById(R.id.start_game);
mImageView = (ImageView) findViewById(R.id.imageView);
}
private void initData() {
mHandler = new DigletttHandler(Diglett.this);
Log.i(TAG,"mposition = "+mPositions.length);
}
private void initListener(){
mStartGame.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG,"开始游戏111");
//开始打地鼠
start(mRandomTime);
mStartGame.setVisibility(View.GONE);
mTextView.setVisibility(View.VISIBLE);
Log.i(TAG,"开始游戏222");
}
});
mImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//只有点击过图片才自增
mClickNumber++;
mImageView.setX(mPositions[mRandomPosition][0]);
mImageView.setY(mPositions[mRandomPosition][1]);
mImageView.setVisibility(View.VISIBLE);
//同时更改TextView显示打了多少个地鼠
mTextView.setText("打了"+mClickNumber+"只地鼠");
next(mRandomTime);
}
});
}
/**
* 开始打地鼠,显示地鼠 给个随即地址 把Button隐藏
*/
private void start(int delayed) {
mImageView.setX(mPositions[0][mRandomPosition]);
mImageView.setY(mPositions[1][mRandomPosition]);
mImageView.setVisibility(View.VISIBLE);
//发送延迟消息
Message message = mHandler.obtainMessage();
mHandler.sendMessageDelayed(message,delayed);
}
Random random ;
private void next(int delayed) {
if(random ==null){
random = new Random();
}
mRandomTime = random.nextInt(RANDOM_TOME)+ RANDOM_TOME;
//显示地鼠,随即生成一个位置,判断是否打到最大数目 否则一直循环发送延迟消息
if(mClickNumber<MAX_COUNT){
//发送消息
Message message = mHandler.obtainMessage();
message.what = Flag;
message.arg1 = random.nextInt(10);
mHandler.sendMessageDelayed(message,delayed);
}else{
//重新开始游戏按钮
mStartGame.setVisibility(View.VISIBLE);
//重置当前打过多少地鼠
mClickNumber = 0;
mImageView.setVisibility(View.GONE);
mTextView.setText("很棒,打完拉,点击重新开始游戏");
return;
}
}
static class DigletttHandler extends Handler{
WeakReference weakReference ;
public DigletttHandler(Diglett activity) {
weakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//获取当前的上下文
Diglett mActivity = (Diglett) weakReference.get();
//处理开始游戏的消息, 未点击地鼠的话,同样执行next()。不记录当前点击的数量
Log.i(TAG,"clickNumber = "+mActivity.mClickNumber);
if(msg.what == Flag){
int position =msg.arg1 ;
mActivity.mImageView.setX(mActivity.mPositions[position][0]);
mActivity.mImageView.setY(mActivity.mPositions[position][1])
;mActivity.next(mActivity.mRandomTime);
}
}
}
}
还有几个问题没解决,地鼠打完消息还在发送,打地鼠的时候,越打速度越快,等几天修改。