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();
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容