Handler在Thread使用
在子线程中使用handler实例:
public class MainActivity extends Activity {
private static final String TAG = "MainAty";
@BindView(R.id.tvText)
TextView tvText;
private Handler threadHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
new Thread(
new Runnable() {
@Override
public void run() {
//在子线程中创建handler
Looper.prepare();
threadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.i(TAG, "threadHandler receive msg");
break;
}
}
};
Looper.loop();
Log.i(TAG, "thread exit");
}
}
, "thread-1").start();
}
@OnClick(R.id.tvText)
public void ClickTextView(){
//子线程创建的handler发送消息
threadHandler.sendMessage(threadHandler.obtainMessage(1));
}
@Override
protected void onDestroy() {
super.onDestroy();
//退出时候需要注意Loop还在死循环,需要退出,不然子线程不会退出
threadHandler.getLooper().quit();
}
}
可以看到,在子线程中创建handler需要注意,首先如果没有Looper.prepare();
那么直接创建会抛出异常,异常大概如下
java.lang.RuntimeException: Can't create handler inside thread Thread[thread-1,5,main] that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:205)
此异常跟进源码发现:
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
//抛出异常的点
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
}
那分析Looper.prepare到底干了啥Looper.myLooper:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
Looper.prepare会new Looper()并且挂在到ThreadLocal上,于是当前线程绑定了创建的Looper对象;而Looper.myLooper就是根据当前线程拿到创建的Looper;
至此如果在子线程创建handler一定需要Looper.prepare;
而Looper.loop()是进入一个死循环中,保证当前线程不会退出,如果退出了,那发生的Message消息也没办法处理到,源码位置:
public static void loop() {
...
//死循环,保证不会退出,否则发送的handler消息处理不到
for (;;) {
Message msg = queue.next(); // might block
//没有msg时候退出
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
}
...
}
当然这里循环不会造成CPU资源过度消耗,因为queue.next调用nativePollOnce会阻塞线程;但如果Activity退出时候,还是需要将Loop.quit方法去停止死循环,保证不会内存泄露,资源回收及时,如果Loop.quit源码;
public void quit() {
mQueue.quit(false);
}
void quit(boolean safe) {
...
//移除所有的Message,这样queue.next == null;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
//queue.next调用nativePollOnce,这里在唤醒
nativeWake(mPtr);
}
}
HandlerThread
Android考虑到子线程实现这种方式比较繁琐,因此创建了一个HandlerThread类:
public class HandlerThreadActivity extends Activity {
private static final String TAG = "HandlerThreadAty";
@BindView(R.id.tvText)
TextView tvText;
private Handler threadHandler;
private HandlerThread thread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
thread = new HandlerThread("handler-thread");
thread.start();
threadHandler = new Handler(thread.getLooper()){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.i(TAG, "threadHandler receive msg");
break;
}
}
};
}
@OnClick(R.id.tvText)
public void ClickTextView(){
//子线程创建的threadHandler发送消息
threadHandler.obtainMessage(1).sendToTarget();
}
@Override
protected void onDestroy() {
super.onDestroy();
//退出时候需要注意HandlerThread.quit保证退出
thread.quit();
}
}
来分析一波HandlerThread源码:
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;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
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;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
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;
}
public int getThreadId() {
return mTid;
}
}
HandlerThread是继承Thread,说白了就是一个单线程,串型执行,执行效率不高,其中有2个构造方法,
public HandlerThread(String name):只需要指定线程名
public HandlerThread(String name, int priority) :指定线程名和线程优先级;
public void run():run方法会调用Looper.prepare()将当前线程和new Looper绑定在ThreadLocal中,然后Looper.myLooper取出放在mLooper;而getLooper就是返回mLooper,也就是返回HandlerThread线程在run方法创建的那个Looper对象;
public Handler getThreadHandler():getThreadHandler这个方法对外不公开;
public boolean quit/quitSafely():调用looper.quit/quitSafely退出;
IntentService
IntentService是由handlerThread+Handler+Service组成
分析其源码:
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
public IntentService(String name) {
super();
mName = name;
}
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
mServiceLooper.quit();
}
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
其在oncreate时候创建一个HandlerThread,并且利用HandlerThread线程的Looper创建了一个mServiceHandler;
ondestory时候执行mServiceLooper.quit()退出Looper的循环;
onStartCommand方法中调用onStart,在onStart方法中利用mServiceHandler发送消息,其中msg.arg1会存startId;
当ServiceHandler收到消息,执行handleMessage后,调用onHandleIntent,最后stopSelf(startId);
因此说IntentService一个异步Service,其onHandleIntent是在子线程中执行的,并且执行完毕后,会destory self;
比如startService启动3次,假设在onStartCommand中startId分别为1,2,3,分别调用stopself(1),stopself(2),stopself(3);
来看为什么stopself没有走到onDestory方法,分析源码:
//Service#stopSelf
public final void stopSelf(int startId) {
...
mActivityManager.stopServiceToken(
new ComponentName(this, mClassName), mToken, startId);
...
}
//AMS#stopServiceToken
@Override
public boolean stopServiceToken(ComponentName className, IBinder token,
int startId) {
synchronized(this) {
return mServices.stopServiceTokenLocked(className, token, startId);
}
}
//AMS#stopServiceTokenLocked
boolean stopServiceTokenLocked(ComponentName className, IBinder token,
int startId) {
ServiceRecord r = findServiceLocked(className, token, UserHandle.getCallingUserId());
if (r != null) {
if (startId >= 0) {
...
//这里判断如果上次startId和这次stop的startId不等,直接退出
if (r.getLastStartId() != startId) {
return false;
}
...
}
...
//真正StopService的地方
bringDownServiceIfNeededLocked(r, false, false);
...
return true;
}
return false;
}
因此startId分别启动1,2,3时候,mLastStartId = 3,只有调用stopSelf(3)时候才会进入到onDestory生命周期;
这里需要注意如下几点:
IntentService只有一个线程,如果多次start,onCreate只会执行一次,onStartCommand会执行多次,那handler发送的消息会有多个,消息队列会依次执行回调onHandleIntent,等所有消息都依次分发执行完毕后,执行完后最后调用到onDestory生命周期,kill self;此外只能通过startService启动,不能通过bindService启动,原因是生命周期onStartCommand不会在bindService中回调;