美文网首页
Android Framework源码阅读笔记

Android Framework源码阅读笔记

作者: 艾瑞败类 | 来源:发表于2023-04-05 13:36 被阅读0次

    作者:橘子没了

    应用启动流程

    暂时无法在飞书文档外展示此内容

    Activity启动流程

    关键debug节点:

    
    //左侧 ActivithThread
    //右侧 ActivityTaskManagerService>..>ActivityTaskSupervisor
    //中继 debug:ClientTransaction.schedule>mclient.scheduleTransaction
    //这里是 mclient是 ApplicationThread,从这里通过binder回到ActivithThread的
    //父类 ClientTransactionHandler.scheduleTransaction>TransactionExecutor>
    //TransactionExecutor负责解析ClientTransaction,分发回执到ATMS到消息与
    //调用ActivityThread的各个launch方法
    //要注意TransactionExecutor.performLifecycleSequence中的switch基本只会进入ON_CREATE,其他的在各Launch
    //方法中自己串联的
    //回到左侧 ActivithThread各Launch,创建activity,执行resume,发送idle消息执行stop,destory等
    从启动看重点:ActivivtyRecord在何时创建,
    

    ActivityThread extends ClientTransactionHandler

    下面都是 通过binder发送到ApplicationThread由它调用ActivityThread父类的 scheduleTransaction

    最后在TransactionExecutor(ClientTransactionHandler)的 executeCallbacks,executeLifecycleState 中调用对应的 execute方法,分配到对应的 ActivityThread 的各个生命周期调用里

    第一次一个消息: TopResumedActivityChangeItem

    ActivityTaskSupervisor:mAtmService.getLifecycleManager().scheduleTransaction(binder:ApplicationThread, appToken,
            TopResumedActivityChangeItem.obtain(onTop));
    ClientLifecycleManager.scheduleTransaction{
    ClientTransaction clientTransaction = transactionWithCallback(client, activityToken, callback);
    {new ClientTransaction(client=ApplicationThread)}
    }
    clientTransaction.schedule()
    

    第二次一个消息: PauseActivityItem

    ActivityTaskSupervisor:mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
            prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
                    prev.configChangeFlags, pauseImmediately));
    

    第三次 2个 消息:launch and resume

    ActivityTaskSupervisor:
    final ClientTransaction clientTransaction = ClientTransaction.obtain(
            proc.getThread(), r.appToken);
    clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
    //还有一个变量:mLifecycleStateRequest:ResumeActivityItem
    mService.getLifecycleManager().scheduleTransaction(clientTransaction);
    

    第四次一个消息:TopResumedActivityChangeItem

    ActivityTaskSupervisor:
    ActivityRecord.scheduleTopResumedActivityChanged{
    mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
            TopResumedActivityChangeItem.obtain(onTop));
    }
    

    第五个消息 : StopActivityItem

    ActivityTaskSupervisor:
    ActivityTaskSupervisor.activityIdleInternal>processStoppingAndFinishingActivities>
    ActivityRecord.stopPossible{
    mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
            StopActivityItem.obtain(configChangeFlags));
    }
    

    重点关注ActivityThread.handleXXX方法:

    handleReLaunchActivity>

    performDestroyActivity>
    r.activity.retainNonConfigurationInstances();//保存 viewmodel等信息传递到新Activity
    

    handleLaunchActivity>

    unscheduleGcIdler();//取消在后台接受GC的消息
    WindowManagerGlobal.initialize();//WMS 初始化
    performLaunchActivity>
    newActivity//反射创建 activity
    makeApplicationif: (mApplication != null)    return mApplication;
    activity.attach>
      attachBaseContext
      new PhoneWindow()
      if (theme != 0) {
        activity.  setTheme(theme)  ;
    }
      if (r.isPersistable()) {//  区分持久化,调用onCreat  
        mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    } else {
        mInstrumentation.callActivityOnCreate(activity, r.state);
    }
    

    handleResumeActivity>

    unscheduleGcIdler();
    performResumeActivity>
    activity.performResume>
      dispatchActivityPreResumed//全局callback 回调
      performStart/performRestart
      mInstrumentation.callActivity  OnResume  (this);
      mFragments.dispatchResume();
      dispatchActivityPostResumed();全局callback 回调
    if (r.window == null && !a.mFinished && willBeVisible) >//不考虑 onCreate中setContrentView触发了Window绑定
    r.window = r.activity.getWindow();//PhoneWindow
    View decor = r.window.getDecorView();
    ViewManager wm = a.getWindowManager();//WindowManagerGlobal
    wm.addView(decor, l)>//完成 viewRootImpl 与DecorView关联
      Root = new ViewRootImpl()
      root.setView(view:DecorView)>
        requestLayout();//触发第一个粗略布局
    Looper.myQueue().addIdleHandler(new Idler())>;//插入一条Idler消息
    ac.activityIdle(a.token, a.createdConfig, stopProfiling)>//给所有没有
      ActivityClientController.activityIdle>//ActivityClientController是一个 IBinder
        activityIdleInternal>
          mHandler.removeMessages(      IDLE_TIMEOUT_MSG      , r);
          mHandler.removeMessages(      LAUNCH_TIMEOUT_MSG      );
          processStoppingAndFinishingActivities>
            if (r.isInHistory()) {
        if (        r.finishing        ) {
            // TODO(b/137329632): Wait for idle of the right activity, not just any.
            r.destroyIfPossible(reason);
        } else {
            r.stopIfPossible();>
        }
    }
            ..  r.destroyImmediately(reason);
          r.stopIfPossible();>
        mAtmService.getLifecycleManager().scheduleTransaction(    StopActivityItem    );
        r.destroyImmediately(reason);>
        mAtmService.getLifecycleManager().scheduleTransaction(    DestroyActivityItem    )
        在resume之后发送idle消息,触发 stop
        同时设置Idle超时消息,如果 10s没有执行则会主动再次触发 activityIdleInternal   
        mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
            StopActivityItem.obtain(configChangeFlags));     
        mAtmService.mH.postDelayed(mStopTimeoutRunnable, STOP_TIMEOUT);//stop 超时时间 11s
        mStopTimeoutRunnable:Slog.w(TAG, "Activity stop timeout for " + ActivityRecord.this);
        notifyAppStopped();//应用停止运行
        //如何取消stopTimeOut消息 需要去handleTopActivity看
    

    关于 IdleHandler的执行时机

    Looper.myQueue().addIdleHandler(new Idler())//mIdleHandlers.add()

    MessageQueue.next()>MessageQueue:链表

    每次根据时间查找满足执行的message

    此次时间上没有要执行的message时,执行截至目前存在的全部Idle消息

    轮训下一个时间可执行的消息

    handleStopActivity

    performStopActivityInner>
        performPauseActivityIfNeeded //再次确认puase了没
        callActivityOnStop>
            callActivityOnSaveInstanceState // targetSdkVersion<28
            activity.performStop>
                dispatchActivityPreStopped
                mFragments.dispatchStop()
                mInstrumentation.callActivityOnStop
                dispatchActivityPostStopped
            callActivityOnSaveInstanceState(r) // targetSdkVersion>=28
    if(targetSdkVersion>11){
        QueuedWork.waitToFinish()//等待完成全部finish runnable
    }
    pendingActions.setStopInfo(stopInfo);//这里是重点
    //-----如何回传到 ATMS 去 removeStopTimeOut消息的逻辑
    TransactionExecutor中对所有的 ClientTransactionItem 调用
    item.execute(mTransactionHandler:ClientTransactionHandler, token, mPendingActions);
    item.postExecute(mTransactionHandler, token, mPendingActions);
    回到 ActivityThread.reportStop(mPendingActions)>mH.post(pendingActions.getStopInfo());
    执行 PendingTransactionActions.StopInfo 的run方法 通知ATMS
    ActivityClient.getInstance().activityStopped(
            mActivity.token, mState, mPersistentState, mDescription);
    //binder:ActivityClientController>ActivityRecor.activityStopped>removeStopTimeOut
    

    关于waitToFinish导致ANR的问题:在SharedPreferenceImpl中会调用 QueuedWork.addFinisher(sp 内存写入磁盘的runnable),单低于26(8.0)的SharedPreferenceImpl的实现性能较差,每次都是全部写入磁盘,有大量SP数据需要写入时,触发 finish 就会导致ANRq

    handleDestroyActivity>

    performDestroyActivity>
        if (getNonConfigInstance) {r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances()}
        mInstrumentation.callActivityOnDestroy(r.activity);
    cleanUpPendingRemoveWindows>
        WindowManagerGlobal.closeAll()>
            ViewRootImpl.die>
                //解绑输入时间,销毁硬件渲染器,dispatchDetachedFromWindow,解绑 decorView
                WindowManagerGlobal.getInstance().doRemoveView(this);//移除ViewRootImpl
    

    事件:

    第一块:接受native事件

    WindowInputEventReceiver extends InputEventReceiver
    Native>
    InputEventReceiver.dispatchInputEvent>WindowInputEventReceiver.onInputEvent>ViewRootImpl.enqueueInputEvent>ViewRootImpl.doProcessInputEvents>....>processPointerEvent>View.dispatchPointerEvent>DecorView.dispatchTouchEvent>mWindow.CallBack.dispatchTouchEvent | super(ViewGroup).dispatchTouchEvent {Activity 可以截胡或消费后放行,但事件是从Native直接传递到ViewRootImpl的},
    默认是走Activity dispatchTouchEvent,但会优先 回到View上,如果重写了可以自己截胡或者消费完再调super,只重写 onTouchEvent不重写dispatchTouchEvent 则在View消费后才有可能获取到事件
    activity代码:
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) { // 这里的调用链条:IphoneWindow>mDecorView.superDispatchTouchEvent>ViewGroup.dispatchTouchEvent()
            return true;
        }
        return onTouchEvent(ev);
    }
    Window.Callback.dispatchTouchEvent>Activity.dispatchTouchEvent>DOWN:onUserInteraction,Activity.onTouchEvent)
    ViewGroup.dispatchTouchEvent>
    

    第二块:ViewGroup

    在Down事件或已有事件消费目标时

    ViewGroup.disallowIntercept:false>ViewGroup.onInterceptTouchEvent
                                                  ViewGroup.disallowIntercept:true>intercepted = false
    

    如果子view没有为父view设置DisallowIntercept,就查看父view自己是否在onInterceptTouchEvent自己拦截

    第三块:ViewGroup if (!canceled && !intercepted) {}

    未取消,父View也未拦截,处于初始事件,遍历childView 寻找消费目标:dispatchTransformedTouchEvent>addTouchTarget>

    child.dispatchTouchEvent>realView.dispatchTouchEvent>onTouch | onTouchEvent

    第四块:ViewGroup if(mFirstTouchTarget == null)

    找不到消费目标丢给 父类:View.dispatchTouchEvent 处理

    第五块:View

    将后续事件传给上面找到的TargetView进行消费

    布局:

    第一块:onCreate:Activity的window 创建

    ActivityThread.handleLaunchActivity>...>activit.attach>attachBaseContext,mWindow = PhoneWindow(ActivityClientRecord.mPendingRemoveWindow) 
    if(mPreserveWindow) mDecorView 将被复用否则 new PhoneWindow
    UserActivity.setContentView>AppCompatActivity.setContentView>:initViewTreeOwners>getWindow().getDecorView
    >PhoneWindow.getDecorView>PhoneWindow.installDecor>:createDecorView,mDecor.setWindow(this);
    创建DecorView完成window与 DecorView的关联。DecorView父类是FrameLayout
    DecorView创建时会出实话导航栏背景色,半透明状态栏颜色,导航栏进出动画等,                                                                             AppCompatActivitysetContentView>:
    getDelegate.setContentView>AppCompatDelegateImpl.setContentView>:ensureSubDecor()再次确保decorView已创建好
    

    第二块:onResume

    ActivityThread.handleResumeActivity>:WindowManager(WindowManagerGlobal).addView(decorView:window.getDecorView)>:
    root = new ViewRootImpl(view.getContext(), display)
    global.mViews.add(decorView)//全局保存
    global.mRoots.add(root)//全局保存
    root.setView(decorView..)//完成ViewRootImpl 与DecorView 关联>:
    requestLayout()//进行首次布局,确保接受系统事件前有布局
    mWindowSession.addToDisplayAsUser(InputChannel)
    mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
            Looper.myLooper());//这里没看懂,没到如何被回调
    view.assignParent(this);将ViewRootImpl 指定为DecorView的ParentView
    requestLayout()>:checkThread(指判断是否为创建线程),scheduleTraversals()
    scheduleTraversals()>mChoreographer.postCallback(horeographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    mTraversalRunnable.run> doTraversal> :
    performMeasure
    performLayout
    performDraw
    

    自定义View嵌套滑动冲突

    核心是结合多个 NestedScrollingParent,NestedScrollingChild 处理好上下游滑动事件的消费逻辑

    几年前写的一个支持潜逃子View的下拉刷新库:github.com可以让我快速拾起对滑动事件处理的知识。

    ANR

    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        println("eventView:click")
        Thread.sleep(10*10000)
        return super.dispatchTouchEvent(ev)
    }
    

    上面的代码在小米K30 上 如果没有新的系统消息执行获取反馈,则不会触发 处理超时ANR

    ROOM厂商会优化ANR的体验,模拟器会ANR

    官方文档:

    • 输入调度超时:如果您的应用在 5 秒内未响应输入事件(例如按键或屏幕触摸)。

    • 执行服务:如果应用声明的服务无法在几秒内完成 Service.onCreate()Service.onStartCommand()/Service.onBind() 执行。

    • 未调用 Service.startForeground():如果您的应用使用 Context.startForegroundService() 在前台启动新服务,但该服务在 5 秒内未调用 startForeground()

    • intent 广播:如果 [BroadcastReceiver](https://developer.android.com/reference/android/content/BroadcastReceiver?hl=zh-cn) 在设定的一段时间内没有执行完毕。如果应用有任何前台 activity,此超时期限为 5 秒。

    其他实际细节:

    前台广播5s,后台广播60s

    诊断ANR

    • 应用在主线程上非常缓慢地执行涉及 I/O 的操作。

    • 应用在主线程上进行长时间的计算。

    • 主线程在对另一个进程进行同步 binder 调用,而后者需要很长时间才能返回。

    • 主线程处于阻塞状态,为发生在另一个线程上的长操作等待同步的块。

    • 主线程在进程中或通过 binder 调用与另一个线程之间发生死锁。主线程不只是在等待长操作执行完毕,而且处于死锁状态。

    总结:

    除了API调用的逻辑错误外,主要是在主线程有长时间执行的代码或锁,阻塞了后续系统or用户的操作,系统会发出ANR提示

    相关文章

      网友评论

          本文标题:Android Framework源码阅读笔记

          本文链接:https://www.haomeiwen.com/subject/xvbcddtx.html