Android笔记

组件应用

1. Context的理解

参考
探索Context之Context是什么
Context能做什么决定于它来自哪儿. 下表描述了常见的Context的来源以及其应用范围:
在这里插入图片描述
注解:

  1. Application的Context可以启动一个Activity,但是会在新Task中创建。这可能可以满足一些特定需求,但是这也会创建不标准的返回栈(Back Stack), 所以不推荐, 也不认为是好的实践。
  2. 这个也是合法的, 但是Inflate出来的View是根据你当前系统的默认主题(Theme)的,而非你的Application所使用的主题。
  3. Android 4.2及以上,如果Receiver是null(用来获取一个Sticky Broadcast的当前值的),则是允许的。

2. Service的启动方式以及生命周期

Service的启动分为startService和bindService两种形式

  • 生命周期
    在这里插入图片描述

  • onStartCommand方法的返回值
    onStartCommand方法执行时,返回的是一个int型。这个整型可以有三个返回值:
    START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand方法后,服务被异常kill掉,系统不会自动重启该服务
    START_STICKY:如果Service进程被kill掉,保留Service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建Service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand方法。如果在此期间没有任何启动命令被传递到Service,那么参数Intent将为null。
    START_REDELIVER_INTENT:重传Intent。使用这个返回值时,系统会自动重启该服务,并将Intent的值传入。

  • 绑定服务的通信方式
    扩展Binder类
    Messager
    AIDL

  • 启动服务和绑定服务的先后顺序问题
    如果同时使用startService 与bindService 方法启动Service,需要终止该Service时,要调用stopService 和unbindService 方法(unbindService 依附于启动它的Context,startServicec 并不依附于启动它的Context。如果先调用unbindService ,这时服务并不会被终止,当调用stopService 后,服务才会被终止;如果先调用stopService ,服务也不会被终止,当调用unbindService 或者之前调用bindService 的Context不存在了(如Activity被finish掉了)服务才会自动停止);
    手机屏幕发生旋转时,如果Activity设置的是自动旋转的话,在旋转的过程中,Activity会重新创建,那么之前通过bindService 建立的连接便会断开(之前的Context不存在了),服务也会被自动停止

2.1 startService

在这里插入图片描述
关键流程:
ActiveServices.startServiceLocked

  1. 通过caller查找调用者进程,判断是否为前台进程,然后调用retrieveServiceLocked去ServiceMap里查找目标Service。
  2. 如果启动前台Service,通过AppOpsService检查相关权限,如果mode类型是MODE_IGNORED,则将标志位fgRequired改为false
  3. 如果启动后台Service,也是通过AppOpsService检查是否有后台限制,比如O+版本启动后台Service会报Crash
  4. 检查完相关权限后,调用startServiceInnerLocked启动Service

ActiveServices.retrieveServiceLocked

  1. 根据userId获取对应的ServiceMap
  2. 解析Intent,获取ComponentName信息和FilterComparison信息,首先会根据这两个信息分别从ServiceMap中查找ServiceRecord
  3. 如果没有查找到,则新创建ServiceRecord,保存到ServiceMap中
  4. 将ServiceRecord封装成ServiceLookupResult返回。

ActiveServices.bringUpServiceLocked

  1. 如果目标Service已被启动,则回调onStartCommand函数
  2. 获取目标Service的进程,如果进程未启动则通知AMS启动进程,并将ServiceRecord添加到mPendingServices中,待进程启动完成后再继续启动Service,进程启动失败的话,则调用bringDownServiceLocked处理善后工作
  3. 如果目标进程已启动,则调用realStartServiceLocked

ActiveServices.realStartServiceLocked

  1. 绑定ServiceRecord和ProcessRecord
  2. 刷新ServiceRecord的开始启动的时间,调用方法bumpServiceExecutingLocked向AMS发送检测ANR的超时时间,前台服务20s,后台服务200s
  3. 更新LRU和OomADJ相关进程列表信息
  4. 通知应用进程加载Service类信息,并发送前台通知到通知栏

ActiveServices.sendServiceArgsLocked

  1. 调用方法bumpServiceExecutingLocked向AMS发送检测ANR的超时时间
  2. 设置startForeground调用超时检测,超时时间为10s
  3. 回调Service的onStartCommand函数

2.2 bindService

在这里插入图片描述
关键流程:
ContextImpl.bindServiceCommon
ActiveServices.retrieveServiceLocked
ActiveServices.bringUpServiceLocked
ActiveServices.realStartServiceLocked
ActiveServices.requestServiceBindingsLocked
ActivityThread.scheduleBindService
ActivityThread.handleBindService
Service.onBind
ActivityThread.publishServiceLocked

3. Activity的启动模式以及生命周期

  • 生命周期
    在这里插入图片描述
    在这里插入图片描述

关键流程:
Activity.startActivityForResult

  1. 获取IApplicationThread和token
  2. 通过Instrumentation通知AMS启动Activity

ActivityTaskManagerService.startActivityAsUser

  1. 通过ClientLifecycleManager通知ActivityStarter

ActivityStarter.startActivity

  1. 通过resultTo找到所标识的ActivityRecord,即启动者,并进行更新
  2. 常规检查,如果失败,则结束启动,返回错误码
  3. 权限检查,如果失败,会中断启动,但返回的是START_SUCCESS
  4. 创建被启动者的ActivityRecord,获取当前正在显示的ActivityStack,然后根据ActivityStack的uid是否一致,如不一致则需要切换app,加入PendingActivityLunch中

ActivityStarter.startActivityUnchecked

  1. 调用computeLaunchingTaskFlags方法计算启动模式flag
  2. 调用computeSourceStack,处理启动者的ActivityStack的异常情况,比如正在销毁,以及目标启动Activty的启动模式是FLAG_ACTIVITY_NEW_TASK
  3. 处理目标启动Activty已存在的场景,如果已存在,只需找到并重新挪到前台。目标Activity的宿主栈TaskRecord如果在后台,也需要将其挪至前台,绑定ActivityRecord和TaskRecord。
  4. 处理目标启动Activity不存在的场景,如果不存在则说明,比如NEW_TASK场景,则会创建对应的TaskRecord。

ActivityStack.startActivityLocked

  1. 如果目标activity的TaskRecord不存在或者是newTask,则直接插入mTaskHistory的最前台
  2. 如果不是newTask并且存在,则遍历mTaskHistory找到对应的TaskRecord并挪到前台
  3. 如果是首次启动该Activity,则需要创建AppWindowToken,然后将ActivityRecord移到TaskRecord的最前端,为ActivityRecord绑定一个窗口

ActivityStackSupervisor.startSpecificActivityLocked

  1. 检查要启动的Activity的宿主进程是否启动,如果已启动,则调用realStartActivityLocked直接启动Activity.
  2. 宿主进程未启动,则给AMS的mH发消息,通知AMS开始启动宿主进程。

ActivityStackSupervisor.realStartActivityLocked

  1. 启动Activity需要冻结屏幕,防止误操作startFreezingScreenLocked
  2. 调用setProcess将ActivityRecord和进程绑定
  3. 先pause掉原先的Activity,pause完成后,会回调ActivityStack的completePauseLocked方法,然后开始resume目标activity

4. ContentProvider是如何实现数据共享的

5. 广播的注册方式

在这里插入图片描述

6. ListView和RecycleView

参考
Android ListView工作原理完全解析,带你从源码的角度彻底理解
ListView源码分析
ListView源码分析
深入浅出 RecyclerView
【腾讯Bugly干货分享】Android ListView 与 RecyclerView 对比浅析—缓存机制
在这里插入图片描述

7. ListView的优化手段有哪些?越详细越多手段越好

  • 复用convertView
  • 使用ViewHolder
  • item中有图片时,异步加载
  • 快速滑动时,不加载图片
  • item中有图片时,应对图片进行适当压缩
  • 分批和分页加载

8. SurfaceView和GLSurfaceView

参考

9. 进程保护以及后台启动服务的限制

Android O 后台startService限制简析

系统架构原理

1. Handler、Looper、MessageQueue之间的关系

参考
Android消息机制1-Handler(Java层)
Android消息机制的冷门知识点
在这里插入图片描述

2. IdleHandler和消息屏障

3. 主线程进入looper循环为什么没有ANR

4. HandleThread和IntentService

5. Android中进程间通信的方式

6. Input系统原理以及Android事件分发机制

6.1 InputManager启动

参考
启动篇

6.2 基本流程

参考
InputReader线程
InputDispatcher线程
UI线程
进程交互

6.3 ANR原理

参考
ANR原理分析

6.4 View事件分发流程

参考
View事件分发流程
在这里插入图片描述

回调函数调用顺序

  • onTouch -> onTouchEvent->onClick
    onTouch返回true,则不会继续往下调用
    onTouch返回false,继续调用onTouchEvent,Up事件到来时,触发onClick
  • onTouch -> onTouchEvent->onLongClick
    而Down事件到来时,会postDelayed大概500ms的延时消息,超时后会判断是否执行onLongClick

核心要点

  • 事件分发原理: 责任链模式,事件层层传递,直到被消费。
  • View 的 dispatchTouchEvent 主要用于调度自身的监听器和onTouchEvent。
  • View的事件的调度顺序是 onTouchListener > onTouchEvent >onLongClickListener > onClickListener
  • 不论 View 自身是否注册点击事件,只要 View是可点击的就会消费事件。
  • 事件是否被消费由返回值决定,true 表示消费,false 表示不消费,与是否使用了事件无关
  • ViewGroup 中可能有多个 ChildView 时,将事件分配给包含点击位置的 ChildView
  • ViewGroup 和 ChildView 同时注册了事件监听器(onClick等),由 ChildView 消费
  • 一次触摸流程中产生事件应被同一 View消费,全部接收或者全部拒绝
  • 只要接受 ACTION_DOWN 就意味着接受所有的事件,拒绝 ACTION_DOWN 则不会收到后续内容
  • 如果当前正在处理的事件被上层 View 拦截,会收到一个 ACTION_CANCEL,后续事件不会再传递过来。

7. View的绘制流程

View为什么会至少进行2次onMeasure、onLayout

8. Binder机制

在这里插入图片描述

9. 屏幕刷新的机制以及Surface

9.1 屏幕刷新机制

9.2 Surface的创建

9.3 SurfaceFlinger的连接

10. 应用启动流程,热启动和冷启动

参考
理解Android进程创建流程

  1. system_server进程
    通过Process.start()方法发起创建新进程请求,会先收集各种新进程uid、gid、nice-name等相关的参数,然后通过socket通道发送给zygote进程;
  2. zygote进程
    接收到system_server进程发送过来的参数后封装成Arguments对象,图中绿色框forkAndSpecialize()方法是进程创建过程中最为核心的一个环节(详见流程6),其具体工作是依次执行下面的3个方法:
    preFork():先停止Zygote的4个Daemon子线程(java堆内存整理线程、对线下引用队列线程、析构线程以及监控线程)的运行以及初始化gc堆;
    nativeForkAndSpecialize():调用linux的fork()出新进程,创建Java堆处理的线程池,重置gc性能数据,设置进程的信号处理函数,启动JDWP线程;
    postForkCommon():在启动之前被暂停的4个Daemon子线程。
  3. 新进程
    进入handleChildProc()方法,设置进程名,打开binder驱动,启动新的binder线程;然后设置art虚拟机参数,再反射调用目标类的main()方法,即Activity.main()方法。
    在这里插入图片描述
    Android系统中的进程管理:进程的创建
    Android系统中的进程管理:进程的优先级
    Android系统中的进程管理:内存的回收

11. AsyncTask原理

12. Activity和Service通信方式,AIDL

13. AMS、WMS和Activity的通信

参考
简述Activity与Window关系

13.1 关系图

在这里插入图片描述

13.2 详细过程

在这里插入图片描述

14. 序列化Serializable和Parcelable的理解和区别

15. 窗口管理

参考
WindowManagerService架构剖析之窗口分组与分层

性能优化

Android 性能优化必知必会

1. ANR的原理

参考
ANR机制以及问题分析
Service处理超时

  1. 超时时长
    前台进程中执行Service,超时时间是SERVICE_TIMEOUT(20秒)
    后台进程中执行Service,超时时间是SERVICE_BACKGROUND_TIMEOUT(200秒)
  2. 开始计时
    在ActiveServices中。 当Service的生命周期开始时,bumpServiceExecutingLocked()会被调用,紧接着会调用scheduleServiceTimeoutLocked(),此时会调用AMS的Handler发送一个延时消息开始计时。
    void scheduleServiceTimeoutLocked(ProcessRecord proc) {
        if (proc.executingServices.size() == 0 || proc.thread == null) {
            return;
        }
        Message msg = mAm.mHandler.obtainMessage(
                ActivityManagerService.SERVICE_TIMEOUT_MSG);
        msg.obj = proc;
        mAm.mHandler.sendMessageDelayed(msg,
                proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
    }
  1. 清除计时
    当Service的生命周期结束时,会调用serviceDoneExecutingLocked()方法,之前抛出的SERVICE_TIMEOUT_MSG消息在这个方法中会被清除。
    private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
            boolean finishing) {
        r.executeNesting--;
        if (r.executeNesting <= 0) {
            mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
        }
    }

Broadcast处理超时
Broadcast分为静态广播和动态广播,其中动态广播有分为普通广播和有序广播,由于普通的动态广播是并行分发,所以不会发生ANR。只有在处理静态广播和有序广播时才会发生ANR,ANR判定有两处:

  1. 判定当前时间是否已经超过了r.dispatchTime + 2×mConstants.TIMEOUT×numReceivers
    dispatchTime 表示这一系列广播消息开始派发的时间。“串行广播消息”是逐个接收器派发的,一个接收器处理完毕后,才开始处理下一个消息派发。 开始派发到第一个接收器的时间就是dispatchTime。dispatchTime需要开始等广播消息派发以后才会设定,也就是说,第一次进入processNextBroadcast()时, dispatchTime=0,并不会进入该条件判断。
    mConstants.TIMEOUT 由当前BroadcastQueue的类型决定,在初始化AMS的时候会设定该值(forground为10秒,background为60秒)
BroadcastQueue.processNextBroadcastLocked
     if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) {
         if ((numReceivers > 0) &&
                 (now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) {
				// 发生ANR
             broadcastTimeoutLocked(false); // forcibly finish this broadcast
             forceReceive = true;
             r.state = BroadcastRecord.IDLE;
         }
     }

假设一个广播消息有2个接受器,TIMEOUT是10秒,当2×10×2=40秒后,该广播消息还未处理完毕,就调用broadcastTimeoutLocked()方法, 这个方法会判断当前是不是发生了ANR

  1. setBroadcastTimeoutLocked
    通过设置一个定时消息BROADCAST_TIMEOUT_MSG来跟踪当前广播消息的执行情况,这种超时监测机制跟Service ANR很类似,也是抛到AMS线程的消息队列。 如果所有的接收器都处理完毕了,则会调用cancelBroadcastTimeoutLocked()清除该消息;否则,该消息就会响应,并调用broadcastTimeoutLocked()
BroadcastQueue.processNextBroadcastLocked
       if (! mPendingBroadcastTimeoutMessage) {
           long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
           setBroadcastTimeoutLocked(timeoutTime);
       }

Input处理超时

会调用findFocusedWindowTargetsLocked()或findTouchedWindowTargetsLocked()寻找接收输入事件的窗口。在找到窗口以后,会调用checkWindowReadyForMoreInputLocked() 检查窗口是否有能力再接收新的输入事件,会有一系列的场景阻碍事件的继续派发,则输入事件需要继续等待,紧接着就会调用handleTargetsNotReadyLocked()来判断是不是已经的等待超时了,如果当前事件派发已经超时,则说明已经检测到了ANR,调用onANRLocked()方法,然后将nextWakeupTime设置为最小值,马上开始下一轮调度。

2. Handler内存泄漏

  1. 产生原因
    在java中非静态内部类和匿名内部类都会隐式持有当前类的外部引用,Handler使用非静态内部类时,就会持有当前Activity的隐式引用,如果Handler没有被释放,比如发送一个延时消息,则应用主线程的MessageQueue里的Message消息,就会持有Handler的引用。当Activity切到后台时,由于有Handler的引用,导致Activity不能被销毁,从而产生内存泄漏。
  2. 解决方案
    静态内部类+弱引用

3. LruCache原理

参考
【Android】LRU 缓存——内存缓存与磁盘缓存

三方框架

1. OkHttp

参考
OKHttp源码解析
okhttp源码分析(四)-ConnectInterceptor过滤器
okhttp源码分析(三)-CacheInterceptor过滤器
okhttp源码分析(二)-RetryAndFollowUpInterceptor过滤器
OkHttp3.14 源码剖析系列(六)——连接复用机制及连接的建立
在这里插入图片描述

2. RxJava

3. Glide

4. LeakCavanry

参考
LeakCanary源码分析

  1. 用ActivityLifecycleCallbacks接口来检测Activity生命周期
  2. WeakReference + ReferenceQueue 来监听对象回收情况
  3. Apolication中可通过processName判断是否是任务执行进程
  4. MessageQueue中加入一个IdleHandler来得到主线程空闲回调
  5. LeakCanary检测只针对Activiy里的相关对象。其他类无法使用,还得用MAT原始方法

5. retrofit

参考
Retrofit源码学习

6. 插件化和热更新

Android热修复技术专题:来自微信、淘宝、支付宝、QQ空间的热修复方案

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值