在上一个例子中,最终我们发现,其实用到的线程只有一个,那就是程序的主线程(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中的代码拷贝中主线程中去执行了吗?如果有谁知道麻烦为我解答一下,不甚感激!