Android面试系列文章2018之Android部分HandlerThread机制篇

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ClAndEllen/article/details/79346492

Android面试系列文章2018之Android部分HandlerThread机制篇

HandlerThread知识体系图

1.HandlerThread是什么?

  产生的背景:开启子线程进行耗时操作,多次创建和销毁子线程是很耗费资源的,但是木有关系,谷歌考虑了这点为我们专门开发出了HandlerThread机制,那么它有什么特点呢?请看下面。

  本质:Handler + Thread + Looper,是一个Thread内部有Looper。当你被面试官问道HandlerThread是什么,有何特点,那么你应该这么回答:

  a.HandlerThread本质上是一个线程类,它继承了Thread。

  b.HandlerThread有自己内部的Looper对象,可以进行Looper循环。

  c.通过获取HandlerThread的Looper对象传递给Handler对象,可以在handlerMessage方法中执行异步任务。

  d.优点是不会有堵塞,减少对性能的消耗,缺点是不能进行多任务的处理,需要等待进行处理,处理效率较低。

  e.与线程池注重并发不同,HandlerThread是一个串行队列,HandlerThread背后只有一个线程。

2.HandlerThread的使用

  其实HandlerThread的使用就是为了主线程通知子线程,再由子线程通知主线程,这里面建立一套完整的通信体系,并不像传统的开启子线程,使用HandlerThread的好处就是主线程可以发送信息告诉子线程要干什么,而且可以一直发送信息,无论在主线程的何处,都可以发送合适的信息告诉子线程,它要干什么,子线程做完了信息中交代的事情,然后再通知主线程更新等操作。说白了HandlerThread提供了主线程向子线程的通信。

public class MainActivity extends AppCompatActivity {


    private ImageView imageView;

    private HandlerThread handlerThread;

    /**
     * 图片地址数组
     */
    private String url[]={
        "图片地址1","图片地址2","图片地址3","图片地址4","图片地址5"
    };

    private Handler uiHandler = new Handler(){

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            //加载图片
            imageView.setImageBitmap((Bitmap) msg.obj);


        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = findViewById(R.id.iv1);

        handlerThread = new HandlerThread("downloadImage");
        //必须开启HandlerThread线程
        handlerThread.start();

        //获取子Handler
        Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());

        for(int i=0;i<url.length;i++){
            childHandler.sendEmptyMessageDelayed(i,1000*i);
        }


    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        handlerThread.quit();//注意释放资源
    }

    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;
    }


    class ChildCallback implements Handler.Callback{
        @Override
        public boolean handleMessage(Message message) {
            //在子线程中进行网络请求
            Bitmap bitmap=downloadUrlBitmap(url[message.what]);
            Message message1 = new Message();
            message1.obj =bitmap;
            //通知主线程去更新UI
            uiHandler.sendMessage(message1);
            return false;
        }
    }




}

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

3.HandlerTread机制原理

下面我们通过源码来分析一下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();    //唤醒其他线程的等待锁,这边是为了唤醒getLooper()方法中的等待锁
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }        
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        // 线程开始,该方法会一直等待Looper对象创建完成才会执行,               
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }    
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }    
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }    
    public int getThreadId() {
        return mTid;
    }
}

  首先我们看一下它的run()方法,可以发现该方法中调用Looper.myLooper()创建了一个Looper对象mLooper,并把该对象放到线程变量sThreadLocal中,然后通过调用Looper.loop()开启消息循环,Looper.loop()方法会不断循环从MessageQueue中取出消息处理消息,没有消息是则会阻塞。getLooper()方法是获取线程中的Looper对象,可以用来初始化Handler对象。
quit()和quitSafely()的区别在于,quit()会清空所有消息(无论延时或非延时),quitSafely()只清空延时消息,无论是调用了quit()方法还是quitSafely()方法,Looper就不再接收新的消息。即在调用了Looper的quit()或quitSafely()方法之后,消息循环就终结了,这时候再通过Handler调用sendMessage或post等方法发送消息时均返回false,线程也就结束了。

没有更多推荐了,返回首页