- 1. 事件分发
- 2. MeasureSpec含义
- 3. Handler 理解,为什么主线程 Looper.loop()不会卡死
- 4. Handler.postDelay如何实现延迟?
- 5. 子线程更新UI的方法
- 6. 子线程真的不能更新UI吗?
- 7. Fragment和Activity生命周期
- 8. Sqlite 专题
- 9. get post区别,post参数放哪?
- 10. ANR
- 11. OOM
- 12. Activity四大启动模式
- 13. Activity 中 onStart,和 onResume 以及 onPause 和 onStop 区别?
- 14. 屏幕适配
- 15. IdleHandler是什么
- 16. Bitmap相关问题
- 17. Bitmap 如何高效加载
- 18. Activity,Window,DocerView,ViewRootImpl的关系?
- 19. 断点续传
1. 事件分发
- 只有在 ViewGroup接收down事件或者子view能处理down事件时,会走 onInterceptTouchEvent
- 如果ViewGroup决定拦截一个事件(DOWN,MOVE或者UP),仅代表可能会(特殊情况参照结论4)执行该View的OnTouchEvent方法
- onTouchEvent 方法在DOWN事件返回 true 代表该 View 可以处理事件序列,后续的事件分发也会继续分发到该View无论MOVE和UP的返回值是什么;反之则代表不可以处理,此时会调用上级View的onTouchEvent处理,如果直到Activity的onTouchEvent依旧不能处理则后续直接调用Activity的OnTouchEvent处理
- 如果一个子View在DOWN事件是已经标明能处理,但是在MOVE时被父ViewGroup拦截,那么子View会收到CANCEL事件,后续事件转移到父ViewGroup中处理
参考 csdn-事件分发
2. MeasureSpec含义
MeasureSpec 是由父布局的参数(EXACTLY,AT_MOST)和子布局的宽高模式(XML布局)共同决定的
- 父ViewGroup是EXACTLY:
- 子View参数是具体值:
子View的MeasureSpec就是childSize+EXACTLY
- 子View参数是MATCH_PARENT:
子View的MeasureSpec就是parentSize+EXACTLY
- 子View参数是WRAP_PARENT:
子View的MeasureSpec就是parentSize+AT_MOST
父ViewGroup是AT_MOST:
1 子View参数是具体值:
子View的MeasureSpec就是childSize+EXACTLY
2.子View参数是MATCH_PARENT:
子View的MeasureSpec就是parentSize+AT_MOST
- 子View参数是WRAP_PARENT:
子View的MeasureSpec就是parentSize+AT_MOST
参考 csdn
3. Handler 理解,为什么主线程 Looper.loop()不会卡死
四大角色:Handler,looper, MessageQueue,Message
Handler初始化时,通过ThreadLocal获得相应Looper对象,这保证了一个线程一一对应一个Looper对象,而Looper初始化时new出了一个MessageQueue,
也就是说Handler,Looper,MessageQueue是一一对应的
在sendMessage中,封装了Message对象,其根据触发时间的先后顺序(开机到现在的毫秒数)插入MessageQueue链表的相应位置中,
同时在Looper中有loop()方法,他会调用MessageQueue.next()根据触发时间取出最新消息并调用dispatchMessage发送到Handler处理
下面代码是Handler处理消息的先后优先级:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
至于 Looper.loop() 为什么不会卡死,这是因为安卓是消息驱动的,onCreate等生命周期归根到底也是一个Handler的消息,正是因为死循环,安卓才可以正常运行不会退出,相反,如果消息中执行耗时操作导致消息阻塞会导致程序卡死,
4. Handler.postDelay如何实现延迟?
MessageQueue是根据Message的事件排序的,在插入queue时,如果是延迟的Message,会调用NativePollOnce方法阻塞住,直到时间到了才自动唤醒
如果正在阻塞过程中,新的消息需要再其之前执行,会调用naticeWake()方法唤醒,再进行相应处理
5. 子线程更新UI的方法
- handler.post()
- handler.sendMessage()
- view.post()
- runOnUiThread
- AsyncTask
6. 子线程真的不能更新UI吗?
结论,子线程可以更新UI,分情况
在 ViewRootImpl 中存在如下代码:
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
而mThread是在ViewRootImpl初始化时定义的,对于Activity来讲,ViewRootImpl 是在 ActivityThread 的 handleResumeActivity 方法中初始化的,所以 mThread 是主线程。
但是,如果我在子线程通过 WindowManager.addView 去添加一个View,那么我就可以在子线程更新UI了,因为addView中最终会初始化ViewRootImpl,线程就被定为子线程
7. Fragment和Activity生命周期
onAttach
onCreate
onCreateView
onActivityCreate __以上相当于Activity的onCreate方法
onStart __相当于Activity的onStart方法
onResume __相当于Activity的onResume方法
onPause __相当于Activity的onPause方法
onStop __相当于Activity的onStop方法
onDestroyView
onDestroy
onDetach __以上相当于Activity的onDestroy方法
8. Sqlite 专题
9. get post区别,post参数放哪?
10. ANR
11. OOM
12. Activity四大启动模式
- standard
- singleTop
用于通知跳转的页面
- singleTask
用于主页面
- singleInstance
13. Activity 中 onStart,和 onResume 以及 onPause 和 onStop 区别?
onstart和onStop是从Activity在前台还是后台这个角度分析的,
onResume和onPause是从Activity是否可见这个角度分析的
14. 屏幕适配
头条适配方案:
根据
px = dp * density
设计图给出一个 dp 值,其对应的 px 值也是已知的,所以我们能得出宽方向上的 density,以此为基准,高的方向可以自由拉伸缩小
图片适配:
mdpi | hdpi | xhdpi | xxhdpi | xxxhdpi |
---|---|---|---|---|
1x | 1.5x | 2x | 3x | 4x |
屏幕参数:
px = density * dp
density = dpi / 160
dpi = 对角线px / 对角线inch
15. IdleHandler是什么
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
Log.i("TEST_TAG", "queueIdle: ");
return false;
}
});
他可以在当前Looper闲置时执行某些逻辑
比如: 发送一个返回true的IdleHandler,在里面让某个View不停闪烁,这样当用户发呆时就可以诱导用户点击这个View,这也是种很酷的操作哦~
16. Bitmap相关问题
一张图片占用多大内存?
图片放在不同文件夹下会有相应的缩放效果,比如
180180的图片,放入hdpi中,hdpi对应dpi为240,而手机的dpi为560,那么实际图片大小为 180 ( 560 / 240 ) + 0.5f = 420px
而图片的像素点有这么几种:ALPHA_8
,RGB_565
,ARGB_4444
,ARGB_8888
其中ARGB_8888占用4字节,而 RGB_565和ARGB_4444占用2字节,所以ARGB_8888的图片占用 420 420 4 = 689kb
17. Bitmap 如何高效加载
- 获取需要的长和宽,一般获取控件的长和宽。
- 设置
BitmapFactory.Options
中的inJustDecodeBounds
为true,可以帮助我们在不加载进内存的方式获得Bitmap
的长和宽。 - 对需要的长和宽和Bitmap的长和宽进行对比,从而获得压缩比例,放入
BitmapFactory.Options
中的inSampleSize
属性。 - 设置
BitmapFactory.Options
中的inJustDecodeBounds
为false,将图片加载进内存,进而设置到控件中。
18. Activity,Window,DocerView,ViewRootImpl的关系?
Activity 仅仅掌管生命周期,相当于一个壳子,在其 attach 方法中,Activity 包含了一个 window:PhoneWindow,PhoneWindow 中包含了 DocerView,DocerView 是 FrameView 的子类,可以认为他就是 View 树的根节点,我们 setContentView 中的布局会被插在 DocerView 的 @android:id/content 中,
而说道 ViewRootImpl,他不是 view 也不是 ViewGroup,他是一个特殊的类,掌管 View 的绘制等功能,在系统创建 View 过程中,在调用 windowManager.addView 中会给每个 DocerView 创建一个 ViewRootImpl,在 ViewRootImpl 中最终会执行 View 的绘制三件套
19. 断点续传
基础知识:
- Http基础:在Http请求中,可以加入请求头
Range
,下载指定区间的文件数。 RandomAccessFile
:支持随机访问,可以从指定位置进行数据的读写。
有了这个基础以后,思路就清晰了:
- 通过
HttpUrlConnection
获取文件长度。 - 自己分配好线程进行制定区间的文件数据的下载。
- 获取到数据流以后,使用
RandomAccessFile
进行指定位置的读写。