
因为AndroID的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态,为什么不对UI控件加上锁机制?
首先加上锁会让UI访问的逻辑变得复杂;其次锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行AndroID是在哪儿校验UI *** 作是否是在UI线程?
//VIEwRootImpl.javapublic VIEwRootImpl(Context context, display display) { ... ... mThread = Thread.currentThread(); ... ... }//该方法会多个方法的开头被调用,例如requestLayout()、invalIDateChildInParent()等等... ...voID checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException("Only the original thread that created a vIEw hIErarchy can touch its vIEws."); }}消息机制原理AndroID系统主要通过Message、MessageQueue、Looper、Handler三个类来实现消息处理,其中Message代表消息,MessageQueue是用来描述消息队列,Looper是用来创建消息队列以及进入消息循环,Handler 是用来发送消息和处理消息。(以下源码均来自API 26 AndroID 8.0,源码在线查看:http://androidxref.com),先看下整个事件的整体脉络,建议根据图然后去找对应的函数调用。大图点击
未命名表单.pngHandlerHandler的成员变量
//可能造成内存泄露private static final boolean FIND_POTENTIAL_LEAKS = false;//是否为异步,默认为falsefinal boolean mAsynchronous;final Looper mLooper;final MessageQueue mQueue;final Callback mCallback;Handler的创建和获取public Handler() { this(null, false); }public Handler(Callback callback, boolean async) { //检测内存是否存在内存泄露的可能 ... ... mLooper = Looper.myLooper(); if (mLooper == null) { //必须先执行Looper.prepare()的原因 throw new RuntimeException( "Can't create handler insIDe thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async;}消息发送//最基本的消息发送public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0); }public final boolean sendEmptyMessage(int what){ return sendEmptyMessageDelayed(what, 0); }//延时发送消息public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis);}public final boolean sendMessageDelayed(Message msg, long delayMillis){ if (delayMillis < 0) { delayMillis = 0; } //SystemClock.uptimeMisllis()是系统启动至今的时间 return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}//在未来某一确定的时间点发送消息(发送消息最终的调用在这)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);}private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { //Message中保存的Handler 对象 msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis);}消息处理public voID dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { // 该Callback实在初始化的时候传进来 if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}//Handler 的内部接口public interface Callback { public boolean handleMessage(Message msg);}LooperLooper的重要成员//存储不同线程的Looperstatic final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();//主线程的Looperprivate static Looper sMainLooper;final MessageQueue mQueue;final Thread mThread;Looper的创建和获取public static voID prepare() { prepare(true);}private static voID prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { //Looper.prepare()只能执行一次的原因 throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed));}//准备主线程的Looperpublic static voID prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); }}//Looper构造器private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread();}//拿到当前线程的Looper对象public static @Nullable Looper myLooper() { return sThreadLocal.get();}//拿到主线程的Looper对象public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; }}UI线程中Looper的创建//ActivityThread.javapublic static voID main(String[] args) { ... ... Looper.prepareMainLooper(); ... ... Looper.loop(); ... ...}消息循环public static voID loop() { final Looper me = myLooper(); final MessageQueue queue = me.mQueue; for (;;) { Message msg = queue.next(); if (msg == null) { return; } ... ... msg.target.dispatchMessage(msg); }}MessageQueueMessageQueue的重要成员//该MessageQueue是否可退出,通过Looper.prepare()创建的都是可退出的,UI线程的MessageQueue是不可以退出的private final boolean mQuitAllowed;//当前消息队列的头部 Message mMessages;//C++层NativeMessageQueue的地址private long mPtr; //是否处于阻塞状态 private boolean mBlocked;MessageQueue的创建MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; //JNI调用初始化 C++ 层的 NativeMessageQueue.cpp mPtr = nativeInit();}消息入队boolean enqueueMessage(Message msg, long when) { ... ... synchronized (this) { ... ... msg.when = when; Message p = mMessages; boolean neeDWake; //头部等于空,或者入队消息的时间小于当前时间就插到队首 if (p == null || when == 0 || when < p.when) { msg.next = p; mMessages = msg; neeDWake = mBlocked; } else { //寻找队尾或者在自己发送时间之后的位置,然后插入队列中 neeDWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (neeDWake && p.isAsynchronous()) { neeDWake = false; } } msg.next = p; prev.next = msg; } if (neeDWake) { //JNI调用,唤醒NativeMessageQueue nativeWake(mPtr); } } return true;}消息出队Message next() { //执行下一条消息时还需要等待的时间,当为-1时代表消息队列中没有消息,则无限等待 int nextPollTimeoutMillis = 0; for (;;) { ... ... //JNI调用,阻塞 *** 作,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { final long Now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; ... ... if (msg != null) { if (Now < msg.when) { //当异步消息触发时间大于当前时间,则设置下一次轮询的超时时长 nextPollTimeoutMillis = (int) Math.min(msg.when - Now, Integer.MAX_VALUE); } else { // 获取一条消息,并返回 mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; //成功地获取MessageQueue中的下一条即将要执行的消息 return msg; } } else { //没有消息 nextPollTimeoutMillis = -1; } //消息正在退出,返回null(调用了quit()方法) if (mQuitting) { dispose(); return null; } ... ... } ... ... }}NativeMessageQueu//Java层中MessageQueue的nativaInit()方法static jlong androID_os_MessageQueue_nativeInit(jnienv* env, jclass clazz) { NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); ... ... //将NativeMessageQueue对象的指针强转为Java中的long类型并返回 return reinterpret_cast<jlong>(nativeMessageQueue);}NativeMessageQueue的构造器//和Java层的MessageQueue的构造器很类似NativeMessageQueue::NativeMessageQueue() : mPollEnv(NulL), mPollObj(NulL), mExceptionObj(NulL) { //获取当前线程的Looper,类似Java层的myLooper() mLooper = Looper::getForThread(); if (mLooper == NulL) { mLooper = new Looper(false); Looper::setForThread(mLooper); }}Looper.cpp构造器Looper::Looper(bool allowNonCallbacks) { //构造一个唤醒事件的文件描述符,能在用户态用做事件wait/notify机制,通过内核取唤醒用户态的事件。 //这个对象保存了一个内核维护的uint64_t类型的整型counter。这个counter初始值被参数一指定,一般初值设置为0。 //read:如果计数值counter的值不为0,读取成功,获得到该值。如果counter的值为0,非阻塞模式,会直接返回失败,并把errno的值指纹EINVAL。 //如果为阻塞模式,一直会阻塞到counter为非0位置。 //write:会增加8字节的整数在计数器counter上,如果counter的值达到0xfffffffffffffffe时,就会阻塞。直到counter的值被read。阻塞和非阻塞情况同上面read一样。 mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); ... ...}唤醒锁voID Looper::wake() { uint64_t inc = 1; //向唤醒事件的文件中写入数据 write(mWakeEventFd, &inc, sizeof(uint64_t));}轮询int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, voID** outData) { int result = 0; for(;;){ ... ... if (result != 0) { ... ... return result; } result = pollinner(timeoutMillis); }}int Looper::pollinner(int timeoutMillis) { ... ... //等待被唤醒,唤醒时间为timeoutMillis,如果timeoutMillis<0,则等待I/O事件唤醒 int eventCount = epoll_wait(mEpollFd, eventItems, EPolL_MAX_EVENTS, timeoutMillis); ... ... for (int i = 0; i < eventCount; i++) { int fd = eventItems[i].data.fd; uint32_t epollEvents = eventItems[i].events; if (fd == mWakeEventFd) { if (epollEvents & EPolliN) { awoken(); //已经唤醒了,则读取并清空管道数据 } } else { ... ... } ... ... } ... ... return result;}voID Looper::awoken() { uint64_t counter; //不断读取管道数据,目的就是为了清空管道内容 TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));}疑点Looper.loop()已经进入死循环,那么主线程的其他 *** 作是如何执行的?首先从线程的角度分析:一个线程是会在什么时间结束?很明显是该线程中的代码执行完成后就会结束,然后该线程就会被JVM回收。那么如何保证一个线程永远处于运行状态?貌似只有死循环。所以这么设计肯定是合理的,不然主线程早已被回收了,之所以一直存在就是因为Looper.loop()一直在执行。这时候第二个问题就出现了,如果Looper.loop()一直在阻塞等待,那么UI的绘制是如何执行?那么只能从MainLooper的源头查起,没错就是ActivityThread:final ApplicationThread mAppThread = new ApplicationThread();final H mH = new H();public static voID main(String[] args) { Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); //这时候就是进入死循环,一旦退出循环直接抛出异常 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited");}//ActivityThread的内部类,在这个类中有许多发送消息的方法,并且从方法中的参数可以看出都是与UI线程相关的 *** 作,并且我们知道Binder的服务端都是运行在Binder线程池中private class ApplicationThread extends IApplicationThread.Stub { @OverrIDe public final voID scheduleLaunchActivity(... ...) { ... ... sendMessage(H.LAUNCH_ACTIVITY, r); } public final voID schedulePauseActivity(... ...) { ... ... sendMessage(finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY, ... ...); } ... ...}//ActivityThread的内部类,该类继承自Handler,并且处理的消息都是与UI线程相关private class H extends Handler { switch (code) { case LAUNCH_ACTIVITY: ... ... handleLaunchActivity(r, null, "LAUNCH_ACTIVITY"); break; case PAUSE_ACTIVITY: handlePauseActivity(... ...); break; ... ... }}所以根据以上代码,可以大致分析出,UI线程本质的的所有 *** 作都是有MainHanlder来进行处理,而发送这些消息的 *** 作就分别在不同的子线程中,例如与生命周期相关的都是在ApplicationThread的线程中,下面从网上盗了张图:Android中为什么主线程不会因为Looper.loop里的死循环卡死?
image.png
所以说主线程不能独活,必须依赖于其他线程来给主线程发送消息,而我们自己在主线程里面发送消息无疑是来自于另一个线程先发的消息,例如我们经常会在Activity的生命周期中进行发送消息,而这些生命周期方法的执行也是来自与另一个线程的消息,就相当于我们在handlerMessage方法中再次发送消息下面的Demo可以说明上述结论是正确的。
public class MainActivity extends AppCompatActivity { private Handler handler; @OverrIDe protected voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.activity_main); new Thread(new Runnable() { @OverrIDe public voID run() { Log.d("TestHandler","Start Thread One"); Looper.prepare(); handler = new Handler(){ @OverrIDe public voID handleMessage(Message msg) { Log.d("TestHandler",msg.what+" = "+System.currentTimeMillis()); if (msg.what == 3){ Log.d("TestHandler","send Message!"); sendEmptyMessageDelayed(4,1000); sendEmptyMessage(5); } } }; Looper.loop(); handler.sendEmptyMessageDelayed(1,500); handler.sendEmptyMessage(2); } }).start(); new Thread(new Runnable() { @OverrIDe public voID run() { Log.d("TestHandler","Start Thread Two"); try { Thread.sleep(500); } catch (InterruptedException e) { e.printstacktrace(); } handler.sendEmptyMessage(3); } }).start(); }}//输出03-21 22:21:31.132 21995-22032/? D/TestHandler: Start Thread One03-21 22:21:31.133 21995-22033/? D/TestHandler: Start Thread Two03-21 22:21:31.633 21995-22032/? D/TestHandler: 3 = 152164209163303-21 22:21:31.633 21995-22032/? D/TestHandler: send Message!03-21 22:21:31.633 21995-22032/? D/TestHandler: 5 = 152164209163303-21 22:21:32.635 21995-22032/com.whf.test D/TestHandler: 4 = 1521642092635∂ 内存泄露问题为什么会发生内存泄露?Java 回收机制是根据可达性来判别,即一个对象如果是没有被其他对象引用(即不可达的),这时候在GC的过程就会把这个对象列为可回收的,在下一次的GC的时候就会执行回收。一个非静态内部类会持有外部类的引用。Message对象的taget属性引用了Handler对象。基于以上三点可知,当一个Activity中有一个非静态的Hanlder类,该Handler类就会持有该Activity的引用,如果当该Activitv退出的时候,其内部的Handler发送的Message还没有被处理完,而这些Message对象都会持有该Activity的引用,所以就会导致Activity无法被回收,从而造成内存泄露。Handler如何避免内存泄露?以静态内部类或外部类的形式存在,不要在Activity中写非静态的Handler类Handler的静态内部类访问外部类时采用弱引用在Activity回收前,清空MessageQueue中存储的该Handler发送的Message,通过调用Handler的
removeCallbackAndMessages()方法,该方法原理如下://Handlepublic final voID removeCallbacksAndMessages(Object token) { mQueue.removeCallbacksAndMessages(this, token);}//MessageQueuevoID removeCallbacksAndMessages(Handler h, Object object) { synchronized (this) { Message p = mMessages; while (p != null && p.target == h && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; p.recycleUnchecked(); p = n; } while (p != null) { Message n = p.next; if (n != null) { if (n.target == h && (object == null || n.obj == object)) { Message nn = n.next; n.recycleUnchecked(); p.next = nn; continue; } } p = n; } }}HandlerThreadpublic 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; }}IntentServicepublic 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(int)来保证你当前停止Service的请求是基于上一个请求的。也就是说,当你调用stopSelf(int),你把startID传给了对应的要停止的Service, //这个startID是上一个请求的StartID!!如果没有第二个请求来,那么这个Service就会死掉,但是如果这个Service已经又接受到一个新的启动请求之后, //你才调用stopSelf(int),那么你传递给stopSelf()的ID是上一个请求的ID,而当前Service的startID已经更新为新的请求的ID,造成两个ID不对应,stopSelf()失效, //那么Service就不会停止。这样就避免了将后面的请求终止 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);} 21人点赞 Android总结
以上是内存溢出为你收集整理的浅析Android 消息机制全部内容,希望文章能够帮你解决浅析Android 消息机制所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)