您的位置:首页 > 移动开发 > Android开发

Android中HandlerThread介绍与使用案列

2018-04-02 19:01 465 查看

HandlerThread 产生背景

1、开启Thread子线程进行耗时操作

2、多次创建和销毁线程是很耗资源的

HandlerThread是什么

是handler+thread+looper结合,是一个thread内部有looper

特点:

- 本质上是一个线程类,继承了thread,

- 其内部有自己的Looper对象,可以进行looper循环,

- 通过获取handlerthread的looper对象传递给handler对象,可以在handlermessage方法中执行异步任务

- 优点是不会堵塞,减少对性能的消耗。缺点是不能同时进行多任务的处理,需要等待进行处理。处理效率比较低。

- 与线程池注重并发不同的是,handlerthread是一个串行队列,handlerthread背后只有一个队列。

Handler使用场景

handlerthread源码:

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

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

/**
* 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继承自Thread,因此在run()中的逻辑都是在子线程中运行的。

接下来就是两个关键的方法,run()和getLooper():

run()中可以看到是很简单的创建Looper以及让Looper工作的逻辑。

run()里面当mLooper创建完成后有个notifyAll(),getLooper()中有个wait(),这有什么用呢?因为的mLooper在一个线程中执行创建,而我们的handler是在UI线程中调用getLooper()初始化的。

也就是说,我们必须等到mLooper创建完成,才能正确的返回。getLooper();wait(),notify()就是为了解决这两个线程的同步问题。

使用案例:

public class DownloadThread extends HandlerThread implements Handler.Callback{
private final String TAG = this.getClass().getSimpleName();
private final String KEY_URL = "url";
public static final int TYPE_START = 1;
public static final int TYPE_FINISHED = 2;

private Handler mWorkerHandler;
private Handler mUIHandler;
private List<String> mDownloadUrlList;

public DownloadThread(final String name) {
super(name);
}

@Override
protected void onLooperPrepared() {    //执行初始化任务
super.onLooperPrepared();
mWorkerHandler = new Handler(getLooper(), this);    //使用子线程中的 Looper
if (mUIHandler == null) {
throw new IllegalArgumentException("Not set UIHandler!");
}

//将接收到的任务消息挨个添加到消息队列中
for (String url : mDownloadUrlList) {
Message message = mWorkerHandler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString(KEY_URL, url);
message.setData(bundle);
mWorkerHandler.sendMessage(message);
}
}

public void setDownloadUrls(String... urls) {
mDownloadUrlList = Arrays.asList(urls);
}

public Handler getUIHandler() {
return mUIHandler;
}

//注入主线程 Handler
public DownloadThread setUIHandler(final Handler UIHandler) {
mUIHandler = UIHandler;
return this;
}

/**
* 子线程中执行任务,完成后发送消息到主线程
*
* @param msg
* @return
*/
@Override
public boolean handleMessage(final Message msg) {
if (msg == null || msg.getData() == null) {
return false;
}

String url = (String) msg.getData().get(KEY_URL);
//下载开始,通知主线程
Message startMsg = mUIHandler.obtainMessage(TYPE_START, "\n 开始下载 @" + DateUtils.getCurrentTime() + "\n" + url);
mUIHandler.sendMessage(startMsg);

SystemClock.sleep(2000);    //模拟下载
//下载完成,通知主线程
Message finishMsg = mUIHandler.obtainMessage(TYPE_FINISHED, "\n 下载完成 @" + DateUtils.getCurrentTime() + "\n" + url);
mUIHandler.sendMessage(finishMsg);
return true;
}
@Override
public boolean quitSafely() {
mUIHandler = null;
return super.quitSafely();
}

}


MainActivity代码:

public class MainActivity extends AppCompatActivity implements Handler.Callback{

@BindView(R.id.tv_start_msg)
TextView mTvStartMsg;
@BindView(R.id.tv_finish_msg)
TextView mTvFinishMsg;
@BindView(R.id.btn_start_download)
Button mBtnStartDownload;

private Handler mUIHandler;
private DownloadThread mDownloadThread;

@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
init();
}

private void init() {
mUIHandler = new Handler(this);
mDownloadThread = new DownloadThread("下载线程");
mDownloadThread.setUIHandler(mUIHandler);
mDownloadThread.setDownloadUrls("下载资源一",
"下载资源二", "下载资源三");
}

@OnClick(R.id.btn_start_download)
public void startDownload() {
mDownloadThread.start();
mBtnStartDownload.setText("正在下载");
mBtnStartDownload.setEnabled(false);
}

//主线程中的 Handler 处理消息的方法
@Override
public boolean handleMessage(final Message msg) {
switch (msg.what) {
case DownloadThread.TYPE_FINISHED:
mTvFinishMsg.setText(mTvFinishMsg.getText().toString() + "\n " + msg.obj);
break;
case DownloadThread.TYPE_START:
mTvStartMsg.setText(mTvStartMsg.getText().toString() + "\n " + msg.obj);
break;
}
return true;
}
}


布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:padding="8dp">

<TextView
android:id="@+id/tv_start_msg"
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="下载开始信息"/>

<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorAccent"/>

<TextView
android:id="@+id/tv_finish_msg"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="8dp"
android:text="下载完成信息"/>

<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorAccent"/>

<Button
android:id="@+id/btn_start_download"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="开始下载"/>
</LinearLayout>


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: