1:初始化,子线程和主线程通讯流程分析
1:Looper的初始化有两种,一种是prepareMainLooper()和prepare(),prePareMainLooper是在主线程中创建的Looper,是在ActivityThread的main方法中初始化的
public static void main(String[] args) {
...省略一些代码
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...省略一些代码
Looper.loop();
下面是是prepareMainLooper()方法源码
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
下面是prepare()方法源码
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));
}
prepareMainLooper()中其实也是调用的prepare(boolean isquit),当时默认是是不能quit(quit就是退出looper的循环),prepare方法的实现就是把当前的线程和Looper绑定起来;
2:下面看下Looper的构造方法,和MessageQueue的构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
...
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
基本上没有什么参数,就是把前面设置进来的quitAllowed分别传递到Looper和MessageQueue
3:启动Looper.loop()方法(ActivityThrea的main方法中系统给我调用了),looper找到messagequeue,链表循环next()去处message,调用message.target.dispatchMessage(msg),调用完之后message销毁放回到message池中.
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//注释1
...省略
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...省略
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...省略
msg.recycleUnchecked();
target就是message发送出去的handler.源码如下
跟踪handler的sendmessage(msg),后来会走到sendMessageAtTime(Message msg, long uptimeMillis)
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
看下enqueueMessage(queue, msg, uptimeMillis);
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //注释
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
this就是当前的handler的,赋值给了message的target
4:下面看下handler的dispatchMessage(Message msg);
public void dispatchMessage(Message msg) {
if (msg.callback != null) { // 注释1 :这里是Handler.post(runnable)
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
在这里可以看出如果优先设置callback就走callback实在没有设置就走handlemessage(msg)(这个是新版本google放弃的一个Handler的创建方式);注释一表明,runnable的处理优先级是高于message的.到这里子线程和主线程通讯过场算是完成了.
2:handler内存泄漏导致的原因和怎么避免内存泄漏;
handler内存泄漏主要是因为handler发送出去的Message或者是runnable都MessageQueue中(按照发送出来的时间先后顺序,执行也是通过loop死循环按照时间的先后(存放)取出来交给handler执行,如果当前界面销毁了但是messagequeue中的这个message或者是runnable没有执行,当执行到这个message的时候引用到之前类的资源就会出错.)
Handler handler1 = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == 0) {
startActivity(new Intent(MainActivity.this,SecondActivity.class));
}
return false;
}
});
private void test() {
new Thread(new Runnable() {
@Override
public void run() {
// SystemClock.sleep(3000);
Message message = handler1.obtainMessage();
message.what = 0;
handler1.sendMessageDelayed(message,3000);
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e(TAG,">>>>销毁了");
}
比如上面的场景,延时3秒钟发送跳转到secondActivity的message,在3秒钟的时间没销毁了当前的Activity推到了桌面,但是执行这个message会跳转到secondAcivity.解决方案.在ondestroy方法中handler1= null ;在handler1.sendMessageDelayed(message,3000)之前给给!=null的判断就可以避免;
子线程中可以new Handler()吗?
我的理解是可以的,不过在创建Handler之前要创建一个子线程的Looper对象,然后通过这个looper对象去创建Handler,但是自己创建的Looper对象是个异步操作可以没有没有创建完成就去创建Handler也会报错,当然系统是给我创建了一个handlerThread(其实是一个子线程,在run方法中创建一个looper通过唤醒机制)
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
因此在子线程创建一个Handler方法如下:
HandlerThread thread = new HandlerThread("thread-1");
thread.start();
Looper looper = thread.getLooper();
Handler handler = new Handler(looper);
注意:一定要在run之后再获取looper对象,因为run方法就是创建looper对象;
更新ui的操作一定是在主线程吗?
对于这个问题肯定很多人想到的是肯定的,其实这句话也不完全对.比如
textView.setText();有时候在子线程运行是不会报错的;(有时候是viewrootimpl对象还没有创建的时候)
如果我们在更新ui之前做延时就会报错"Only the original thread that created a view hierarchy can touch its views." 我们知道android更新界面会从新requestLayout.(比如textview.settext()中会有个checkForRelayout()里面会掉view.requestlayout)我们看下view的reqeustLayout()
public void requestLayout() {
if (mMeasureCache != null) mMeasureCache.clear();
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
// Only trigger request-during-layout logic if this is the view requesting it,
// not the views in its parent hierarchy
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null && viewRoot.isInLayout()) {
if (!viewRoot.requestLayoutDuringLayout(this)) {
return;
}
}
mAttachInfo.mViewRequestingLayout = this;
}
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout(); //注释1
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
viewrootImpl是viewparent的实现类,注释1处调了之后代码会走到viewrootimpl来的requstLayout()
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
看下checkThread()
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
思路清晰了android更新界面 view.requestLayout()-viewparent.requestlayout()-viewrootimpl
.requestLayout-checkThread().如果当前线程不是主线程想去更新ui就会抛出上面的异常;
那么程序刚开始的时候为什么在子线程中更新ui没有报错了,原因只有一个那个时候viewrootimpl对象还没有创建成功;我们在Activity的生命周期创建过程中防线viewrootimpl是在onresume之后才创建成功的.(下次分析下acitivity的启动原理)