在上一个例子中,最终我们发现,其实用到的线程只有一个,那就是程序的主线程(UI线程)。那么怎么把那个例子改成用新建的线程来实现呢,今天我尝试了一下,写了下面这个小程序。
当然,首先要声明一下,今天的这个例子并不是推荐的写法,而是我为了学习多线程而写的例子(貌似更常用的是AsyncTask,而不是Thread和Handler去更新UI)。
在今天的这个例子中,我用到了Looper,先说说Looper是什么
在API中是这么解释Lopper的:Class used to run a message loop for a thread。我的理解是Looper是用来控制message queue的类
Looper常用的几个用法有:
Looper.prepare() 安卓的主线程中会默认调用这个方法来创建消息队列。但是,如果我们自己新建的线程,如果需要消息队列,则需要手动调用这个方法。
Looper.loop() 这个方法用在prepare()方法之后,调用该方法之后,进入消息循环。
Looper.getMainLooper() 我在代码中用到了这个方法,这个方法的作用是获得主线程的Looper实例
Looper.myLooper() 这个方法用到获取当前线程的Looper实例
今天这个例子和第二篇中实现的功能一下,我只不过改了一个写法而已,下面是代码:
package com.example.handler2;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
public class MainActivity extends Activity {
Button startButton = null;
Button stopButton = null;
ProgressBar progressbar = null;
Thread counter = null;
//获取主线程的looper
Looper looper = Looper.getMainLooper();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startButton = (Button) findViewById(R.id.startButton);
stopButton = (Button) findViewById(R.id.stopButton);
progressbar = (ProgressBar) findViewById(R.id.progressBar);
//为button绑定onclicklistener
startButton.setOnClickListener(new ButtonOnclickListener());
stopButton.setOnClickListener(new stopOnclickListener());
}
class ButtonOnclickListener implements OnClickListener{
public void onClick(View v) {
progressbar.setVisibility(View.VISIBLE);
counter = new Thread(){
int i = 1;
@Override
public void run() {
// TODO Auto-generated method stub
i += 10;
Message msg = handler.obtainMessage();
msg.arg1 = i;
//让线程延迟一秒
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
Log.i("run", "run "+i+"%");
Log.i("run", Thread.currentThread().getName());
msg.sendToTarget();
}
};
counter.start();
}
}
class stopOnclickListener implements OnClickListener{
public void onClick(View v) {
//从message queue 中去掉run
handler.removeCallbacks(counter);
//让progressbar置成隐藏
progressbar.setVisibility(View.GONE);
}
}
//将handler与主线程关联
Handler handler = new Handler(looper){
public void handleMessage(android.os.Message msg) {
int i =msg.arg1;
//根据message中传来的参数控制进度条
progressbar.setProgress(i);
Log.i("run", Thread.currentThread().getName());
if(i<100){
handler.post(counter);
}else{
handler.removeCallbacks(counter);
progressbar.setVisibility(View.GONE);
}
};
};
}
这段代码,和第二篇中最大的差别就是,当我点击启动按钮后,调用的不是主线程的run,而是我新建的线程。
但是因为安卓不允许我们在主线程之外的线程中对UI进行修改,所以我在新建的线程中只是进行计数,然后将计数的结果通过message传递到主线程中,在主线程中更新进度条。
遗留问题:
本来这个例子到这里就结束了,但是,我为了深入了解一下就在我新建的线程的run中和主线程Handler的handlerMessage方法中打印了当前线程的名称:
结果如下:
按照我最初的理解,在日志中应该是主线程和我的线程交替写入日志,但是实际的情况是在第一次是counter线程,后面打印的都是主线程
请问各位,有谁知道这是为什么吗?
我看到在API中Handler的post方法是这么说明的:Causes the Runnable r to be added to the message queue. The runnable will be run on the thread to which this handler is attached.
难道是,我使用了post方法,就相当于把counter线程的run中的代码拷贝中主线程中去执行了吗?如果有谁知道麻烦为我解答一下,不甚感激!