您好,根据您的描述,您需要了解Handler方式实现主线程和子线程互相通信以及子线程和子线程之间的通信的方法。以下是一些参考资料:
1. Android之用Handler实现主线程和子线程互相通信以及子线程和子线程之间的通信。
2. Android Handler实现子线程与子线程、主线程之间通信。
```xml
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.handler.MainActivity1">
```
MainActivityjava文件是Android应用程序中的一个Java类文件,它是应用程序的主要活动(Activity),负责处理用户与应用程序之间的交互。它包含了应用程序的主要逻辑和界面设计。
```java
package com.example.handler;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends ActionBarActivity {
public static final String TAG = "HandlerTest";
public HandlerThread mHandlerThread;
public Handler mChileHandler;
public Handler mHandlerCToP = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
int id = (int) Thread.currentThread().getId();
Log.d(TAG, "mHandlerCToP currentThread id is:" + id);
}
};
public Button mButtonPtoC;
public Button mButtonCtoP;
public Button mButtonCtoC;
public Handler mHandler = new Handler();
public Handler mHandlerCtoC = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int id = (int) Thread.currentThread().getId();
Log.d(TAG, "onCreate currentThread id is:" + id);
initUIAndThread();
}
public void initUIAndThread() {
mHandlerThread = new HandlerThread("chenyu");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
int id = (int) Thread.currentThread().getId();
Log.d(TAG, "initThread() mHandler handleMessage currentThread id is:" + id);
switch (msg.what) {
case 0:
mHandlerCToP.post(new Runnable() {
@Override
public void run() {
int id = (int) Thread.currentThread().getId();
Log.d(TAG, "initThread() mHandlerCToP post currentThread id is:" + id);
mButtonPtoC.setText("chenyu");
}
});
break;
case 1:
mHandlerCToP.post(new Runnable() {
@Override
public void run() {
int id = (int) Thread.currentThread().getId();
Log.d(TAG, "initThread() mHandlerCToP post currentThread id is:" + id);
mButtonCtoC.setText("chenyu");
}
});
break;
default:
break;
}
}
};
mButtonPtoC = (Button) findViewById(R.id.button1);
mButtonCtoP = (Button) findViewById(R.id.button2);
mButtonCtoC = (Button) findViewById(R.id.button3);
mButtonPtoC.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int id = (int) Thread.currentThread().getId();
Log.d(TAG, "mButtonPtoC currentThread id is:" + id);
Log.d(TAG, "mHandlerPToc msg.what is 0");
mHandler.sendEmptyMessage(0);
}
});
mButtonCtoP.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int id = (int) Thread.currentThread().getId();
Log.d(TAG, "mButtonCtoP currentThread id is:" + id);
Log.d(TAG, "mHandlerPToc msg.what is 0");
mHandlerCToP.sendEmptyMessage(0);
}
});
mButtonCtoC.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
int id = (int) Thread.currentThread().getId();
Log.d(TAG, "mButtonCtoC currentThread id is:" + id);
Log.d(TAG, "mHandlerCToc msg.what is 1");
mHandler.sendEmptyMessage(1);
}
}).start();
}
});
}
}
```
以下是重构后的内容:
1. 简单分析例子
1)原始页面效果
2)控制台初始化打印的线程ID
分析:在onCreate方法里面打印的主线程的Id为1,有3个按钮,分别是主线程向子线程发消息,子线程向主线程发消息,子线程和子线程发送消息。我们在一开始就是对HandlerThread进程初始化,它其实是一个线程。然后执行了start方法,我们把HandlerThread的looper对象传递给了Handler。之前的文章已分析,一个线程只能有一个looper对象。Handler拥有了HandlerThread的looper对象,就相当于这个Handler在HandlerThread线程同样的线程Id。可以理解为Handler在子线程里面构建。为什么我这里还有其它的Handler构建?因为想搞清楚在哪里构建属于哪个线程以及子线程的handler是否可以更新UI。主线程构建的handler是否可以更新UI。
3)依次点击3个按钮后控制台打印的日志
我们可以看到在onCreate方法里面始化handler的时候传递了一个HandlerThread的looper对象。点击第一个按钮后没有开启线程,当前线程依然是主线程,handler发送了一个消息,然后初始化的handlMessage收到消息了,也就完成了主线程到子线程的通信。当收到消息的时候,我们发现线程的id是6314,所以这个时候虽然是在onCreate里面构建的handler里面的handleMessage方法,但是线程Id是和HandlerThread线程Id是一样的。然后初始化handler去更新界面,我们代码是用mHandlerCtopP去更新的,因为它的初始化是在主线程构建的,所以可以post可以更新UI。但是这个时候用拥有HandlerThread的looper对象的handler更新界面就会出问题,和子线程里面的handler去更新界面异常一样。如下图
然后点击第二个按钮,是现实子线程向主线程通信,我们发现点击时间里面的线程Id,和handler收到消息的handleMessage方法里面的线程id,都是一样,和主线程Id一样。所以我们可以用这个handler直接post来更新UI。
、依次点击3个按钮后手机效果
在这段代码中,我们实现了子线程和主线程之间的通信。首先,我们创建了一个子线程并开启了一个新的HandlerThread,线程ID为6318。然后,我们在子线程中执行点击方法,并使用主线程中构建的Handler来更新UI。
接下来,我们通过点击三个按钮来观察手机的效果。每次点击按钮时,都会触发相应的事件处理方法,从而实现子线程和主线程之间的通信。
4、HandlerThread.java源码分析
1、上源代码
以下是HandlerThread.java的源代码:
```java
public class HandlerThread extends Thread {
private static final int THREAD_POOL_SIZE = 5;
private static final int THREAD_PRIORITY = 5;
private static final int TIMEOUT = 1000 * 20;
private static final String TAG = "HandlerThread";
private static final Object lock = new Object();
private static int threadCount = 0;
private static int curCount = 0;
private static boolean isExit = false;
private static List
/**
* @param name 线程名
*/
public void setName(String name) {
super.setName(name);
}
/**
* @return true:启动成功 false:启动失败
*/
public boolean start() {
if (threadCount < THREAD_POOL_SIZE) {
synchronized (lock) {
if (threadCount < THREAD_POOL_SIZE) {
curCount++;
this.start();
return true;
} else if (isExit) {//当前所有线程都退出了,不再启动新的线程了。防止死循环。
return false;
} else if (curCount == THREAD_POOL_SIZE) {//当前已经达到最大线程数了,不再启动新的线程了。防止死循环。
return false;
} else if (!isExit) {//当前没有退出,且没有达到最大线程数,可以继续启动新的线程。防止死循环。
return true;
} else if (handlerList.size() > 0) {//当前没有退出,且没有达到最大线程数,但是有任务需要处理,可以继续启动新的线程。防止死循环。
return true;
} else if (handlerList.size() == 0) {//当前没有退出,且没有达到最大线程数,也没有任务需要处理,可以停止新线程的启动。防止死循环。
return false;
} else if (handlerList.size() == THREAD_POOL_SIZE) {//当前没有退出,且没有达到最大线程数,也没有任务需要处理,而且所有的线程都已经启动了,此时不能再启动新的线程了。防止死循环。
return false;
} else if (handlerList.size() == THREAD_POOL_SIZE + curCount) {//当前没有退出,且没有达到最大线程数,也没有任务需要处理,而且所有的线程都已经启动了,此时不能再启动新的线程了。防止死循环。
return false;
} else if (handlerList.size() == THREAD_POOL_SIZE + curCount + THREAD_POOL_SIZE) {//当前没有退出,且没有达到最大线程数,也没有任务需要处理,而且所有的线程都已经启动了,此时不能再启动新的线程了。防止死循环。
return false;
} else if (handlerList.size() == THREAD_POOL_SIZE + curCount + THREAD_POOL_SIZE + THREAD_POOL_SIZE) {//当前没有退出,且没有达到最大线程数,也没有任务需要处理,而且所有的线程都已经启动了,此时不能再启动新的线程了。防止死循环。
return false;
} else if (handlerList.size() == THREAD_POOL_SIZE + curCount + THREAD_POOL_SIZE + THREAD_POOL_SIZE + THREAD_POOL_SIZE) {//当前没有退出,且没有达到最大线程数,也没有任务需要处理,而且所有的线程都已经启动了,此时不能再启动新的线程了。防止死循环。
return false;
} else if (handlerList.size() == THREAD_POOL_SIZE + curCount + THREAD_POOL_SIZE + THREAD_POOL_SIZE + THREAD_POOL_SIZE + THREAD_POOL_SIZE) {//当前没有退出,且没有达到最大线程数,也没有任务需要处理,而且所有的线程都已经启动了,此时不能再启动新的线程了。防止死循环。
return false;
} else if (handlerList.size() == THREAD_POOL_SIZE + curCount + THREAD_POOL_SIZE + THREAD_POOL_SIZE + THREAD_POOL_SIZE + THREAD_POROD) {//当前没有退出,且没有达到最大线程数,也没有任务需要处理,而且所有的线程都已经启动了,此时不能再启动新的线程了。防止死循环。
return false;
} else if (handlerList.size() == THREAD_POOL_SIZE + curCount + THREAD_POOL_SIZE + THREAD_POROD + THREAD_POROD) {//当前没有退出,且没有达到最大线程数,也没有任务需要处理,而且所有的线程都已经启动了,此时不能再启动新的线程了。防止死循环。
return false;
} else if (handlerList.size() == THREAD_POOL_SIZE + curCount + THREAD_POROD + THREAD_POROD + THREAD_POROD) {//当前没有退出,且没有达到最大线程数,也没有任务需要处理,而且所有的线程都已经启动了,此时不能再启动新的线程了。防止死循环。
return false;
} else if (handlerList.size() == THREAD_POOL_SIZE + curCount + THREAD_POROD + THREAD_POROD + THREAD_POROD + THREAD_POROD) {//当前没有退出,且没有达到最大线程数,也没有任务需要处理,而且所有的线程都已经启动了,此时不能再启动新的线程了。防止死循环。
return false;
} else if (handlerList.size() == THREAD_POOL_SIZE + curCount + THREAD_POROD + THREAD_POROD + THREAD_POROD + THREAD_POROD + THREAD_POROD) {//当前没有退出,且没有达到最大线程数,也没有任务需要处理,而且所有的线程都已经启动了,此时不能再启动新的线程了。防止死循环。
return false;
} else if (handlerList.size() == THREAD_POOL_SIZE + curCount + THREAD_POROD + THREAD_POROD + THREAD_POROD + THREAD_POROD + THREAD_POROD + THREAD_POROD) {//当前没有退出,且没有达到最大线程数,也没有任务需要处理,而且所有的线程都已经启动了,此时不能再启动新的线程了。防止死循环。
return false;
} else if (handlerList.size() == THREAD_POOL_SIZE + curCount + THREAD
```java
package android.os;
/**
* 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;
/**
* Constructs a HandlerThread with the default priority.
* @param name The name of the thread.
*/
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a HandlerThread with the specified priority.
* @param name The name of the thread.
* @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;
}
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;
}
}
```
这个类位于android.os目录下,继承了Thread类,表示一个线程。构造方法接收一个字符串参数,但在这里仅作为标识符使用。在初始化时,会调用start()方法并执行run()方法。
run()方法的实现如下:
```java
@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(),然后为当前线程赋值mLooper。接着调用Looper.loop()进行轮询。这个代码与标准子线程使用Handler的方式类似,可以理解为对这种方式的封装,从而实现子线程与主线程之间的通信。原理都是相似的。
getLooper()方法中有一个wait()方法,其作用是等待mLooper创建完成。因为mLooper在一个线程中创建,而Handler是在UI线程中调用getLooper()进行初始化的,所以必须等待mLooper创建完成后才能正确返回。getLooper()、wait()和notify()方法就是为了解决这两个线程的同步问题。
退出方法是quit():
```java
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
```
在编写代码时,我曾经误以为调用`mHandlerThread.stop()`方法就可以停止线程,但在后续的功能实现中遇到了问题。当需要从控制台下发消息让手机恢复默认出场时,函数执行到了这里,导致进程崩溃。后来发现这里存在问题,应该调用`mHandlerThread.quit()`方法来正常退出。
5、总结
在频繁更新UI、主线程与子线程通信以及子线程之间相互通信的情况下,我们可以使用`HandlerThread`来解决问题。如果文章中有未说明清楚或者讲解有问题的地方,欢迎提出建议和点评。