android view 消毁 android thread 销毁

admin2024-06-01  10

1. 简述

Android中耗时操作不能放在主线程,执行耗时操作都需要开启子线程来执行,执行完线程以后线程都会自动销毁。如果经常要开启线程,接着又销毁线程,这是很消耗性能的。可以选择的方案有:

a. 使用线程池 (线程池的相关介绍可以参考之前的文章:Java线程池)

b. 直接创建子线程(创建线程的方式可以参考之前的文章:Java创建线程的三种方式)

c. 使用 HandlerThread

2. HandlerThread的使用

a. 创建HandlerThread的实例对象;

b. 启动创建的HandlerThread线程;

c. 创建Handler对象,将HandlerThread的Lopper作为参数,完成 Handler对象与HandlerThread的Looper对象的绑定。

3. 实例

在布局中定义一个按钮来开启异步操作,一个TextView来显示执行异步操作后的消息变化。

布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/mTv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="100dp"
        android:text="Hello World!" />

    <Button
        android:id="@+id/mBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/mTv"
        android:layout_marginTop="80dp"
        android:text="点击按钮" />
</RelativeLayout>

Activity以及对应的逻辑:

package cn.zzw.messenger.handlerthreaddemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.lang.ref.WeakReference;

public class MainActivity extends AppCompatActivity {
    TextView mTv;
    Button mBtn;
    Handler mWorkHandler;
    Handler mUiHandler;
    int count = 0;

    /**
     * 创建主线程的Handler
     */
    private static class UiHandler extends Handler {
        WeakReference<MainActivity> reference;

        public UiHandler(MainActivity activity) {
            reference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            if (null != reference) {
                MainActivity activity = (MainActivity) reference.get();
                if (null != activity) {
                    //主线程接收到消息后,更新UI
                    activity.mTv.setText("" + activity.count);
                }
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mUiHandler = new UiHandler(this);
        //创建 HandlerThread 的实例对象
        HandlerThread mHandlerThread = new HandlerThread("zuowei.zhang");
        //启动创建的 HandlerThread 线程
        mHandlerThread.start();
        //创建工作线程的Handler对象,将HandlerThread的Lopper作为参数,完成 Handler对象与HandlerThread的Looper对象的绑定
        mWorkHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                try {
                    //在工作线程的Handler的handleMessage中执行耗时操作
                    Thread.sleep(5000);
                    count += 1000;
                    //耗时操作完毕后,通过主线程的Handler,将消息发送到主线程。
                    mUiHandler.sendEmptyMessage(10011);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        mTv = findViewById(R.id.mTv);
        mBtn = findViewById(R.id.mBtn);
        mBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //通过工作线程的Handler发送消息到其绑定的消息队列
                mWorkHandler.sendEmptyMessage(10086);
            }
        });
    }
}

效果:

连续点击4次,发现每5秒,count的值增加1000,并显示在TextView上。

android view 消毁 android thread 销毁,android view 消毁 android thread 销毁_android,第1张

从上面的动画中可以发现:连续点击4下时,并无按照最新点击的按钮操作显示,而是按顺序的一个个显示出来。这是因为使用HandlerThread时只是开了一个工作线程,当点击了n下后,只是将n个消息发送到消息队列MessageQueue里排队,等候派发消息给Handler再进行对应的操作。

4. 源码分析

先上下HandlerThread的源码,加上自带的注释,总共不到200行的代码:

package android.os;

import android.annotation.NonNull;
import android.annotation.Nullable;

/**
 * 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;
    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() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    
    /**
     * 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;
    }

    /**
     * @return a shared {@link Handler} associated with this thread
     * @hide
     */
    @NonNull
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }

    /**
     * Quits the handler thread's looper.
     * <p>
     * Causes the handler thread's looper to terminate without processing any
     * more messages in the message queue.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p class="note">
     * Using this method may be unsafe because some messages may not be delivered
     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
     * that all pending work is completed in an orderly manner.
     * </p>
     *
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     *
     * @see #quitSafely
     */
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

    /**
     * Quits the handler thread's looper safely.
     * <p>
     * Causes the handler thread's looper to terminate as soon as all remaining messages
     * in the message queue that are already due to be delivered have been handled.
     * Pending delayed messages with due times in the future will not be delivered.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p>
     * If the thread has not been started or has finished (that is if
     * {@link #getLooper} returns null), then false is returned.
     * Otherwise the looper is asked to quit and true is returned.
     * </p>
     *
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     */
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}

当我们使用的时候调用如下代码:

HandlerThread mHandlerThread = new HandlerThread("zuowei.zhang");
//启动创建的 HandlerThread 线程
mHandlerThread.start();

先看下它的构造方法:

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

 有两个构造方法,name代表当前线程的名称,priority为线程的优先级别。

当创建 HandlerThread 对象后,调用 start() 方法开启线程,执行的是线程的 run() 方法:

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

在此方法中,首先调用 Looper.prepare() 去初始化 Looper对象,并持有锁机制来获得当前线程的Looper对象。并且调用了 Looper.loop() ,开启循环。对于Looper 的相关介绍可以参考之前的文章 Android Handler 消息机制 。

在执行 Looper.loop() 方法之前,执行了 onLooperPrepared() :

/**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }

此方法的实现是一个空的,用户可以在子类中实现该方法。该方法的作用是在线程loop之前做一些初始化工作。也可以不实现该方法,取决于具体的需求。

当创建 Handler 对象后,会调用 HandlerThread 的 getLooper() 方法,并将其作为参数放入 Handler 中。

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

首先判断此工作线程是否存活,如果没有存活直接返回 null 。如果当前线程存活的,接着判断线程中的 mLooper 是否为null,如果为null,说明当前线程已经创建成功。如果当前线程存活的,接着判断线程中的 mLooper 是否为null,如果为null,说明当前线程已经创建成功,只是还没创建Looper对象,就会调用wait方法去等待 mLooper 的创建。当 run() 方法执行后,并且初始化 mLooper 对象后,就会调用 notifyAll() 方法来通知 wait() 方法等待结束,跳出循环,获得mLooper对象的值。当 mLooper 对象获得后,接下来执行的流程可以参考之前的文章:Android Handler 消息机制。

接下来继续看剩下的方法:

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

从这两个方法的方法名可以看出,当前线程退出循环的方法,一种是安全的,一中是不安全的。两者的区别在于方法内部一个指向的是 looper.quit() 和 looper.quitSafely() 。

在 Looper.java 类中:

public void quit() {
        mQueue.quit(false);
    }


    public void quitSafely() {
        mQueue.quit(true);
    }

最终它们调用的都是 MessageQueue.java类中的方法: quit(boolean safe),区别在于传入的参数值。

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 的作用在于调用不同的方法,当 safe 为 true 的时候调用的是 removeAllFutureMessagesLocked()方法 ;当 safe值为 false 的时候调用了 removeAllMessagesLocked() 方法。

方法 removeAllMessagesLocked():

private void removeAllMessagesLocked() {
        Message p = mMessages;
        while (p != null) {
            Message n = p.next;
            p.recycleUnchecked();
            p = n;
        }
        mMessages = null;
    }

这个方法其实就是遍历Message链表,移除所有信息的回调,并重置为null。 

方法 removeAllFutureMessagesLocked():

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

这个方法,它会根据Message.when这个属性,判断当前消息队列是否正在处理消息,没有正在处理消息的话,直接移除所有回调,正在处理的话,等待该消息处理处理完毕再退出该循环。

因此说 quitSafe() 是安全的,而 quit() 方法是不安全的,因为quit方法不管是否正在处理消息,直接移除所有回调。

5. 总结

a. HandlerThread适用于构建循环线程。

b. 在创建Handler作为HandlerThread线程消息执行者的时候必须调用start方法之后,因为创建Handler需要的Looper参数是从HandlerThread类中获得,而Looper对象的赋值又是在HandlerThread的run方法中创建。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明原文出处。如若内容造成侵权/违法违规/事实不符,请联系SD编程学习网:675289112@qq.com进行投诉反馈,一经查实,立即删除!