如何在子线程中创建并使用Handler

上一篇文章我们从源代码的角度分析了Handler的实现,这篇文章我们说下如何在子线程中创建Handler,在子线程中创建Handler只需2步:

  1. 创建looper:Looper.prepare()
  2. 启动looper:Looper.loop()

为了创建当前Thread的Handler对外提供一个方法获取当前的Looper,

Java代码如下:

public class HandlerThread extends Thread {

    private Looper mLooper;

    @Override
    public void run() {
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
        }
        Looper.loop();
    }

    public Looper getLooper() throws Exception {
        if (!isAlive()) {
            throw new Exception("current thread is not alive");
        }
        if (mLooper == null) {
            throw new Exception("current thread is not start");
        }
        return mLooper;
    }
}

下面我们来使用下看看:

class MainActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val handlerThread = com.example.handlerdemo.HandlerThread()
        handlerThread.start()
        val handler1 = object : Handler(handlerThread.getLooper()) {
            override fun handleMessage(msg: Message?) {
                super.handleMessage(msg)
                Log.d("aaa", "thread:${Thread.currentThread().name},handle message")
            }
        }

        handler1.sendMessage(Message.obtain())
    }
    
}

上面的代码非常简单就是创建HandlerThread并start,然后通过HandlerThread的looper创建Handler,发送一条消息进行验证。
然而我们运行我们会发现直接crash,异常如下:
在这里插入图片描述
找到对应的代码我们发现是mLooper未初始化造成的,可我们的代码是先start然后获取的looper,为什么会没有初始化呢?其实这里就是典型的线程同步问题,由于我们启动HandlerThread和创建Handler都是在主线程中运行的,而HandlerThread的run方法是运行在子线程中,所有导致获取looper时HandlerThread还未初始化looper,那么如何解决这个问题呢?

最简单的解决方案:通过notifyAll和wait

public class HandlerThread extends Thread {

    private Looper mLooper;

    @Override
    public void run() {
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Looper.loop();
    }

    public Looper getLooper() throws Exception {
        if (!isAlive()) {
            throw new Exception("current thread is not alive");
        }
        synchronized (this){
            if (null == mLooper){
                wait();
            }
        }
        return mLooper;
    }
}

在运行,输出日志如下:
在这里插入图片描述
我们发现handler处理是在子线程中处理而非主线程。
其实解决线程同步有很多办法,比如无限循环等待、Semaphore(信号量)等,不过建议使用上面的方式。

其实SDK已经为我们提供了HandlerThread,我们无需自己来实现,有人可能就说了你这不是蛋疼吗?
我想说我们不仅仅要会使用SDK提供的api,我们还要学会其中的思想,谁敢说我清楚所有SDK的类和api呢?在开始的时候我真不知道有HandlerThread这个类,然后项目上有需求才实现了这个类,当时我记得很清楚,开始没有注意线程同步的问题,使用的时候才发现的,这个类虽然非常简单,但涉及到非常多的知识,比如Handler的运行原理、线程同步等。
下面我们看看系统的HandlerThread:
在这里插入图片描述
通过这2个构造函数我们发现可以对此线程设置名称和优先级。
在这里插入图片描述
与我们的run方法对比多了onLooperPrepared方法,这个方法是让子类重写的。
我们在看下getLooper方法:
在这里插入图片描述
和我自己写的区别主要在synchronized代码块内,区别如下:

  1. 我们是if判断,而系统是while循环判断。
  2. 我们没有在次判断isAlive,而系统判断了。
  3. 未抛出异常,而是直接返回了null。

我们先想第一个问题是否需要while循环,想想如果用户调用了

handlerThread.notifyAll();

那我们将会返回null,显然这不是我们想要的,所以while循环是很必要的,只要looper为null将一直等待。

第二个问题:上面已经判断了isAlive(),while循环是否需要再次判读,注意wait是挂起操作,如果在挂起的时候用户结束了线程,那么此时线程已经结束了,所以需要判断。

第三个问题:是抛出异常还是返回null,现在想来个人觉得这2种方法都不是很好,首先返回null,这需要用户处理null的情况,抛出异常在使用上会不太方便,用户需要自己处理异常,使用如下:
在这里插入图片描述
需要用户自己try…catch
不过2选1的话个人认为返回null更好些,不过需要我们使用的时候注意一些。
继续看HandlerThread源码:

在这里插入图片描述
这里提供了一个Handler对象,如果使用了这个Handler,那如何重写handleMessage方法呢?暂时没有想到这个方法的意义在哪?

继续看:
在这里插入图片描述
这里提供了2个退出的方法,这2个方法是looper的方法,这里不再解释了。

结语:就是这么一个简简单单的功能,可里面涉及到很多方面,在看源码的时候更重要的是学会他们的思想,在看源码的时候可以想想如果是我来写这个功能,我如何来写?千万不要只是想想,不实际动手写,写完后在和系统的对比才会学到更多的东西。

发布了113 篇原创文章 · 获赞 66 · 访问量 30万+

猜你喜欢

转载自blog.csdn.net/mengks1987/article/details/87797941