/*
Handler:
每一个 Handler 实例与一个线程关联,每一个线程又会维护一个自己的 MessageQueue
,当我们创建一个 Handler 的时候我们需要制定一个 Looper 对象(Looper对象对应一个线程)
,这样就将一个 Handler 对象和一个线程绑定到了一起,随后就可以编写我们的耗时操作
,然后通过 Handler 将消息塞入线程的 MessageQueue中,当对应线程从 MessageQueue 取出该条消息的时候
,就会回调 Handler 的 handleMessage方法并拿到消息,这样就完成了跨线程通信。
void handleMessage(Message msg):在该方法中处理其他线程传递过来的消息(用的非常多)
sendEmptyMessage(int what):发送一条空消息,what 可以理解为消息 ID , int类型值
sendEmptyMessageDelayed(int what,long delayMillis):延时发送空消息,what 为自定义 ID
sendMessage(Message msg):发送消息,msg 是打包的消息内容,what只是其中一个属性值
sendMessageDelayed(Message msg):延时发送,msg是消息内容
hasMessage(int what):检查 MessageQueue 中是否包含一条 ID 为 what 的消息。what 是用户自定义的整形数,可作为消息 ID
在主线程创建Handler,触发点击事件,有可能做大量耗时操作就创建子线程new Thread(new Runnable() { ,
获取设置的消息Message message = Message.obtain(); (这里不建议直接new Message,Message内部保存了一个缓存的消息池
,我们可以用obtain从缓存池获得一个消息,Message使用完后系统会调用recycle回收,如果自己new很多Message
,每次使用完后系统放入缓存池,会占用很多内存的。)
,打包发送给Handler (在子线程里还可以添加定时和handler.post的相关操作)
,在handleMessage(Message msg)中处理其他线程传递过来的消息,通过what找目标,实现改变主线程UI的跨线程操作。
*/
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
//UI线程,做轻量级的事情
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//主线程UI的对象
final TextView textView = (TextView) findViewById(R.id.tv);
//创建Handler
final Handler handler = new Handler() {
@Override//在该方法中处理其他线程传递过来的消息
public void handleMessage(Message msg) {
super.handleMessage(msg);
//主线程接到子线程发出来的消息,处理消息
Log.d(TAG, "handleMessage:" + msg.what);
//通过what找目标
if (msg.what == 1002) {
//改变主线程UI
textView.setText("imooc");
Log.d(TAG, "handleMessage:" + msg.arg1);//输出其中arg1的值
}
}
};
//触发点击事件
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//有可能做大量耗时操作 子线程
new Thread(new Runnable() {
@Override
public void run() {
try {
//设置延迟时间
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//通知UI更新,what值为消息id
// handler.sendEmptyMessage(1001); 空消息,只是发了一个地址
//获取设置的消息
Message message = Message.obtain();
message.what = 1002;
message.arg1 = 1003;//arg1 和 arg2 是使用的低成本替代方案,如果只需要存储几个整数值。
message.arg2 = 1004;//伴随what输入的数,int类型
message.obj = MainActivity.this;
//发送消息,msg 是消息内容
//打包发送给handleMessage(Message msg),由它进行解包出what找到目标
handler.sendMessage(message);
//定时任务
handler.sendMessageAtTime(message,
SystemClock.uptimeMillis() + 3000);//当前时间
handler.sendMessageDelayed(message, 2000);
//handler.post的用法:
//可运行的
final Runnable runnable = new Runnable() {
@Override
public void run() {
int a = 1 + 2 + 3;
}
};
//发送可运行后运行
handler.post(runnable);//直接把这个方法发出去
runnable.run();
//延迟发送
handler.postDelayed(runnable, 2000);
}
}).start();
}
});
}
}
/*
异步下载文件并更新进度条:
* 主线程,在主线程创建Handler -->start* 点击按钮 * 发起下载 * 开启子线程做下载 * 下载完成后通知主线程 -->主线程更新进度条
在manifest设置权限:<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
*/
public class DownloadActivity extends Activity {
private Handler mHandler;
//提取what
public static final int DOWNLOAD_MESSAGE_CODE = 100001;
public static final int DOWNLOAD_MESSAGE_FAIL_CODE = 100002;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_download);
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//点击按钮之后开启一个子线程做下载
new Thread(new Runnable() {
@Override
public void run() {
//下载方法自定义,输入参数APPURL
download("http://download.sj.qq.com/upload/connAssitantDownload/upload/MobileAssistant_1.apk");
}
}).start();
}
});
//创建Handler
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case DOWNLOAD_MESSAGE_CODE:
if (msg.obj != null)//信息中的obj不为空,则传入进度值
progressBar.setProgress((Integer) msg.obj);//将百分号去掉
break;
case DOWNLOAD_MESSAGE_FAIL_CODE://下载失败
}
}
};
}
//下载并更新进度条
private void download(String appUrl) {
try {
URL url = new URL(appUrl);
//用参数appurl开启URL连接
URLConnection urlConnection = url.openConnection();
//获取输入流
InputStream inputStream = urlConnection.getInputStream();
//获取文件的总长度,便于下面算出进度
int contentLength = urlConnection.getContentLength();
//获取存储地址 Studio提示Environment.getExternalStorageDirectory()过时了
//放在imooc地址下面 ,要用Context#getExternalFilesDir代替
String downloadFolderName = Environment.getExternalStorageDirectory()
+ File.separator + "imooc" + File.separator;//文件分隔符
//在存储地址 文件创建 File类
File file = new File(downloadFolderName);//前缀长度和路径path
if (!file.exists()) {
file.mkdir();//如果不存在就创建
}
//安装包文件创建
//文件名字
String fileName = downloadFolderName + "imooc.apk";
//给apk文件它的前缀长度和路径path
File apkFile = new File(fileName);
if (apkFile.exists()) {
apkFile.delete();//存在就删了
}
//下载进度条
int downloadSize = 0;
byte[] bytes = new byte[1024];
int length = 0;
//输出流=apk文件输出流
//实时输出进度值
OutputStream outputStream = new FileOutputStream(fileName);
//输入流的比特值长度,存在有效字节则写入输出流
while ((length = inputStream.read(bytes)) != -1) {
//写入输出流
outputStream.write(bytes, 0, length);
//下载大小为
downloadSize += length;
//打包信息发送给Handler ,更新UI线程进度条
Message message = Message.obtain();
message.obj = downloadSize * 100 / contentLength;
message.what = DOWNLOAD_MESSAGE_CODE;
mHandler.sendMessage(message);
}
//关闭输入输出流
inputStream.close();
outputStream.close();
} catch (MalformedURLException e) {
notifyDownloadFaild();//通知下载失败
e.printStackTrace();
} catch (IOException e) {
notifyDownloadFaild();//通知下载失败
e.printStackTrace();
}
}
//通知下载失败,依然要把msg传给Handler
private void notifyDownloadFaild() {
Message message = Message.obtain();
message.what = DOWNLOAD_MESSAGE_FAIL_CODE;
mHandler.sendMessage(message);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:max="100"
android:progress="100" />
</LinearLayout>
/*
倒计时:避免handler的内存泄露 创建静态内部类,设置倒计时间隔、最大值、最小值
第一个message 传过去最大值,添加时间间隔 直接运行 接收msg,循环发送消息控制
前面两个都需要点击button跳到子线程去实现,这个不需要子线程。
*/
public class CountDownTimeActivity extends AppCompatActivity {
public static final int COUNTDOWN_TIME_CODE = 100001;
// 倒计时间隔
public static final int DELAY_MILLIS = 1000;
// 倒计时最大值
public static final int MAX_COUNT = 10;
private TextView mCountdownTimeTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//得到控件,在其上面显示变化
mCountdownTimeTextView = (TextView) findViewById(R.id.countdownTimeTextView);
//创建了一个handler,继承Handler 实例化
CountdownTimeHandler handler = new CountdownTimeHandler(this);
//第一个message 传过去最大值,开始运行。
Message message = Message.obtain();
message.what = COUNTDOWN_TIME_CODE;
message.arg1 = MAX_COUNT;
handler.sendMessageDelayed(message, DELAY_MILLIS);//添加时间间隔
}
//避免handler的内存泄露 创建静态内部类
public static class CountdownTimeHandler extends Handler {
static final int MIN_COUNT = 0;//倒计时最小值
//持有弱引用CountDownTimeActivity,GC回收时会被回收掉
final WeakReference<CountDownTimeActivity> mWeakReference;
CountdownTimeHandler(CountDownTimeActivity activity) {
mWeakReference = new WeakReference<>(activity);
}
//接收msg
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//用本Activity的对象,用activity得到控件去操作
CountDownTimeActivity activity = mWeakReference.get();
switch (msg.what) {
case COUNTDOWN_TIME_CODE:
int value = msg.arg1;//此时是最大值
//在TextView也就是页面set显示出来
activity.mCountdownTimeTextView.setText(String.valueOf(value--));//递减
//循环发送消息控制,一次次发送
if (value >= MIN_COUNT) {
Message message = Message.obtain();
message.what = COUNTDOWN_TIME_CODE;
message.arg1 = value;
sendMessageDelayed(message, DELAY_MILLIS);//添加时间间隔
}
break;
}
}
}
}