在我们开发中,我们常常会使用多线程来实现很多需求功能,譬如,在网络中访问数据,然后在界面中展示响应数据,在APP首页实现个轮播效果的viewpager,亦或是实现一个定时器的效果,定时的刷新我们的界面。。。今天我们这里稍作整理下,把能实现上述效果的可行方案归纳起来,总结如下四种方式:
方式一: AsyncTask
谷歌提供的异步工具,刷新UI神器,也是我们最常用的方式之一,很多网络请求框架也是基于它的,使用起来也很简单,贴一下代码
其中的doInbackground()方法是在子线程中调用,我们这里定义一个整形progress 然后用publishProgress()发消息给主线程,完成我们的的UI刷新,最后让线程休眠一秒钟,实现定时器的功能
private MyAsyncTask task = null;
private boolean running = true;
public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
while (running) {
try {
// 处理耗时操作。我们这边线程休眠1秒实现秒表的效果
progress++;
publishProgress();//类似于发消息给主线程,更新UI
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onProgressUpdate(Void... values) {
// 为主线程 实现我们刷新UI的逻辑
reFreshUi(progress);
super.onProgressUpdate(values);
}
}
然后我们定义两个方法,来实现该定时器的关停:
private void startTask() {
stopTask();
running = true;
task = (MyAsyncTask) new MyAsyncTask().execute();
}
private void stopTask() {
if (task != null) {
running = false;
task.cancel(true);
task = null;
}
}
当我们要开始执行定时器的时候,只需要new一个task然后调用execute()方法。将running设为ture,此时doinbackground就会进入无限循环中。
当我们要停止刷新UI时,将running设为false,我们的task就会跳出执行循环,然后调用cancel方法就可以停下来,再次调用开始的时候,我们不能直接调用execute,因为一个task对象该方法只能调用一次,所以我们需要new 一个task然后再调用execute()方法即可。
最后,我们在我们不需要使用的时候,也要将它停下来,以防内存泄露,比如在Activity的 ondestry方法中将它停下来:
@Override
protected void onDestroy() {
stopTask();
super.onDestroy();
}
让后我们来看看实现效果:
**方法二:
Thread +Handler**
这边我们使用自己定义的线程完成定时器操作,但是在这里,更新UI我们需要通过handler发消息给主线程,来实现我们的UI更新,代码如下:
Thread myThread = null;
private boolean threadRun = true;
private void startThread() {
stopThreand();
threadRun = true;
myThread = new Thread(new Runnable() {
@Override
public void run() {
try {
while (threadRun) {
// 以下执行耗时操作,执行完成以后。。发送消息给handler更新UI
progress++;
Message message = new Message();
message.what = 0;
mHandler.sendMessage(message);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
myThread.start();
}
private void stopThreand() {
if (myThread != null) {
threadRun = false;
myThread.interrupt();
myThread = null;
}
}
@SuppressLint("HandlerLeak")
public Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
reFreshUi(progress);
break;
}
}
};
**方法三
TimerTask+handler**
除了我们自己开线程以外,我们还可以利用java本身提供的TimerTask代替我们开子线程,并且可以实现重复周期,不需要我们自己来休眠线程,再加上handler来更新UI:
private Timer timer = null;
private TimerTask timerTask = null;
private void startTimer() {
stopTimer();
timer = new Timer();
timerTask = new TimerTask() {
@Override
public void run() {
progress++;//这边也是子线程,用于发消息更新UI
Message message = new Message();
message.what = 0;
mHandler.sendMessage(message);
}
};
timer.schedule(timerTask, 0, 1000);//第三个参数代表我们之前定义的休眠的1秒,即我们定义的重复周期
}
private void stopTimer() {
if (timer != null) {
timer.cancel();
timer = null;
}
if (timerTask != null) {
timerTask.cancel();
timerTask = null;
}
}
这边同样的的,timer.schedule方法只能调用一次,当我们听下来之后,再次开起来需要重新new对象。
**方法四
handler.postdelay**
此方法我们没有额外定义子线程,也可以实现类似于定时器的效果,我们只需要在Runable方法中反复回调handler.postdelay方法即可,在我们需要停止的时候,移除回调即可,即handler.removecallbacks(Runanble)。代码如下:
private Runnable r = new Runnable() {
@Override
public void run() {
progress++;//非子线程中,直接更新UI
reFreshUi(progress);
mHandler.postDelayed(r, 1000);//一秒以后,再次回调此方法
}
};
private void start() {
stop();
mHandler.post(r);
}
private void stop() {
mHandler.removeCallbacks(r);//移除回调
}
总结,以上四种方法都可以实现定时刷新UI,如果仅仅是延迟刷新UI或者定时器,则第四种方法更为直观和方便,没有开子线程,不会出现内存泄露的问题等,如果我们需要访问网络,则使用前面几种方法。
源码下载