九、HandlerThread解析

Android系统中,执行耗时操作都需要另外开启子线程来执行,执行完线程以后自动销毁。为了避免重复的创建和销毁线程,避免过多的消耗性能,可以采用:

  • 1.使用线程池
  • 2.使用HandlerThread

1.HandleThread 使用场景,以及如何使用

使用场景

HandlerThread是Google帮我们封装好的,可以用来执行多个耗时操作,而不需要多次创建销毁线程,里面是采用HandlerLooper实现的。

使用方式
  1. 创建HandlerThread的实例对象
      //构造方法的参数表示的是线程的名称
  HandlerThread  mHandlerThread = new HandlerThread("wyw");
  1. 启动创建的实例对象
  mHandlerThread.start()
  1. 将实例对象和handler绑定在一起。(必须按照这三步进行)
  mThreadHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                checkForUpdate();
                if (isUpdate) {
                    mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
                }
            }
        };

完整使用实例代码:

public class MyActivity extends AppCompatActivity {
    private static final int MSG_UPDATE_INFO = 0x100;

    private HandlerThread mHandlerThread;
    private Handler mThreadHandler;
    Handler mMainHandler = new Handler();
    private TextView tv;
    private boolean isUpdate = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //创建handlerThread实例
        mHandlerThread = new HandlerThread("wyw");
        //启动handlerThread实例
        mHandlerThread.start();
        //handlerThread 绑定handler
        mThreadHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                checkForUpdate();
                if (isUpdate) {
                    mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
                }
            }
        };
    }

    private void checkForUpdate() {
        try {
            //模拟耗时
            Thread.sleep(1200);
            mMainHandler.post(new Runnable() {
                @Override
                public void run() {
                    String result = "实时更新中, 当前股票行情: <fontcolor='red'>%d</font>";
                    result = String.format(result, (int) Math.random() * 5000 + 1000);
                    tv.setText(result);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        isUpdate = true;
        mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
    }

    @Override
    protected void onPause() {
        super.onPause();
        isUpdate = false;
        mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandlerThread.quit();
        mMainHandler.removeCallbacksAndMessages(null);
    }
}

2.HandlerThread源码解析

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;

    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();
//持有锁机制来获得当前线程的Looper对象
        synchronized (this) {
            mLooper = Looper.myLooper();
//发出通知,当前线程已经创建mLooper对象成功,这里主要是通知getLooper方法中的waiter
            notifyAll();
        }
//线程的优先级设置
        Process.setThreadPriority(mPriority);
//方法空实现,可以重写这个方法,处理线程开启之前的准备工作
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
 
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
//直到线程创建完Looper之后才能获取Looper对象,Looper未创建成功阻塞
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
    @NonNull
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }

    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;
    }
}
  • HandlerThread构造方法,一个参数和两个参数的,name代表当前线程的名称,priority代表线程的优先级。
  • 前面强调在使用HandlerThread的时候必须先调用start(),接着才可以将HandlerThreadHandler绑定在一起。因为在run()方法中,我们才初始化looper,而我们调用HandlerThread的start()方法的时候,线程会交给虚拟机调度,由虚拟机自动调用run()方法。
  • run()方法中使用锁机制和notifyAll()的原因,可以在getLooper()方法中找到。
    在获取mLooper对象的时候存在一个同步的问题,只有当当线程成功创建并且Looper对象也创建成功之后才能获取mLooper的值。这里等待waite()和run()中的notifAll()共同实现同步。
  • quit()和quitSafe()的区别:
    通过源码追踪,可以发现,实际这个两个方法最终调用的是MessageQueue.quit(boolean safe)。MessageQueue.quit方法源码:
    void quit(boolean safe) {
         if (!mQuitAllowed) {
             throw new IllegalStateException("Main thread not allowed to quit.");
         }
         synchronized (this) {
             if (mQuitting) {
                 return;
             }
             mQuitting = true;
             if (safe) {
                 removeAllFutureMessagesLocked();
             } else {
                 removeAllMessagesLocked();
             }
             // We can assume mPtr != 0 because mQuitting was previously false.
             nativeWake(mPtr);
         }
     }
    
    可以看出,这个方法根据传入的参数safe来判断执行removeAllFutureMessagesLocked()或removeAllMessagesLocked()。
     private void removeAllMessagesLocked() {
         Message p = mMessages;
         while (p != null) {
             Message n = p.next;
             p.recycleUnchecked();
             p = n;
         }
         mMessages = null;
     }
    
    如果不是安全退出,执行removeAllMessagesLocked(),该方法就是遍历Message链表,移除所有信息的回调,并重置为null。
     private void removeAllFutureMessagesLocked() {
         final long now = SystemClock.uptimeMillis();
         Message p = mMessages;
         if (p != null) {
             if (p.when > now) {
                 removeAllMessagesLocked();
             } else {
                 Message n;
                 for (;;) {
                     n = p.next;
                     if (n == null) {
                         return;
                     }
                     if (n.when > now) {
                         break;
                     }
                     p = n;
                 }
                 p.next = null;
                 do {
                     p = n;
                     n = p.next;
                     p.recycleUnchecked();
                 } while (n != null);
             }
         }
     }
    
    如果是安全的退出,执行removeAllFutureMessagesLocked(),该方法,根据Message.when这个属性,判断我们当前消息队列是否正在处理消息,没有处理消息的话,直接移除所有的回调,正在处理的话,等待该消息处理完毕再退出该循环。因此说quiteSafe()是安全的,而quit()是不安全的,因为quit()不管是否正在处理,直接移除所有的回调。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,898评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,401评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,058评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,539评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,382评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,319评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,706评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,370评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,664评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,715评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,476评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,326评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,730评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,003评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,275评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,683评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,877评论 2 335

推荐阅读更多精彩内容