Android常见原理性问题
-
Handler机制和底层实现
在消息接收的线程初始化handler实例,若接收消息的线程非主线程,需要开启looper,主线程默认开启looper,一个线程只有一个looper与一个MessageQueue,可以拥有多个handler。</br>
一个Message经由Handler的发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler的怀抱 -
Handler、Thread和HandlerThread的差别
handler:线程间通信</br>
thread:线程</br>
HandlerThread:内部拥有并管理一个looper的thread,减少了开发者的工作量 -
handler发消息给子线程,looper怎么启动?
手动调用 </br>
Looper.prepare():</br>
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));
}
Looper.loop();</br>
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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
-
ThreadLocal原理,实现及如何保证Local属性?
ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名ThreadLocalVariable更容易让人理解一些。</br>
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。</br>
可以用于维护单例中的非线程安全对象,从线程的角度看,每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。实现:在ThreadLocal类中有一个table,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本
-
请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系
在开发Android 应用时必须遵守单线程模型的原则:
- 不要阻塞UI线程
- 确保只在UI线程中访问Android UI工具包
关系见上
-
事件分发中的onTouch 和onTouchEvent 有什么区别,又该如何使用?
事件处理优先级:onTouchListener(若返回true则不向下调用)> onTouchEvent > onClickListener
-
View刷新机制
-
View绘制流程
measure->layout->draw</br>
onMeasure执行:-
forceLayout为true:这表示强制重新布局,可以通过View.requestLayout()来实现;
-
needsLayout为true,这需要specChanged为true(表示本次传入的MeasureSpec与上次传入的不同),并且以下三个条件之一成立:
- sAlwaysRemeasureExactly为true: 该变量默认为false;</br>
- isSpecExactly为false: 若父View对子View提出了精确的宽高约束,则该变量为true,否则为false</br>
- matchesSpecSize为false: 表示父View的宽高尺寸要求与上次测量的结果不同</br>
-
</br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br></br>
-
如何取消AsyncTask?
cancel方法 --> isCancelled()为true --> 在doInBackground中手动检查决定是否继续运行
我们可以随时调用 cancel(boolean)去取消这个加载任务,调用这个方法会间接调用 iscancelled 并且返回true 。</br>
调用cancel()后,在doInBackground()return后 我们将会调用onCancelled(Object) 不在调用onPostExecute(Object)</br>
为了保证任务更快取消掉,你应该在doInBackground()周期性的检查iscancelled 去进行判断。</br> -
为什么不能在子线程更新UI?
Exception:Only the original thread that created a view hierarchy can touch its views
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
在Activity创建完成后(Activity的onResume之前ViewRootImpl实例没有建立),mThread被赋值为主线程(ViewRootImpl),所以直接在onCreate中创建子线程是可以更新UI的
在子线程中添加 Window,并且创建 ViewRootImpl,可以在子线程中更新view
设计思考:</br>
Android 的单线程模型,因为如果支持多线程修改 View 的话,由此产生的线程同步和线程安全问题将是非常繁琐的,所以 Android 直接就定死了,View 的操作必须在创建它的 UI 线程,从而简化了系统设计。
有没有可以在其他非原始线程更新 ui 的情况呢?有,SurfaceView 就可以在其他线程更新。