1. 事件分发

  1. 只有在 ViewGroup接收down事件或者子view能处理down事件时,会走 onInterceptTouchEvent
  2. 如果ViewGroup决定拦截一个事件(DOWN,MOVE或者UP),仅代表可能会(特殊情况参照结论4)执行该View的OnTouchEvent方法
  3. onTouchEvent 方法在DOWN事件返回 true 代表该 View 可以处理事件序列,后续的事件分发也会继续分发到该View无论MOVE和UP的返回值是什么;反之则代表不可以处理,此时会调用上级View的onTouchEvent处理,如果直到Activity的onTouchEvent依旧不能处理则后续直接调用Activity的OnTouchEvent处理
  4. 如果一个子View在DOWN事件是已经标明能处理,但是在MOVE时被父ViewGroup拦截,那么子View会收到CANCEL事件,后续事件转移到父ViewGroup中处理

参考 csdn-事件分发

2. MeasureSpec含义

MeasureSpec 是由父布局的参数(EXACTLY,AT_MOST)和子布局的宽高模式(XML布局)共同决定的

  • 父ViewGroup是EXACTLY:
  1. 子View参数是具体值:

子View的MeasureSpec就是childSize+EXACTLY

  1. 子View参数是MATCH_PARENT:

子View的MeasureSpec就是parentSize+EXACTLY

  1. 子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

  1. 子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处理消息的先后优先级:

  1. public void dispatchMessage(Message msg) {
  2. if (msg.callback != null) {
  3. handleCallback(msg);
  4. } else {
  5. if (mCallback != null) {
  6. if (mCallback.handleMessage(msg)) {
  7. return;
  8. }
  9. }
  10. handleMessage(msg);
  11. }
  12. }

至于 Looper.loop() 为什么不会卡死,这是因为安卓是消息驱动的,onCreate等生命周期归根到底也是一个Handler的消息,正是因为死循环,安卓才可以正常运行不会退出,相反,如果消息中执行耗时操作导致消息阻塞会导致程序卡死,

4. Handler.postDelay如何实现延迟?

MessageQueue是根据Message的事件排序的,在插入queue时,如果是延迟的Message,会调用NativePollOnce方法阻塞住,直到时间到了才自动唤醒
如果正在阻塞过程中,新的消息需要再其之前执行,会调用naticeWake()方法唤醒,再进行相应处理

5. 子线程更新UI的方法

  1. handler.post()
  2. handler.sendMessage()
  3. view.post()
  4. runOnUiThread
  5. AsyncTask

等等

6. 子线程真的不能更新UI吗?

结论,子线程可以更新UI,分情况

在 ViewRootImpl 中存在如下代码:

  1. void checkThread() {
  2. if (mThread != Thread.currentThread()) {
  3. throw new CalledFromWrongThreadException(
  4. "Only the original thread that created a view hierarchy can touch its views.");
  5. }
  6. }

而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是什么

  1. Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
  2. @Override
  3. public boolean queueIdle() {
  4. Log.i("TEST_TAG", "queueIdle: ");
  5. return false;
  6. }
  7. });

他可以在当前Looper闲置时执行某些逻辑
比如: 发送一个返回true的IdleHandler,在里面让某个View不停闪烁,这样当用户发呆时就可以诱导用户点击这个View,这也是种很酷的操作哦~

16. Bitmap相关问题

一张图片占用多大内存?
图片放在不同文件夹下会有相应的缩放效果,比如
180180的图片,放入hdpi中,hdpi对应dpi为240,而手机的dpi为560,那么实际图片大小为 180 ( 560 / 240 ) + 0.5f = 420px
而图片的像素点有这么几种:
ALPHA_8RGB_565ARGB_4444ARGB_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:支持随机访问,可以从指定位置进行数据的读写。

有了这个基础以后,思路就清晰了:

  1. 通过HttpUrlConnection获取文件长度。
  2. 自己分配好线程进行制定区间的文件数据的下载。
  3. 获取到数据流以后,使用RandomAccessFile进行指定位置的读写。