• Handler系列之创建子线程Handler


      上一篇我介绍了Handler机制的工作原理,默认情况下,ActivityThread类为我们创建的了主线程的Looper和消息队列,所以当你创建Handler之后发送消息的时候,消息的轮训和handle都是在ui线程进行的。这种情况属于子线程给主线程发消息,通知主线程更新ui...等,那么反过来,怎么才能让主线程给子线程发消息,通知子线程做一些耗时逻辑??

      之前的学习我们知道,Android的消息机制遵循三个步骤:

        1  创建当前线程的Looper  

        2  创建当前线程的Handler 

        3  调用当前线程Looper对象的loop方法

      看过之前文章的朋友会注意到,本篇我特意强调了“当前线程”。是的之前我们学习的很多都是Android未我们做好了的,譬如:创建主线程的Looper、主线程的消息队列...就连我们使用的handler也是主线程的。那么如果我想创建非主线程的Handler并且发送消息、处理消息,这一系列的操作我们应该怎么办那???不怎么办、凉拌~~~什么意思???依葫芦画瓢,依然遵循上面的三步走,直接上代码!!!!

    public class ChildThreadHandlerActivity extends Activity {
        private MyThread childThread;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_handler);
    
            childThread = new MyThread();
            childThread.start();
    
            Handler childHandler = new Handler(childThread.childLooper){//这样之后,childHandler和childLooper就关联起来了。
                public void handleMessage(Message msg) {
                    
                };
            };
        }
    
        private class MyThread extends Thread{
            public Looper childLooper;
    
            @Override
            public void run() {
                Looper.prepare();//创建与当前线程相关的Looper
                childLooper = Looper.myLooper();//获取当前线程的Looper对象
                Looper.loop();//调用此方法,消息才会循环处理
            }
        }
    }

    代码如上,我们依然循序Android的三步走战略,完成了子线程Handler的创建,难道这样创建完了,就可以发消息了么?发的消息在什么线程处理?一系列的问题,怎么办?看代码!!!运行上述代码,我们发现一个问题,就是此代码一会崩溃、一会不崩溃,通过查看日志我们看到崩溃的原因是空指针。谁为空???查到是我们的Looper对象,怎么会那?我不是在子线程的run方法中初始化Looper对象了么?话是没错,但是你要知道,当你statr子线程的时候,虽然子线程的run方法得到执行,但是主线程中代码依然会向下执行,造成空指针的原因是当我们new Handler(childThread.childLooper)的时候,run方法中的Looper对象还没初始化。当然这种情况是随机的,所以造成偶现的崩溃。

      那怎么办?难道我们不能创建子线程Handler ???No!!!No!!!No!!!,你能想到的Android早就为我们实现好了,HandlerThread类就是解决这个问题的关键所在,看代码!!!

    public class HandlerThreadActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_handler);
            TextView textView = (TextView) findViewById(R.id.tv);
            textView.setText("HandlerThreadActivity.class");
    
            HandlerThread handlerThread = new HandlerThread("HandlerThread");
            handlerThread.start();
    
            Handler mHandler = new Handler(handlerThread.getLooper()){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    Log.d("HandlerThreadActivity.class","uiThread2------"+Thread.currentThread());//子线程
                }
            };
    
            Log.d("HandlerThreadActivity.class","uiThread1------"+Thread.currentThread());//主线程
            mHandler.sendEmptyMessage(1);
        }
    }

    创建HandlerThread对象的时候,有个参数,是指定线程名字的。上面的代码不管运行多少次都不会奔溃!!!并且这种方法创建的handler的handleMessage方法运行在子线程中。所以我们可以在这里处理一些耗时的逻辑。到此我们完成了主线程给子线程发通知,在子线程做耗时逻辑的操作。

      下面我们去看看源码,看看为什么使用HandlerThread就可以避免空指针那?

        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类的getLooper方法如上,我们看到当我们获取当前线程Looper对象的时候,会先判断当前线程是否存活,然后还要判断Looper对象是否为空,都满足之后才会返回给我Looper对象,否则处于等待状态!!既然有等待,那就有唤醒的时候,在那里那???我们发现HandlerThread的run方法中,有如下代码:

        @Override
        public void run() {
            mTid = Process.myTid();
            Looper.prepare();
            synchronized (this) {
                mLooper = Looper.myLooper();
                notifyAll();
            }
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            Looper.loop();
            mTid = -1;
        }

    说明了什么那???HandlerThread类start的时候,Looper对象就初始化了,并唤醒之前等待的。所以HandlerThread很好的避免了之前空指针的产生。所以以后要想创建非主线程的Handler时,我们用HandlerThread类提供的Looper对象即可。

      至此,前三篇我们讲了Handler的使用、工作原理、创建子线程Handler。下一篇我会讲使用Handler引起的内存泄漏的解决办法。

  • 相关阅读:
    异步运行
    ES6新增----深入理解generator
    ES6新增(箭头函数)
    ES6新增(有关变量)
    I2C写时序图[转]
    kernel中,dump_stack打印调用栈,print_hex_dump打印一片内存,记录一下
    http://man.linuxde.net/ 转
    Linux网络
    Linux基础:用tcpdump抓包(转)
    指针长度问题,不同架构的指针长度不同,可能32位,也可能64位,与unsigned long长度相同
  • 原文地址:https://www.cnblogs.com/lang-yu/p/6228832.html
Copyright © 2020-2023  润新知