Android中的多线程

1. Java多线程基础

Java多线程,线程同步,线程通讯

2. Android常用线程

HandlerThread:

HandlerThread:线程中实现了Handler机制。

// 步骤1:创建HandlerThread实例对象
// 传入参数 = 线程名字,作用 = 标记该线程
   HandlerThread mHandlerThread = new HandlerThread("handlerThread");
// 步骤2:启动线程
   mHandlerThread.start();

// 步骤3:创建工作线程Handler & 复写handleMessage()
// 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
// 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
  Handler workHandler = new Handler( handlerThread.getLooper() ) {
            @Override
            public boolean handleMessage(Message msg) {
                ...//消息处理
                return true;
            }
        });
// 步骤4:使用工作线程Handler向工作线程的消息队列发送消息
// 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
  // a. 定义要发送的消息
  Message msg = Message.obtain();
  msg.what = 2; //消息的标识
  msg.obj = "B"; // 消息的存放
  // b. 通过Handler发送消息到其绑定的消息队列
  workHandler.sendMessage(msg);

// 步骤5:结束线程,即停止线程的消息循环
  mHandlerThread.quit();

Android 的 Handler 机制
本质:在某个指定的运行中的线程上执行代码
思路:在接受任务的线程上执行循环判断
基本实现:
Thread 里 while 循环检查,加上 Looper(优势在于⾃定义 Thread 的代码可以少写很多),再加上 Handler(优势在于功能分拆,而且可以有多个 Handler)。
Java 的 Handler 机制:
HandlerThread:具体的线程
Looper:负责循环、条件判断和任务执⾏
Handler:负责任务的定制和线程间传递

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

    /**
    * 源码分析:此处调用的是父类(Thread类)的start(),最终回调HandlerThread的run()
    */ 
    @Override
    public void run() {
        // 1. 获得当前线程的id
        mTid = Process.myTid();
        // 2. 创建1个Looper对象 & MessageQueue对象
        Looper.prepare();
        // 3. 通过持有锁机制来获得当前线程的Looper对象
        synchronized (this) {
            // 发出通知:当前线程已经创建mLooper对象成功
            // 此处主要是通知getLooper()中的wait()
            mLooper = Looper.myLooper();
            notifyAll();
             // 此处使用持有锁机制 + notifyAll() 是为了保证后面获得Looper对象前就已创建好Looper对象
        }
        // 4. 设置当前线程的优先级
        Process.setThreadPriority(mPriority);
        // 5. 在线程循环前做一些准备工作 ->>分析1
        // 该方法实现体是空的,子类可实现 / 不实现该方法
        onLooperPrepared();
        // 6. 进行消息循环,即不断从MessageQueue中取消息 & 派发消息
        Looper.loop();
        mTid = -1;
    }
    
    public Looper getLooper() {
        // 若线程不是存活的,则直接返回null
        if (!isAlive()) {
            return null;
        }
        // 若当前线程存活,再判断线程的成员变量mLooper是否为null
        // 直到线程创建完Looper对象后才能获得Looper对象,若Looper对象未创建成功,则阻塞
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    // 此处会调用wait方法去等待
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        // 上述步骤run()使用 持有锁机制 + notifyAll()  获得Looper对象后
        // 则通知当前线程的wait()结束等待 & 跳出循环
        // 最终getLooper()返回的是在run()中创建的mLooper对象
        return mLooper;
    }

    @NonNull
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }
    
    // 方式1:quit() -> MessageQueue.quit(boolean safe)
    // 特点:效率高,但线程不安全
    /**
    * MessageQueue.quit(false) -> removeAllMessagesLocked()
    * 原理:遍历Message链表、移除所有信息的回调 & 重置为null
    */ 
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
    // 方式2:quitSafely() -> MessageQueue.quit(boolean safe)
    // 特点:效率低,但线程安全
    /**
    * MessageQueue.quit(true) -> removeAllFutureMessagesLocked() 
    * 原理:先判断当前消息队列是否正在处理消息
    *      a. 若不是,则类似分析2移除消息
    *      b. 若是,则等待该消息处理处理完毕再使用分析2中的方式移除消息退出循环
    * 结论:退出方法安全与否(quitSafe() 或 quit()),在于该方法移除消息、退出循环时是否在意当前队列是否正在处理消息
    */ 
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

}

AsyncTask:

AsyncTask 的内存泄露
众所周知的原因: AsyncTask 持有外部 Activity 的引用
没提到的原因:执行中的线程不会被系统回收
Java 回收策略:没有被 GC Root 直接或间接持有引用的对象,会被回收。
GC Root:(1. 运行中的线程 2. 静态对象 3. 来自 native code 中的引用)

所以:AsyncTask 的内存泄露,其他类型的线程方案(Thread、 Executor、HandlerThread)⼀样都有,所以不要忽略它们,或者认为 AsyncTask 比别的方案更危险。并没有。
就算是使⽤ AsyncTask,只要任务的时间不⻓(例如 10 秒之内),那就完全没必要做防止内存泄露的处理。
AsyncTask原理及不足

Service 和 IntentService

Service:后台任务的活动空间。适用场景:音乐播放器等。
IntentService:执行单个任务后自动关闭的 Service
IntentService原理

Executor、 AsyncTask、 HandlerThead、 IntentService 的选择

原则:哪个简单用哪个,能用 Executor 就用 Executor。
需要用到「后台线程推送任务到 UI 线程」时,再考虑 AsyncTask 或者 Handler。

HandlerThread 的使⽤场景:原本它设计的使⽤场景是「在已经运⾏的指定线程上执行代码」,但现实开发中,除了主线程之外,几乎没有这种需求,因为 HandlerThread 和 Executor相比在实际应⽤中并没什么优势,反而用起来会麻烦⼀点。

IntentService:首先,它是⼀个 Service;另外,它在处理线程本身,没有比 Executor 有任何优势。

关于 Executor 和 HandlerThread 的关闭

如果在界面组件⾥创建 Executor 或者 HandlerThread,记得要在关闭的时候(例如Activity.onDestroy() )关闭 Executor 和 HandlerThread。

@Override
protected void onDestroy() {
super.onDestroy();
executor.shutdown();
}
@Override
protected void onDestroy() {
super.onDestroy();
handlerThread.quit();
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容