应该如何在子线程准确创建Handler

一.handler基本流程

我们已经知道Handler发送消息,处理消息的机制了。ActivityThread在main方法中,为当前线程创建一个looper,一个messagequeue,并且通过调用Looper的loop方法,开启死循环不断地从messagequeue里面去取消息。当handler通过sendmessage的方法把消息添加到消息队列中,主线程的死循环就会拿到消息,进而做一系列的事情。

我们日常创建Handler的方法:

public class HandlerActivityJava extends AppCompatActivity {


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.handler_layout);
        new Thread(new Runnable() {
            @Override
            public void run() {
                mHandler.sendEmptyMessage(0);
            }
        });
    }


    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            testLog();
        }
    };
}

通常是在主线程中调用handler的改造函数,创建一个匿名内部类。那么在子线程中该如何new 一个handler并且让他正常工作呢?

二.子线程如何创建handler

我们先看看在主线程直接new出handler,handler的构造函数:

public Handler() {
        this(null, false);
    }


public Handler(@Nullable Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

发现有两行重要的代码:

   
//拿到当前线程绑定的Looper
 mLooper = Looper.myLooper();
//拿到当前线程的messagequeue
mQueue = mLooper.mQueue;

我们知道这两个东西在app刚刚启动的时候会自动为当前线程绑定一个looper和messagequeue。但是我们现在在子线程里面去new一个handler,这两个东西就需要我们自己去new出来并且与当前线程绑定。

第一版本

 private Handler threadHandler;
    public void method2() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                threadHandler = new Handler(new Handler.Callback() {
                    @Override
                    public boolean handleMessage(@NonNull Message msg) {
                        return false;
                    }
                });
                Looper.loop();
            }
        }).start();
    }

在这个版本里面,我们在子线程里面自己为当前线程创建了一个Looper,Messagequeue。并且为当前线程启动了一个死循环,不断从消息队列里面去取消息。但是发现一个问题,如果我不想通过匿名内部类的方式创建Handler的话,我该怎么办?

第二版本

    public void method3(){
        Thread thread = new Thread(new Runnable() {
            Looper looper;
            @Override
            public void run() {
                Looper.prepare();
                looper = Looper.myLooper();
                Looper.loop();
            }

            public Looper getLooper(){
                return looper;
            }
        });
        thread.start();

        Handler handler1 = new Handler(thread.getLooper());
    }

thread.getLooper方法报错,thread是一个匿名内部类,外部不会持有匿名内部类的引用。

第三版本

public class MyHandlerThread extends Thread {

    private Looper looper;

    MyHandlerThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        super.run();
        Looper.prepare();
        looper = Looper.myLooper();
        Looper.loop();
    }


    public Looper getLooper() {
        return looper;
    }
}

直接创建一个MyHandlerThread类,把getLooper暴露出去。使用的时候:

public void method4(){
        MyHandlerThread thread = new MyHandlerThread("liaoliao");
        thread.start();

        Handler handler1 = new Handler(thread.getLooper());
        Handler handler2 = new Handler(thread.getLooper());
    }

可是这样会有什么问题?

这边有一个并发问题,因为MyHandlerThread是一个子线程,handler1和handler2又是在主线程里面调用的getLooper方法,谁也不能保证在调用getLooper的时候,MyHandlerThread的start方法已经执行完成了,looper已经完成赋值了。

完美版本

使用HandlerThread:

public void method5(){
        HandlerThread thread = new HandlerThread("liaoliao");
        thread.start();

        Handler handler1 = new Handler(thread.getLooper());
        Handler handler2 = new Handler(thread.getLooper());
    }

为什么HandlerThread不会有上面提到的线程问题呢?

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

        boolean wasInterrupted = false;

        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    wasInterrupted = true;
                }
            }
        }

        /*
         * We may need to restore the thread's interrupted flag, because it may
         * have been cleared above since we eat InterruptedExceptions
         */
        if (wasInterrupted) {
            Thread.currentThread().interrupt();
        }

        return mLooper;
    }

run方法和getLooper方法都使用了synchronized方法锁住了this,也就是当前HandlerThread对象。

当run方法里面的同步代码块先执行的时候,getLooper方法的同步代码块被上锁了,必须等到mLooper被赋值完成后才会拿到锁。

当getLooper里面的同步代码块先执行的时候,检测到mLooper是null,直接执行wait方法,当前主线程挂起,释放cpu资源,释放锁。run方法里面的同步代码块会立即执行,mLooper被赋值后,执行notifyAll方法,wait被重新唤醒,继续执行,最后获取mLooper返回。

wait()和sleep()都是挂起,有什么区别?


wait方法挂起,会释放cpu资源,把锁释放给其他人用
sleep方法挂起,不会释放cpu资源,还会占有锁

为什么使用notifyAll方法不使用notify?


可能上层同时有多个地方都在getLooper,多个线程都在调用getLooper方法,所以需要notifyAll

wait和notify方法执行完后,会立即执行关于锁的操作吗?

wait的时候会立刻把锁交出去,让自己处于阻塞状态

notify需要等待synchronized包裹的所有代码执行完毕,才会执行通知wait方法继续执行。

getLooper方法里面为什么使用while循环,不用if?


这与应用场景有关,如果上层对HandlerThread做了一层封装,也使用synchronized(this)。notifyAll,这时候wait的地方会继续执行,拿到的looper还是空的,所以while循环再判断一次。

你可能感兴趣的:(Handler,java,开发语言,android,android,studio)