buder

201709013工作日记--Android消息机制HandlerThread

1.首先来看一个常规的handler用法:

在主线程中建立一个handler:

private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 0:
                    mTestTV.setText("This is handleMessage");//更新UI
                    break;
            }
        }
    };

 

在子线程中进行耗时操作,结束后发送消息,主线程收到消息后进行更新UI操作。

new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);//在子线程有一段耗时操作,比如请求网络
                    mHandler.sendEmptyMessage(0);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

 

2.现在来看看handler.post()的版本:

private Handler mHandler;//全局变量
@Override
protected void onCreate(Bundle savedInstanceState) {
    mHandler = new Handler();
    new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);//在子线程有一段耗时操作,比如请求网络
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                mTestTV.setText("This is post");//更新UI
                            }
                        });
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();

 

耗时操作完成之后,直接在handler开启的子线程中进行了更新UI的操作。post和sendMessage原理都是封装成Message,并且最终都调用了enqueueMessage()一个无限循环将消息加入到消息队列中(链表的形式)。翻看MessageQueue的方法,我们找到了next(),代码太长,不赘述,我们知道它是用来把消息取出来的就行了。不过这个方法是在什么地方调用的呢,不是在Handler中,我们找到了Looper这个关键人物,我叫他环形使者,专门负责从消息队列中拿消息。参考博客:http://blog.csdn.net/ly502541243/article/details/52062179/

 

3.HandlerThread用法

(1)创建HandlerThread并启动

mThread = new HandlerThread("handler_thread"); 
mThread.start();

 

(2)创建处理任务的handler和在主线程中更新UI的handler

一句话来总结就是:通过handler与HandlerThread进行绑定相当于开启了一个子线程,在这个子线程中处理任务,处理后的任务形成消息或者其他数据形式。再通过主线程的handler.sendMessage(子线程结果),发送给主线程的handler,进行UI操作。

其中处理任务的mWorkHandler与handlerThread关联起来,那么这个Handler对象就是与HandlerThread这个线程绑定了(这时就不再是与UI线程绑定了,这样它处理耗时操作将不会阻塞UI),它将在handleMessage(Message msg)中处理耗时任务,之后在mUIHandler对UI进行刷新,如果在handleMessage()执行完成后,如果想要更新UI,可以用UI线程的Handler发消息给UI线程来更新。

mWorkHandler = new Handler(mThread.getLooper());
mUIHandler = new Handler();

 

(3)在合适的时机退出HandlerThread,比如activity中的onDestroy(),方法有quit()和quitSafely()

下面我们看一个比较典型的handlerThread的使用过程:参考http://blog.csdn.net/javazejian/article/details/52426353

public class HandlerThreadActivity extends Activity {
    /**
     * 图片地址集合
     */
    private String url[]={
            "//img-blog.csdn.net/20160903083245762",
            "//img-blog.csdn.net/20160903083252184",
            "//img-blog.csdn.net/20160903083257871",
            "//img-blog.csdn.net/20160903083257871",
            "//img-blog.csdn.net/20160903083311972",
            "//img-blog.csdn.net/20160903083319668",
            "//img-blog.csdn.net/20160903083326871"
    };
    private ImageView imageView;
    private Handler mUIHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            LogUtils.e("次数:"+msg.what);
            ImageModel model = (ImageModel) msg.obj;
            imageView.setImageBitmap(model.bitmap);
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_thread);
        imageView= (ImageView) findViewById(R.id.image);
        //创建异步HandlerThread
        HandlerThread handlerThread = new HandlerThread("downloadImage");
        //必须先开启线程
        handlerThread.start();
        //子线程Handler
        Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());
        for(int i=0;i<7;i++){
            //每个1秒去更新图片
            childHandler.sendEmptyMessageDelayed(i,1000*i);
        }
    }
    /**
     * 该callback运行于子线程
     */
    class ChildCallback implements Handler.Callback {
        @Override
        public boolean handleMessage(Message msg) {
            //在子线程中进行网络请求
            Bitmap bitmap=downloadUrlBitmap(url[msg.what]);
            ImageModel imageModel=new ImageModel();
            imageModel.bitmap=bitmap;
            imageModel.url=url[msg.what];
            Message msg1 = new Message();
            msg1.what = msg.what;
            msg1.obj =imageModel;
            //通知主线程去更新UI
            mUIHandler.sendMessage(msg1);
            return false;
        }
    }
    private Bitmap downloadUrlBitmap(String urlString) {
        HttpURLConnection urlConnection = null;
        BufferedInputStream in = null;
        Bitmap bitmap=null;
        try {
            final URL url = new URL(urlString);
            urlConnection = (HttpURLConnection) url.openConnection();
            in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
            bitmap=BitmapFactory.decodeStream(in);
        } catch (final IOException e) {
            e.printStackTrace();
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
            try {
                if (in != null) {
                    in.close();
                }
            } catch (final IOException e) {
                e.printStackTrace();
            }
        }
        return bitmap;
    }
}

 

  在这个案例中,我们创建了两个Handler,一个用于更新UI线程的mUIHandler和一个用于异步下载图片的childHandler。最终的结果是childHandler会每个隔1秒钟通过sendEmptyMessageDelayed方法去通知ChildCallback的回调函数handleMessage方法去下载图片并告诉mUIHandler去更新UI界面,以上便是HandlerThread常规使用。

下面来看另外一个例子,使用post方式通信:

public class MainActivity extends AppCompatActivity {

    private TextView mTvServiceInfo;

    private HandlerThread mCheckMsgThread;
    private Handler mCheckMsgHandler;
    private boolean isUpdateInfo;

    private static final int MSG_UPDATE_INFO = 0x110;

    //与UI线程管理的handler
    private Handler mHandler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i("~~~~~~~~~~~~~~~now is ",Thread.currentThread().getName());
        setContentView(R.layout.activity_main);

        //创建后台线程
        mCheckMsgThread = new HandlerThread("check-message-coming");
        mCheckMsgThread.start();
        mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                try
                {
                    //模拟耗时,处理任务(模拟实时获取大盘数据),处理完成之后,通知mHandler完成界面更新
                    Thread.sleep(1000);
                    mHandler.post(new Runnable()
                    {
                        @Override
                        public void run()
                        {
                            String result = "实时更新中,当前大盘指数:<font color='red'>%d</font>";
                            result = String.format(result, (int) (Math.random() * 3000 + 1000));
                            mTvServiceInfo.setText(Html.fromHtml(result));
                        }
                    });

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (isUpdateInfo)
                {
                    mCheckMsgHandler.sendEmptyMessageDelayed(MSG_UPDATE_INFO, 1000);
                }
            }
        };

    }
}

 

4.handlerThread的一些细节补充学习:

  HandlerThread本质上是一个线程类,继承自Thread类,但是HandlerThread有自己的Looper对象,可以进行looper循环,不断从MessageQueue中取消息。

HandlerThread的特点

  • HandlerThread将loop转到子线程中处理,说白了就是将分担MainLooper的工作量,降低了主线程的压力,使主界面更流畅。
  • 开启一个线程起到多个线程的作用。处理任务是串行执行,按消息发送顺序进行处理
    相比多次使用new Thread(){…}.start()这样的方式节省系统资源。
    但是由于每一个任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。
  • HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程。
  • 通过设置优先级就可以同步工作顺序的执行,而又不影响UI的初始化;
  • HandlerThread本质上就是一个普通Thread,只不过内部建立了Looper

总结

  HandlerThread比较适用于单线程+异步队列的场景,比如IO读写操作,耗时不多而且也不会产生较大的阻塞。对于网络IO操作,HandlerThread并不适合,因为它只有一个线程,还得排队一个一个等着

posted on 2017-09-14 17:09  buder  阅读(194)  评论(0编辑  收藏  举报

导航