handlerThread使用场景分析及源码解析

来源:互联网 发布:店铺做账用那个软件 编辑:程序博客网 时间:2024/06/05 16:01

 这篇博客将结合Android7.0源码,解析HandlerThread的使用场景及实现原理。

1.为什么要有handlerThread组件出现?

   在Android中我们使用消息机制进行线程间的消息传递,如果是向主线程传递消息,我们构造主线程的handler,并使用他发送消息。使用起来还是很方便的。但是如果是要子线程传递消息,就需要构建子线程的handler. 这样使用起来就比较麻烦了!比如下面这样:

 public class Main2Activity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main2);        new Thread(new Runnable() {            @Override            public void run() {                Looper.prepare();                Handler handler=new Handler(){                    @Override                    public void handleMessage(Message msg) {                        super.handleMessage(msg);                        //处理子线程的消息                    }                };                Looper.loop();            }        }).start();    }}

我们得在子线程中构建Looper,并调用Looper的loop方法才能正确的构建出子线程的handler对象。这样用也没什么,但代码看着实在是很不舒服呀。 所以android给我们提供了HandlerThread组件,封装了子线程中构建looper的繁琐操作。

2.HandlerThread的使用场景

   在介绍HandlerThread之前,我们先想一个它的使用场景,通过使用场景,结合使用示例。我们才能在分析它的源码的时候有一个好的切入点,而且还可以加深我们对于handlerThread的一些印象; 可以想象一下这样的一个场景,例如我之前研发了一款股票交易软件,因为股票的行情数据都是实时变化的。所以我们软件需要每隔一定时间向服务器请求行情数据。
代码示例如下:

public class MainActivity extends AppCompatActivity {    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式    TextView updateTimeTv;    Handler mUIHandler=new Handler(){        @Override        public void handleMessage(Message msg) {            //更新ui            updateTimeTv.setText((String)msg.obj);            //1秒后再次请求服务器,并更新数据            timeMessagehandler.sendEmptyMessageAtTime(0,1000);        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        updateTimeTv = (TextView)findViewById(R.id.tv);        initThreadHandler();    }    HandlerThread handlerThread;    Handler timeMessagehandler;    private void initThreadHandler() {        //构建HandlerThread        handlerThread=new HandlerThread("updateTimeThread");        //启动handler        handlerThread.start();        //构建子线程的handler对象        timeMessagehandler=new Handler(handlerThread.getLooper()){            @Override            public void handleMessage(Message msg) {                //请求服务端时间                try {                    //模拟耗时请求                    Thread.sleep(3000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                //网络请求成功,通过主线程更新ui                Message message=Message.obtain();                message.obj= df.format(new Date());                mUIHandler.sendMessage(message);                System.out.println("handlerTimeMessage"+Thread.currentThread().getName());            }        };        //向子线程发送消息,通知它发送网络请求        timeMessagehandler.sendEmptyMessage(0);    }}   

我们分析一下上面的代码实现,一步一步来解析HandlerThread的使用,略过mUIHandler和view的初始化代码不表。我们可以看到上面的代码先构建了一个HandlerThread对象,然后调用了它的start方法。 这里我们可以提前剧透一下。HandlerThread对象对象其实继承了Thread对象。所以调用它的start方法就会执行它的run方法。 然后我们用handlerThread.getLooper()方法构建一个timeMessagehandler对象。 从上面的代码看timeMessagehandler对象的handleMessage中可以执行耗时操作,也就说明该方法是执行在子线程中的。那handlerThread.getLooper()方法所获取的方法也就肯定是子线程的Looper对象了。 这部分不清楚的可以看我另一篇文章,android消息机制源码分析http://blog.csdn.net/nightcurtis/article/details/77539971。 所以我们就可以timeMessagehandler对象向子线程发送消息执行耗时任务, 然后通过主线程的mUIHandler对象发消息处理ui的显示。 这些代码就太简单这里就不说了。 结合这段使用代码实例,我们可以知道我们下面这短短的3行代码就能够完成之前构建线程,初始化looper等等操作才能完成的构造子线程handler的这样一个需求。这是怎么做到的呢!

      handlerThread=new HandlerThread("updateTimeThread");        //启动handler      handlerThread.start();        //构建子线程的handler对象      timeMessagehandler=new Handler(handlerThread.getLooper());

3.HandlerThread的源码解析

   通过HandlerThread的使用场景解析我们知道通过短短3行代码就可以完成之前初始化looper,构建handler对象,并调用loop才能正确创建的子线程handler对象。 这里面的秘密是什么呢? 结合HandlerThread的源码。我们来解析一下:

   public class HandlerThread extends Thread {    int mPriority;    int mTid = -1;    Looper mLooper;    public HandlerThread(String name) {        super(name);        mPriority = Process.THREAD_PRIORITY_DEFAULT;    }   public HandlerThread(String name, int priority) {        super(name);        mPriority = priority;    }   protected void onLooperPrepared() {    }    @Override    public void run() {        mTid = Process.myTid();        Looper.prepare();        synchronized (this) {            mLooper = Looper.myLooper();            notifyAll();        }        Process.setThreadPriority(mPriority);        onLooperPrepared();        Looper.loop();        mTid = -1;    }    public Looper getLooper() {        if (!isAlive()) {            return null;        }        // If the thread has been started, wait until the looper has been created.        synchronized (this) {            while (isAlive() && mLooper == null) {                try {                    wait();                } catch (InterruptedException e) {                }            }        }        return mLooper;    }     ......   }

  其实HandlerThread的源码很简单。HandlerThread继承了Thread对象,所以我们构造HandlerThread对象的时候就是构建了一个子线程对象。 调用线程对象的start方法,就会执行HandlerThread的run函数。 我们看到在run方法中有 Looper.prepare(),Looper.loop()两个方法。其实和我们在子线程中构建looper的方法完全相同。 真正值得注意的是,发现在run方法中和looper的getLooper中有一个线程通信的处理。 这个是为什么呢? 想想之前我们是主线程中先构建handlerThread对象,然后调用它的start方法,去初始化looper对象。 然后就调用handlerThread的getLooper()方法去初始化一个子线程的Handler对象。 我们知道 handlerThread的start方法是执行在子线程中的,而handlerThread的getLooper()方法是执行在主线程中的。我们不能确认他们谁先执行,如果是getLooper()方法先执行,而这个时候子线程的looper还没有创建。构建handler就会抛出异常。 所以我们需要确保getLooper()方法在Looper创建成功后执行。所以在这里添加了线程通信的处理。

4.总结

其实分析源码后我们能够发现HandlerThread的源码特别简单,但我们解析它的源码不止是为了分析它的源码。更重要的是要学习它的一下封装的思想。 还有就是了解它的原理后,以后遇到使用了HandlerThread的组件心里上会有征服感。哈哈。 比如了解了HandlerThread的原理后再去看IntentService的原理。 真是不能再简单了。 有么有!!

原创粉丝点击