[Android]HandlerThread

本文深入解析HandlerThread的工作原理,包括其特点、使用步骤及内部机制,如Looper、MessageQueue的交互,帮助理解Android异步处理机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HandlerThread特点

  • HandlerThread本身是一个线程类,它继承了Thread
  • HandlerThread有自己内部Looper对象,可以进行Looper循环
  • 通过获取HandlerThread的Looper对象并传递给Handler对象,可以在handleMessage方法中执行异步任务
  • 创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象

使用步骤 

1.创建实例对象

HandlerThread handlerThread = new HandlerThread("downloadImage");

传入参数的作用主要是标记当前线程的名字,可以任意字符串。 

2.启动HandlerThread线程

1.  //必须先开启线程
2.  handlerThread.start();

此时,ThreadHandler已经创建完并启动了线程。如何将耗时的异步任务放到HandlerThread线程中?

3.构建循环消息处理机制

/**
* 该callback运行于子线程
*/
class ChildCallback implements Handler.Callback {
    @Override
    public boolean handleMessage(Message msg) {
        //在子线程中进行相应的网络请求

        //通知主线程去更新UI
        mUIHandler.sendMessage(msg1);
        return false;
    }
}

4.构建异步handler

//子线程Handler
Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());

第3和第4步是构建一个可以用于异步操作的handler,并将前面创建的HandlerThread的Looper对象以及Callback接口类,作为参数传递给当前的handler,这样当前的异步handler就拥有了HandlerThread的Looper对象,由于HandlerThread本身是异步线程,因此Looper也与异步线程绑定,从而handlerMessage方法也就可以异步处理耗时任务了。Looper+Handler+MessageQueue+Thread异步循环机制构建完成。

示例代码

创建了两个handler,一个用于更新UI线程的mUIHandler,一个用于异步下载图片的childHandler。

public class HandlerThreadActivity extends AppCompatActivity {

    /**
     * 图片地址集合
     */
    private String url[] = {
            "https://img-blog.csdn.net/20160903083245762",
            "https://img-blog.csdn.net/20160903083252184",
            "https://img-blog.csdn.net/20160903083257871",
            "https://img-blog.csdn.net/20160903083257871",
            "https://img-blog.csdn.net/20160903083311972",
            "https://img-blog.csdn.net/20160903083319668",
            "https://img-blog.csdn.net/20160903083326871"
    };
    private ImageView imageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_thread);

        imageView = (ImageView) findViewById(R.id.image);
        //创建异步HandlerThread
        HandlerThread handlerThread = new HandlerThread("downloadImage");
        //必须先开启线程
        handlerThread.start();
        //子线程Handler  用于异步下载图片
        Handler childHandler = new Handler(handlerThread.getLooper(), new ChildCallback());
        for (int i = 0; i < 7; i++) {
            //每一秒去更新一个图片
            childHandler.sendEmptyMessageDelayed(i, 1000 * i);
        }
    }

    /**
     * 该callback运行于子线程
     */
    class ChildCallback implements Handler.Callback {
        @Override
        public boolean handleMessage(Message message) {
            //在子线程中进行网络请求
            Bitmap bitmap = downloadUrlBitmap(url[message.what]);
            ImageModel imageModel = new ImageModel();
            imageModel.url = url[message.what];
            imageModel.bitmap = bitmap;
            Message msg = new Message();
            msg.what = message.what;
            msg.obj = imageModel;
            //通知主线程去更新UI
            mUIHandler.sendMessage(msg);

            return false;
        }
    }

    private Bitmap downloadUrlBitmap(String urlString) {
        HttpURLConnection urlConnection = null;
        BufferedInputStream in = null;
        Bitmap bitmap = null;

        try {
            final URL url = new URL(urlString);
            urlConnection = (HttpURLConnection) url.openConnection();
            in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
            bitmap = BitmapFactory.decodeStream(in);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return bitmap;
    }

    /**
     * 用于更新UI
     */
    private Handler mUIHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            ImageModel model = (ImageModel) msg.obj;
            imageView.setImageBitmap(model.bitmap);
        }
    };

    class ImageModel {
        String url;
        Bitmap bitmap;

        ImageModel() {
        }
    }
}

源码分析

/**
 * Handy class for starting a new thread that has a looper. The looper can then be 
 * used to create handler classes. Note that start() must still be called.
 */
public class HandlerThread extends Thread {
    int mPriority;//线程优先级
    int mTid = -1;
    Looper mLooper;//当前线程持有的Looper对象
    private @Nullable Handler mHandler;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    
    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }

可以看出HandlerThread继承Thread,构造函数有两个,name指的是线程的名称,priority指的是线程优先级。成员变量mLooper就是HandlerThread自己持有的Looper对象。onLooperPrepared()该方法是一个空实现,必要时可以重写,注意重写时机是在Looper循环启动前。

@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()方法才能开启线程,即run()方法会被调用。可以看出,run()方法执行了Looper.prepare()代码,这时,Looper对象被创建,当Looper对象被创建后绑定在当前线程(也就是异步线程),这样才可以把Looper对象赋值给Handler对象,进而确保Handler对象中的hanleMessage方法是异步线程执行的。

接着执行代码:

synchronized (this) {
    mLooper = Looper.myLooper();
    notifyAll();
}

这里在Looper对象创建后将其赋值给HandlerThread的内部变量mLooper,并通过notifyAll()方法去唤醒等待线程,最后执行Looper.loop();代码,开启looper循环。这里为什么要唤醒等待线程呢?

/**
 * This method returns the Looper associated with this thread. If this thread not been started
 * or for any reason isAlive() returns false, this method will return null. If this thread
 * has been started, this method will block until the looper has been initialized.  
 * @return The looper.
 */
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;
}

在外部通过getLooper方法获取looper对象时会先判断当前线程是否启动了,如果线程已经启动,将会进入同步语句并判断Looper对象是否为null,为null则代表Looper对象还没有被复制,也就是还没被创建,此时当前调用线程进入等待阶段,直到Looper对象被创建并通过notifyAll()方法唤醒等待线程,最后才返回Looper对象。之所以需要等待唤醒机制,是因为Looper的创建是在子线程中执行的,而调用getLooper方法则是在主线程进行的,这样我们就无法保障我们在调用getLooper方法时Looper已经被创建。

所以在获取mLooper对象时会存在一个同步的问题,只有当线程创建成功并且Looper对象也创建成功之后才能获得mLooper的值,HandlerThread内部则通过等待唤醒机制解决了同步问题。

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

当调用quit时,其内部是调用Looper的quit方法,而最终执行的则是MessageQueue中的removeAllMessagesLocked方法,该方法主要把MessageQueue消息池中所有的消息全部清空,无论是延迟消息还是非延迟消息。

当调用quitSafely方法时,其内部调用的是Looper的quitSafely方法,而最终执行的是MessageQueue中的removeAllFutureMessagesLocked方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的费延迟消息派发出去让handler去处理完成后,才停止Looper循环。

quitSafely相比quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。Looper的quit方法基于API1,quitSafely方法基于API18。


转自:https://blog.csdn.net/javazejian/article/details/52426353

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值