Android 12 source code analysis - Application layer six (UI creation and initialization of StatusBar)

Android 12 source code analysis - Application layer six (UI creation and initialization of StatusBar)

In the previous article, we introduced the overall layout of Layout and the initialization of the StatusBar class respectively. The former introduced the overall layout, and the latter introduced the entrance to the creation of the three major windows and the preparation work that needs to be done. Now we Let’s detail the UI creation and initialization of the three major windows respectively, starting with the StatusBar window.

Previous articles include:

  1. "Android 12 source code analysis - Application layer four (Basic layout design and basic concepts of SystemUI) http://t.csdn.cn/yYS80 "
  2. "Android 12 source code analysis - application layer five (startup process of SystemUI's StatusBar class and creation of three windows) http://t.csdn.cn/n3gwH "

In this article, we will introduce the creation and initialization process of the StatusBar window, and view the animation when the icon appears in the StatusBar.

The beginning of StatusBar's UI

In the previous article ("Android 12 source code analysis - application layer five (startup process of SystemUI's StatusBar class and creation of three windows) http://t.csdn.cn/n3gwH " ), we mentioned, The creation of the three major windows starts with the createAndAddWindows() function, then calls the makeStatusBar() function to create, and then calls the InflateStatusBarWindow() function to create the View. When creating the View, the corresponding one is obtained through the SuperStatusBarViewFactory factory class. View. At this point we have no further explanation. Next we will start from here and introduce the creation and initialization of UI in detail.

Let’s first take a look at the getStatusBarWindowView() function of SuperStatusBarViewFactory. The source code is as follows:

public StatusBarWindowView getStatusBarWindowView() {
    
    
	//如果有缓存View,则直接返回
   if (mStatusBarWindowView != null) {
    
    
       return mStatusBarWindowView;
   }
	//如果没有缓存,则创建View对象,并返回
   mStatusBarWindowView =
           (StatusBarWindowView) mInjectionInflationController.injectable(
           LayoutInflater.from(mContext)).inflate(R.layout.super_status_bar,
           /* root= */ null);
   //因为R.layout.super_status_bar文件很是关键,倘若创建不成功则会直接抛出异常
   if (mStatusBarWindowView == null) {
    
    
       throw new IllegalStateException(
               "R.layout.super_status_bar could not be properly inflated");
   }
   return mStatusBarWindowView;
}

The above function is very simple, namely:

  1. First call the injectable() function of InjectionInflationController, which returns a LayoutInflater object
  2. And call the inflate() function of this object. Then return the corresponding View object. This object is the top-level View of the entire StatusBar window called StatusBarWindowView.

As you can tell from the name, this class is related to injection. So let's take a look at the work that InjectionInflationController has to do.

InjectionInflationController

In the constructor of InjectionInflationController, @Inject is placed, so Dagger2 is responsible for its creation. The constructor is as follows:

@Inject
public InjectionInflationController(ViewInstanceCreator.Factory viewInstanceCreatorFactory) {
    
    
    mViewInstanceCreatorFactory = viewInstanceCreatorFactory;
    initInjectionMap();
}

private void initInjectionMap() {
    
    
	//获取ViewInstanceCreator的方法
    for (Method method : ViewInstanceCreator.class.getDeclaredMethods()) {
    
    
    	//如果是返回类型是View的子类,且为Public,那么就将其加入一个Map中.
    	//可见,这个InjectionInflationControllerView,缓存了View类的对象的创建方法.
        if (View.class.isAssignableFrom(method.getReturnType())
                && (method.getModifiers() & Modifier.PUBLIC) != 0) {
    
    
            mInjectionMap.put(method.getReturnType().getName(), method);
        }
    }
}

In the above function, the InjectionInflationController object caches ViewInstanceCreator's method of creating a View subclass. Let's take a look at ViewInstanceCreator. As follows:

ViewInstanceCreator

ViewInstanceCreator is defined in the InjectionInflationController file. The source code is as follows:

//定义为子组件,被放入Dagger2中
@Subcomponent
public interface ViewInstanceCreator {
    
    

    //定义其Factory方法
    @Subcomponent.Factory
    interface Factory {
    
    
        ViewInstanceCreator build(
                @BindsInstance @Named(VIEW_CONTEXT) Context context,
                @BindsInstance AttributeSet attributeSet);
    }

    //定义返回NotificationStackScrollLayout的对象
    //而这个对象是View的子类,这个对象的创建也由Dagger2创建
    NotificationStackScrollLayout createNotificationStackScrollLayout();
}

Now we know who cached the InjectionInflationController object when it was created, namely the -------NotificationStackScrollLayout object

Now you need to think about it, why is only one View defined in this way? Can't other Views be defined?

This involves a historical issue. In early versions, it also included the creation of Views such as QSPanel, QuickQSPanel, and KeyguardMessageArea. However, as the complexity of SystemUI increased, the corresponding Dagger diagram was slowly expanded. During testing, It was found that the classes and interfaces that exist in Dagger will exist in a very simple form, or even as empty objects. This results in a lot of testing work that cannot be completed. Therefore, this method will be slowly removed.

Therefore, the correct approach now is to avoid creating Dagger2 for View as much as possible, but to create another xxxController object. In the xxxController object, inject various required dependencies through Dagger2. Then combine this View and the corresponding Controller Attach

But there is still a View created by Dagger2, so let's see how SystemUI implements this process. That is, starting from the injectable function of InjectinInflationController.

injectable() function of InjectionInflationController

The source code is as follows:

public LayoutInflater injectable(LayoutInflater inflater) {
    
    
	//首先从克隆一个LayoutInflater
    LayoutInflater ret = inflater.cloneInContext(inflater.getContext());
    //然后为克隆的LayoutInflater设置一个私有的工厂对象
    //这个对象在创建某个View之前,会触发这个私有的工厂对象,好让这个私有对象实现一些自定义创建
    ret.setPrivateFactory(mFactory);
    return ret;
}

The function is relatively simple, just see the comments.

The implementation of the mFactory object is: the InjectionFactory class in the InjectionInflationController file, as follows:

InjectionFactory

The source code is as follows:

private class InjectionFactory implements LayoutInflater.Factory2 {
    
    

   @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
    
    
       //首先查看当初在构造函数中保存的创建View的方法
        Method creationMethod = mInjectionMap.get(name);
        //如果创建方法存在,则调用创建方法来创建View对象,
        //到目前为止它只会创建NotificationStackScrollLayout
        if (creationMethod != null) {
    
    
            try {
    
    
                return (View) creationMethod.invoke(
                        mViewInstanceCreatorFactory.build(context, attrs));
            } catch (IllegalAccessException e) {
    
    
                throw new InflateException("Could not inflate " + name, e);
            } catch (InvocationTargetException e) {
    
    
                throw new InflateException("Could not inflate " + name, e);
            }
        }
        return null;
    }

    @Override
    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
    
    
        return onCreateView(name, context, attrs);
    }
}

At this point, the process of creating View for InjectionInflationController is clear.

  1. First collect the methods to create View
  2. If the View in the Xml file happens to be created by the method collected in 1, then call the method in 1

From the above reading, we can see that Dagger2 is not suitable for all situations. In fact, it is mainly due to testing reasons.

At this point, the analysis of the super_status_bar.xml file is completed.


Previously, we analyzed the parsing of the super_status_bar.xml file in detail and learned how it is created as a View step by step. The next thing is the operation on this UI.

Create a suitable CollapsedStatusBarFragment

From the previous blog post ("Android 12 source code analysis - application layer five (startup process of SystemUI's StatusBar class and creation of three windows) http://t.csdn.cn/n3gwH" ) , once it is successfully created The top-level View of the window will add a reusable Fragment to this top-level View. The code for adding Fragment is in makeStatusBarView(). Now we will intercept the part about the StatusBar window as follows:

protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
    
    
    final Context context = mContext;
//省略不相干的部分

//调用FragmaneHostManager得到对应的FragmentHostManager
//然后调用addTagListener设置Fragment创建完成之后的一个监听
//最后调用getFragmentManager()得到FragmentManager开始Fragment的事务
//Fragment事务中,只有replace,将对应的Framgment添加到status_bar_container中
    FragmentHostManager.get(mPhoneStatusBarWindow)
            .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
    
    
               /*上一篇博文中已经详细介绍过*/
            }).getFragmentManager()
            .beginTransaction()
            .replace(R.id.status_bar_container,
                    new CollapsedStatusBarFragment(
                            mOngoingCallController,
                            mAnimationScheduler,
                            mStatusBarLocationPublisher,
                            mNotificationIconAreaController,
                            mFeatureFlags,
                            mStatusBarIconController,
                            mKeyguardStateController,
                            mNetworkController,
                            mStatusBarStateController,
                            this,
                            mCommandQueue
                    ),
                    CollapsedStatusBarFragment.TAG)
            .commit();

//省略不相干的部分
}

From the above content, we can know that the creation of Fragment is completed by the following steps:

  1. Call FragmaneHostManager to get the corresponding FragmentHostManager
  2. Then call addTagListener to set a listener after the Fragment is created, which is used for preparations after the Fragment's View is created.
  3. Finally, call getFragmentManager() to get the FragmentManager to start the Fragment transaction.
  4. In the Fragment transaction, only replace adds the corresponding Fragment to status_bar_container.

So what does the FragmentHostManager do here? Let’s take a look first.

FragmentHostManager的get()

The source code is as follows:

public static FragmentHostManager get(View view) {
    
    
    try {
    
    
    	//调用FragmentService的getFragmentHostManager()方法
        return Dependency.get(FragmentService.class).getFragmentHostManager(view);
    } catch (ClassCastException e) {
    
    
        // TODO: Some auto handling here?
        throw e;
    }
}

In FragmentService, getFragmentHostManager is as follows:

FragmentService的getFragmentHostManager()

The source code is as follows:

public FragmentHostManager getFragmentHostManager(View view) {
    
    
	//首先获取该View的根View
    View root = view.getRootView();
    //根据根View,得到对应的FragmengHostState.
    //FragmentHostState类是一个包装类,它将FragmentHostManager和View包装在一起.
    //而FragmentHostManager类负责View对应的Fragment的事务,后文详解.
    FragmentHostState state = mHosts.get(root);
    //在FragmentService中,将View和FragmentHostState进行缓存,一旦需要则直接返回
    if (state == null) {
    
    
    	//我们现在看到的是StatusBar的第一初始化过程,因此为空,则new一个FragmentHostState
        state = new FragmentHostState(root);
        mHosts.put(root, state);
    }
    //返回state的FragmentHostManager对象
    return state.getFragmentHostManager();
}

We can see from the above code.

  1. FragmentService caches the corresponding object according to the key-value pair of View->FragmentHostState
  2. If there is no such save, create a FragmentHostState object. An important object encapsulated in FragmenHostState is FragmentHostManager. Each View has a FragmentHostManager object encapsulated in FragmentHostState.

The FragmenHostState object is very simple and only encapsulates it, so it will not be introduced again.

Next let's look at the FragmentHostManager object

FragmentHostManager

The source code of FragmentHostManager is as follows. In FragmentHostManager, you only need to know its initialization, because the rest of the content is all simple operations on the objects created by its initialization.

public class FragmentHostManager {
    
    
	//省略不相干部分
	
	FragmentHostManager(FragmentService manager, View rootView) {
    
    
		//在构造函数中,初始化各个成员
	    mContext = rootView.getContext();
	    mManager = manager;
	    mRootView = rootView;
	    //在创建初期,需要先保存配置,通过applyNewConfig将配置更新到mConfigChanges对象中
	    mConfigChanges.applyNewConfig(mContext.getResources());
	    //调用下面的函数,为Fragment设置生命周期的管理
	    createFragmentHost(null);
	}
	
	private void createFragmentHost(Parcelable savedState) {
    
    
		//FragmentController负责协调Fragment的生命周期
		//首先,创建FragmentController对象mFragments
	    mFragments = FragmentController.createController(new HostCallbacks());
	    //必须先调用attach()方法,该方法负责内部的一些类的成员的初始化,否则无法使用
	    mFragments.attachHost(null);
	    //创建对应的生命周期监听对象
	    mLifecycleCallbacks = new FragmentLifecycleCallbacks() {
    
    
	        @Override
	        public void onFragmentViewCreated(FragmentManager fm, Fragment f, View v,
	                Bundle savedInstanceState) {
    
    
	                //调用FragmentHostManager中的listener(内容简单略)
	            FragmentHostManager.this.onFragmentViewCreated(f);
	        }
	
	        @Override
	        public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {
    
    
	        //调用FragmentHostManager中的listener(内容简单略)
	            FragmentHostManager.this.onFragmentViewDestroyed(f);
	        }
	
	        @Override
	        public void onFragmentDestroyed(FragmentManager fm, Fragment f) {
    
    
	        	//跟踪Fragment的回收情况
	            Dependency.get(LeakDetector.class).trackGarbage(f);
	        }
	    };
	    //设置监听
	    mFragments.getFragmentManager().registerFragmentLifecycleCallbacks(
	    mLifecycleCallbacks,true);
	    //如果有保存的状态,则负责恢复这个保存的状态
	    if (savedState != null) {
    
    
	        mFragments.restoreAllState(savedState, (FragmentManagerNonConfig) null);
	    }
	    //现在将状态保持在resume状态.
	    //关于Fragment的生命周期,见后文补充知识
	    mFragments.dispatchCreate();
	    mFragments.dispatchStart();
	    mFragments.dispatchResume();
	}
	
	public FragmentHostManager addTagListener(String tag, FragmentListener listener) {
    
    
		//根据tag选择对应的listener组
	    ArrayList<FragmentListener> listeners = mListeners.get(tag);
	    //如果没有listener组,则创建一个新的list,用于存放这些listener
	    if (listeners == null) {
    
    
	        listeners = new ArrayList<>();
	        mListeners.put(tag, listeners);
	    }
	    listeners.add(listener);
	    //如果当前已经存在Fragment,那么就直接触发listener的回调
	    //可以思考为什么非要在此处触发?
	    Fragment current = getFragmentManager().findFragmentByTag(tag);
	    if (current != null && current.getView() != null) {
    
    
	        listener.onFragmentViewCreated(tag, current);
	    }
	    return this;
	}
	//省略简单部分
	
	//省略不相干部分
} 

FragmenHostManager is mainly responsible for the following:

  1. Manage the life cycle of Fragment.
  2. When the corresponding life cycle is triggered, the corresponding callback of the FragmentLifecycleCallbacks object in FragmentHostManager will be called.
  3. In these callback methods, the corresponding methods in FragmentHostManager will be dropped again, and these methods will trigger the Listener in FragmentHostManager. (That is, the listener set by calling addTagListener() in the makeStatusBarView() function)

Now we know FragmentHostManager. Then go back to the makeStatusBarView() function. After calling FragmentHostManager.get(mPhoneStatusBarWindow), get the FragmentHostManager, and then call the addTagListener(TAG, listener) method. The addTagListener has been introduced in detail in the FragmentHostManager above. .

Now let's answer the thoughts left in the listener - Why should its callback be triggered immediately?
Answer: The setting of the Listener may appear after the creation of the View. If it is after it, then the onFragmentViewCreated() method needs to be actively triggered. .

However, one issue that was not considered here is that the triggering thread may be in a sub-thread, so there is an unreasonable design here.

With the previous steps, you can get the corresponding FragmentManager object. In order to further explain the steps, the process in the previous makeStatusBarView() is summarized:

  1. Call FragmaneHostManager to get the corresponding FragmentHostManager
  2. Call addTagListener to set a listener after the Fragment is created, which is used for preparations after the Fragment's View is created.
  3. Finally, call getFragmentManager() to get the FragmentManager to start the Fragment transaction.
  4. In the Fragment transaction, only replace adds the corresponding Fragment to status_bar_container.

Among them, we have analyzed the first and second steps in detail. The next third and fourth steps are the basic operations of Android Fragment. It first creates the CollapsedStatusBarFragment and then places it in the status_bar_container with the id in the container.

Then let’s take a look at the creation of CollapsedStatusBarFragment

Creation of CollapsedStatusBarFragment

As a Fragment, we view it according to its life cycle, first looking at its constructor, as follows:

@Inject
//初始化函数乏善可陈,故略之
public CollapsedStatusBarFragment(
        OngoingCallController ongoingCallController,
        SystemStatusAnimationScheduler animationScheduler,
        StatusBarLocationPublisher locationPublisher,
        NotificationIconAreaController notificationIconAreaController,
        FeatureFlags featureFlags,
        StatusBarIconController statusBarIconController,
        KeyguardStateController keyguardStateController,
        NetworkController networkController,
        StatusBarStateController statusBarStateController,
        StatusBar statusBarComponent,
        CommandQueue commandQueue
) {
    
    
    //全是成员的赋值,没有什么好看的,略
}

Before looking at the life cycle, we first briefly list the process of its life cycle, as follows:

Fragment的生命周期:
Fragment被创建:
	onAttach(): Fragment与Activity关联时调用。
	onCreate(): Fragment被创建时调用,通常用于初始化成员变量和数据。
	onCreateView(): 创建Fragment的用户界面视图(布局)。
	onActivityCreated(): 当Fragment所属的Activity完成其onCreate()方法后调用。
Fragment变得可见:
	onStart(): Fragment变得可见时调用,可以执行一些初始化或准备工作。
	onResume(): Fragment完全可见时调用,通常用于启动动画、定时任务等。
Fragment被暂停:
	onPause(): Fragment即将暂停,通常用于保存数据或释放资源。
Fragment不再可见:
	onStop(): Fragment不再可见,用于释放更多的资源。
Fragment销毁:
	onDestroyView(): Fragment的视图被销毁,用于清理视图相关的资源。
	onDestroy(): Fragment被销毁时调用,通常用于释放资源。
	onDetach(): Fragment与Activity解除关联时调用。

According to the above life cycle, combined with the source code of CollapsedStatusBarFragment, first check the onCreateView function, as follows:

onCreateView()

The source code is very simple, parse the status_bar.xml file. We have introduced this file in "Android 12 Source Code Analysis - Application Layer Four (Basic Layout Design and Basic Concepts of SystemUI) http://t.csdn.cn/PSqGS " So we won’t continue the introduction. Next, we will explain step by step through graphics. The graphics are drawn manually to assist understanding and memory.

The content in the status_bar.xml file, omitting the status_bar_content in the middle, is as follows;

<com.android.systemui.statusbar.phone.PhoneStatusBarView>

    <ImageView/><!--不会显示为gone-->

    <LinearLayout android:id="@+id/status_bar_contents">
    <!--省略中间内容,便于说明-->
    </LinearLayout>

    <ViewStub
        android:id="@+id/emergency_cryptkeeper_text"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout="@layout/emergency_cryptkeeper_text"
    />

</com.android.systemui.statusbar.phone.PhoneStatusBarView>

Draw an auxiliary diagram as follows:
Insert image description here

Then, adding the status_bar_contents content in the middle becomes the following situation, only the contents in status_bar_content are listed in the xml

<LinearLayout android:id="@+id/status_bar_contents">
    <!--左边区域-->
    <FrameLayout>
        <!--左边区域的heads-up区域-->
        <include layout="@layout/heads_up_status_bar_layout" />
        <!--左边区域的主体内容-->
        <LinearLayout android:id="@+id/status_bar_left_side">
            <!--左边区域显示运营商名字-->
            <ViewStub android:id="@+id/operator_name" />
            <!--左边区域显示时钟-->
            <com.android.systemui.statusbar.policy.Clock
                android:id="@+id/clock"/>
            <!--左边区域显示通话-->
            <include layout="@layout/ongoing_call_chip" />
			<!--左边区域显示通知图标-->
            <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
                android:id="@+id/notification_icon_area"/>
        </LinearLayout>
    </FrameLayout>

    <!--用于填充空白-->
    <android.widget.Space
        android:id="@+id/cutout_space_view"/>
    <!--中心区域-->
    <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
        android:id="@+id/centered_icon_area"/>
	<!--系统图标区域,也即是右边区域-->
    <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area">

        <include layout="@layout/system_icons" />
    </com.android.keyguard.AlphaOptimizedLinearLayout>
</LinearLayout>

To aid understanding, draw the following diagram:

Insert image description here

Once the View is created, the onViewCreated() function will be triggered, as follows:

onViewCreated()

The source code of onViewCreated() is as follows:

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    
    
    super.onViewCreated(view, savedInstanceState);
    //view即为onCreateView()函数中解析的status_bar.xml中的顶层View----PhoneStatusBarView
    mStatusBar = (PhoneStatusBarView) view;
    //status_bar_contents内容,负责显示主要的statusbar内容,见上面的示意图
    View contents = mStatusBar.findViewById(R.id.status_bar_contents);
    //监听,Layout的改变,一旦发生改变,就调用updateStatusBarLocation()
    contents.addOnLayoutChangeListener(mStatusBarLayoutListener);
    //因此需要先调用updateStatusBarLocation()已进行某些值的初始设置
    updateStatusBarLocation(contents.getLeft(), contents.getRight());
    //如果需要恢复,则调用restoreHierarchyState进行恢复(此处我们将暂时跳过,因为其内部直接
    //调用Android View提供标准操作.如果在后面有关于AndroidView的分析,将会进行介绍)
    if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {
    
    
        mStatusBar.restoreHierarchyState(
                savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));
    }
    //创建DarkIconManager,负责接收DarkIconDispatcher中的状态变化
    mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons), mFeatureFlags);
    //再设置,不应该被显示的图标
    mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_volume));
    mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_alarm_clock));
    mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_call_strength));
    //将不应该显示的图标,传递给DarkIconManager
    mDarkIconManager.setBlockList(mBlockedIcons);
    //StatusBarIconController负责接收CommandQueue中的回调,同时也跟踪icon的状态
    //将这些状态分发给需要的类(IconManager)
    //DarkIconManager继承于IconManager,它需要得到StatusBarIconController中的各种通知.
    //因此调用addIconGroup将mDarkIconManager传递进去
    mStatusBarIconController.addIconGroup(mDarkIconManager);
    //获取系统图标的区域,它在状态栏的最右边,见上面的示意图
    mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
    //获取对应的UI,这个是时间图标,见上面示意图
    mClockView = mStatusBar.findViewById(R.id.clock);
    //获取对应的通话图标,见上面的示意图
    mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip);
    //显示系统图标区域,不带动画
    showSystemIconArea(false);
    //显示时钟,不带动画
    showClock(false);
    //初始化,用于显示电话的状态,如,只能用于紧急呼救
    initEmergencyCryptkeeperText();
    //初始化运营商名字,如CMCC中国移动
    initOperatorName();
    //初始化通知图标区域
    initNotificationIconArea();
    //增加动画的回调,这部分在后面文章中会介绍
    mAnimationScheduler.addCallback(this);
}

The main functions of the above function are as follows:

  1. Find View based on id
  2. Initialize DarkIconManager
  3. Display system icon area, display clock area, display notification icon area

Next let's take a look at the content of initEmergencyCryptkeeperText

Initialization of initEmergencyCryptkeeperText

The source code is as follows:

private void initEmergencyCryptkeeperText() {
    
    
	//得到对应的ViewStub,见上面的示意图
    View emergencyViewStub = mStatusBar.findViewById(R.id.emergency_cryptkeeper_text);
    //判断是否要显示:紧急通话,sim卡状态的文本
    if (mNetworkController.hasEmergencyCryptKeeperText()) {
    
    
    	//如果要,且有对应的View,则inflate它
    	//inflate的内容,即为emergency_cryptkeeper_text.xml,该文件内容简单只有一个类
    	//不做介绍
        if (emergencyViewStub != null) {
    
    
            ((ViewStub) emergencyViewStub).inflate();
        }
        //监听网络相关变化,如信号强度,sim卡的更改,飞行模式等等
        mNetworkController.addCallback(mSignalCallback);
    } else if (emergencyViewStub != null) {
    
    
    	//如果没有,则删除对应的view
        ViewGroup parent = (ViewGroup) emergencyViewStub.getParent();
        parent.removeView(emergencyViewStub);
    }
}

The content is simple, see the comments, and then look at another initialization function initOperatorName, as follows

Initialization of initOperatorName

The source code is as follows:

private void initOperatorName() {
    
    
	//根据配置是否要显示,运营商名字,如中国移动显示CMCC
    if (getResources().getBoolean(R.bool.config_showOperatorNameInStatusBar)) {
    
    
    	//如果需要显示,则inflate其中的Layout,见上面的示意图
    	//当inflate时,解析的文件为:operator_name.xml文件,该文件也只有一个类
    	//相对简单,不再展开
        ViewStub stub = mStatusBar.findViewById(R.id.operator_name);
        mOperatorNameFrame = stub.inflate();
    }
}

The content is simple, see the comments, and then look at another initialization function initNotificationIconArea, as follows

Initialization of initNotificationIconArea

The source code is as follows:

public void initNotificationIconArea() {
    
    
	//获得显示通知图标的父容器(StatusBar的左边区域,见上面的示意图)
    ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);
    //获取显示通知图标的具体的View,通过mNotificationIconAreaController内部inflate
    //notification_icon_area.xml文件得到
    mNotificationIconAreaInner =
            mNotificationIconAreaController.getNotificationInnerAreaView();
    //如果通知图标的View有父View,则从中删除,并将其加入notificatonIconArea即左边区域
    if (mNotificationIconAreaInner.getParent() != null) {
    
    
        ((ViewGroup) mNotificationIconAreaInner.getParent())
                .removeView(mNotificationIconAreaInner);
    }
    //加入左边区域
    notificationIconArea.addView(mNotificationIconAreaInner);

	//获得显示中心图标的区域的父容器(StatusBar的中心区域)
    ViewGroup statusBarCenteredIconArea = mStatusBar.findViewById(R.id.centered_icon_area);
    //获取显示中心图标的具体的View,通过mNotificationIconAreaController内部inflate文件
    //center_icon_area.xml得到
    mCenteredIconArea = mNotificationIconAreaController.getCenteredNotificationAreaView();
    //如果具体的View有父View,则从中删除,并将其加入中心区域
    if (mCenteredIconArea.getParent() != null) {
    
    
        ((ViewGroup) mCenteredIconArea.getParent())
                .removeView(mCenteredIconArea);
    }
    //加入中心区域
    statusBarCenteredIconArea.addView(mCenteredIconArea);

    //一切OK之后,还需要根据disable来处理,哪些图标不要显示,关于disable的说明,见上一篇博文
    updateNotificationIconAreaAndCallChip(mDisabled1, false);
}

In the above code, the code used to obtain the specific display View is obtained through mNotificationIconAreaController. Inside this controller, it inflates two files respectively: notification_icon_area.xml and center_icon_area.xml. Returns their top-level View to CollapsedStatusBarFragment

Note: For the sake of smooth writing, the content inflate of NotificationIconAreaController will no longer be expanded. We will introduce it in detail by analyzing UI interactions (such as icon color changes) in later articles.

After having these preparations, we need to judge based on disableFlag which ones need to be displayed and which ones do not need to be displayed. The updateNotificationIconAreaAndCallChip() function will be called, as follows:

updateNotificationIconAreaAndCallChip determines the icon that needs to be displayed

The parameters for calling the updateNotificationIconAreaAndCallChip() function: mDsiabled1. are obtained through the disbale() interface callback. For this part, please see the disabaleFlag content in the previous blog post "Android 12 Source Code Analysis - Application Layer 5 (Startup Process of SystemUI's StatusBar Class and the creation of three windows) http://t.csdn.cn/YTh8c "

Note: The distribution of disableFlag in the previous blog post will appear after this function is called. But it does not affect our analysis of this function.

The source code is as follows:

private void updateNotificationIconAreaAndCallChip(int state1, boolean animate) {
    
    
	//是否要禁止通知图标
    boolean disableNotifications = (state1 & DISABLE_NOTIFICATION_ICONS) != 0;
    //是否要禁止通话图标
    boolean hasOngoingCall = (state1 & DISABLE_ONGOING_CALL_CHIP) == 0;

    //根据设置,调用showxxx或者hidexxx函数,进行显示或者隐藏
    //这两个函数,内部通过调用animateShow()或者调用animateHide()进行显示或者隐藏
    if (disableNotifications || hasOngoingCall) {
    
    
        hideNotificationIconArea(animate);
    } else {
    
    
        showNotificationIconArea(animate);
    }

    //逻辑同通知图标一样
    boolean showOngoingCallChip = hasOngoingCall && !disableNotifications;
    if (showOngoingCallChip) {
    
    
        showOngoingCallChip(animate);
    } else {
    
    
        hideOngoingCallChip(animate);
    }
    //通知状态变化
    mOngoingCallController.notifyChipVisibilityChanged(showOngoingCallChip);
}

The above function only does two things:

  1. Determine whether to display the notification icon based on disableFlag
  2. Determine whether to display the call icon based on disableFlag

Note: Regarding the display of animation, we will explore disableFlag in detail later.

At this point, the View of CollapsedStatusBarFragment has been created. It mainly completes the following work:

  1. Find the corresponding View by id
  2. Initialize DarkIconManager
  3. Initialize various views, such as operator, system icon area, notification icon area, and middle icon area.

In short, this function is responsible for initializing the Views inside this Fragment. Once these Views are created, they need to be initialized between other external Controllers. Therefore, it will trigger the return of another life cycle of another Fragmenet. That is, the previous The callbacks of FragmentLifecycleCallbacks of FragmentHostManager in this article.

The listener after the UI creation of CollapsedStatusBarFragment is completed is responsible for further initialization.

When all UIs are created, the Listener of FragmentHostManager set in the makeStatusBarView() function will be triggered. This part of the content has been introduced in detail in the previous blog post, and is now excerpted as follows:

FragmentHostManager.get(mPhoneStatusBarWindow)
  .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
    
    
      //将Fragment转换成CollapsedStatusBarFragment
      CollapsedStatusBarFragment statusBarFragment =
              (CollapsedStatusBarFragment) fragment;

      //先保存以前的PhoneStatusBarView
      PhoneStatusBarView oldStatusBarView = mStatusBarView;
      //再获取新的PhoneStatusBarView
      mStatusBarView = (PhoneStatusBarView) statusBarFragment.getView();
      //对PhoneStatusBarView进行设置,包括注册监听,传递需要的对象等
      mStatusBarView.setBar(this);
      mStatusBarView.setPanel(mNotificationPanelViewController);
      mStatusBarView.setScrimController(mScrimController);
      mStatusBarView.setExpansionChangedListeners(mExpansionChangedListeners);

      //如果有一个heads up在顶部,则需要调整为正确的状态
      if (mHeadsUpManager.hasPinnedHeadsUp()) {
    
    
          //主要是通知各个监听器,更新各自的状态
          mNotificationPanelViewController.notifyBarPanelExpansionChanged();
      }
      //初始化PhoneStatusBarView中的Bouncer
      //Bouncer界面就是,解锁时要求输入pin,密码等的界面
      //新创建的PhoneStatusBarView中的Bouncer默认是没有打开的。因此要根据
      //当前实际进行更新
      mStatusBarView.setBouncerShowing(mBouncerShowing);
      //如果以前的StatusBar已经展开了一部分,则要求新的StatusBar也展开同样的大小
      //因为新创建的PhoneStatusBar,默认展开是0
      if (oldStatusBarView != null) {
    
    
          float fraction = oldStatusBarView.getExpansionFraction();
          boolean expanded = oldStatusBarView.isExpanded();
          mStatusBarView.panelExpansionChanged(fraction, expanded);
      }

      //重新创建HeadsUpAppearanceController
      //它负责控制在StatusBar中要显示的headsup的图标,主要是heads_up_status_bar_view
      //的操作,见上文示意图
      HeadsUpAppearanceController oldController = mHeadsUpAppearanceController;
      if (mHeadsUpAppearanceController != null) {
    
    
          mHeadsUpAppearanceController.destroy();
      }
      //重新创建一个新的HeadsUpAppearanceController对象
      mHeadsUpAppearanceController = new HeadsUpAppearanceController(
              mNotificationIconAreaController, mHeadsUpManager,
              mStackScroller.getController(),
              mStatusBarStateController, mKeyguardBypassController,
              mKeyguardStateController, mWakeUpCoordinator, mCommandQueue,
              mNotificationPanelViewController, mStatusBarView);
      //从旧对象中读取值赋值给新对象
      mHeadsUpAppearanceController.readFrom(oldController);
      
      //建立LightsOutNotificationController和对应View的关系
      //LightsOutNotificationController负责处理SYSTEM_UI_FLAG_LOW_PROFILE中的
      //图标的消失和隐藏,负责显示小圆点
      //SYSTEM_UI_FLAG_LOW_PROFILE:这个属性的能力是让SystemBar在视觉上变得模糊,
      //具体表现是状态栏图标仅保留电量时间关键图标,并且变暗。导航栏图标变成三个点或者变暗
      mLightsOutNotifController.setLightsOutNotifView(
              mStatusBarView.findViewById(R.id.notification_lights_out));
      //将StatusBar窗口的view,放入NotificationShade窗口的控制器中
      mNotificationShadeWindowViewController.setStatusBarView(mStatusBarView);
      //检查是否要进行StatusBar的模式切换,如果要切换则会形成一个过渡动画
      //关于StatusBar模式切换的细节,我们在介绍完StatusBarWindow的整个初始化过程之后,
      //另外的文章进一步介绍.此处我们关注整个启动过程
      checkBarModes();
  }).getFragmentManager()
  .beginTransaction()
  .replace(R.id.status_bar_container,
          new CollapsedStatusBarFragment(
                  mOngoingCallController,
                  mAnimationScheduler,
                  mStatusBarLocationPublisher,
                  mNotificationIconAreaController,
                  mFeatureFlags,
                  mStatusBarIconController,
                  mKeyguardStateController,
                  mNetworkController,
                  mStatusBarStateController,
                  this,
                  mCommandQueue
          ),
          CollapsedStatusBarFragment.TAG)
  .commit();

The above code is to create and initialize the corresponding objects after the View is created. The creation and initialization of these objects is mainly to separate the CollapsedStatusBarFragment itself, so it is placed in an independent Listener.

Note: It doesn’t matter if you don’t understand the content in the Listener callback above. We will introduce its functions in detail later when we slowly introduce each class. This involves specific UI interactions.

At the same time, in order to understand the triggering of this Listener, a call stack picture is posted here. The reason why it is not further analyzed is because this part does not belong to the content of SystemUI, but the content of Android Fragment.

Insert image description here

Then when everything is ready, the entire UI display will look like this:

Insert image description here

At this point, you will enter the next life cycle function onResume.


onResume

After completing the entire View, its onResume function() will be triggered as follows:

@Override
public void onResume() {
    
    
    super.onResume();
    //接收CommandQueue中的命令,CommandQueue,见上一篇博文  
    mCommandQueue.addCallback(this);
    //接收StatusBarState的改变,StatusBarState,见上一篇博文的介绍
    mStatusBarStateController.addCallback(this);
    //初始化化通话图标
    initOngoingCallChip();
}
Details to note with mCommandQueue.addCallback()

In addCallback(), a very inconspicuous but important operation is performed, as follows:

@Override
public void addCallback(@NonNull Callbacks callbacks) {
    
    
    mCallbacks.add(callbacks);
    //主动调用disable(),其实此处调用disable()跟前文中,主动调用onFragmentViewCreated一样.
    for (int i = 0; i < mDisplayDisabled.size(); i++) {
    
    
        int displayId = mDisplayDisabled.keyAt(i);
        int disabled1 = getDisabled1(displayId);
        int disabled2 = getDisabled2(displayId);
        //这将触发CollapsedStatusBarFragment的disable函数被调用
        callbacks.disable(displayId, disabled1, disabled2, false /* animate */);
    }
}

The above function is very simple:

  1. save callback
  2. disable that triggers the callback

It will trigger the disable() function of CollapsedStatusBarFragment, as follows:

Exploring the processing of disableFlags

The disable() function source code is as follows:

//其中的state1就是disable1Flag
//state2就是disable2Flag
@Override
public void disable(int displayId, int state1, int state2, boolean animate) {
    
    
	//是否是同一个逻辑屏幕,如果不是则会返回
    if (displayId != getContext().getDisplayId()) {
    
    
        return;
    }
    //需要对传入的state1做一下调整,在这个调整函数中,它会主动调用各个Controller判断
    //要不要显示某些UI,然后将相应的标志位设置好,最终将其返回.该函数较简单,不再展开
    state1 = adjustDisableFlags(state1);
    //下面是一些位操作,用于得到哪些位被改变了
    final int old1 = mDisabled1;
    final int diff1 = state1 ^ old1;
    final int old2 = mDisabled2;
    final int diff2 = state2 ^ old2;
    mDisabled1 = state1;
    mDisabled2 = state2;
    if ((diff1 & DISABLE_SYSTEM_INFO) != 0 || ((diff2 & DISABLE2_SYSTEM_ICONS) != 0)) {
    
    
        if ((state1 & DISABLE_SYSTEM_INFO) != 0 || ((state2 & DISABLE2_SYSTEM_ICONS) != 0)) {
    
    
        	//隐藏,animate表示是否使用动画,此处传递过来为false
            hideSystemIconArea(animate);
            hideOperatorName(animate);
        } else {
    
    
            showSystemIconArea(animate);
            showOperatorName(animate);
        }
    }

   //依然更新相应的图标要不要显示,他们依然是调用show/hidexxxx()完成,其中xxx表示的是某个View
    if (((diff1 & DISABLE_ONGOING_CALL_CHIP) != 0)
            || ((diff1 & DISABLE_NOTIFICATION_ICONS) != 0)) {
    
    
        updateNotificationIconAreaAndCallChip(state1, animate);
    }

    //下面的代码,依然使用show/hidexxx()函数完成,其中xxx表示的是某个View
    if ((diff1 & DISABLE_CLOCK) != 0 || mClockView.getVisibility() != clockHiddenMode()) {
    
    
        if ((state1 & DISABLE_CLOCK) != 0) {
    
    
            hideClock(animate);
        } else {
    
    
            showClock(animate);
        }
    }
}

The above function is also very simple:

  1. Get the corresponding bit from diableFlag
  2. Determine whether to show or hide a View based on bits
  3. Showing or hiding is all done through the show/hidexxx() function

Next, let's take a look at the specific process of the show/hidexxx() function. Let's take the first showSystemIconArea() in the onVIewCreated() function as an example. As follows:

showSystemIconArea()

The source code is as follows:

private void showSystemIconArea(boolean animate) {
    
    
     //先判断动画的状态,现在还在初始的流程中,state为IDEL
     //在下一篇文章中,将会介绍AnimationScheduler的作用
     int state = mAnimationScheduler.getAnimationState();
     if (state == IDLE || state == SHOWING_PERSISTENT_DOT) {
    
    
         //调用animateShow()函数,是否要通过动画显示,此处为传递的值为false
         animateShow(mSystemIconArea, animate);
     }
 }

The above function is completed:

  1. Whether to call the animateShow() function, which will be responsible for animation

Before continuing to look at the animateShow() function, it is necessary to check whether other show/hidexxx() functions are the same. Fortunately, almost all show/hidexxx() functions will eventually call animateShow() or animateHiddenStatte ().

Next let’s look at the animateShow() function

animateShow()

The animateShow() function is responsible for whether to coordinate animation when showing a View.

private void animateShow(View v, boolean animate) {
    
    
	//先取消View中的动画
	v.animate().cancel();
	//再设置View位可见
	v.setVisibility(View.VISIBLE);
	//如果不需要动画,则直接将透明度设置为1,并返回
	if (!animate) {
    
    
	   v.setAlpha(1f);
	   return;
	}
	//如果需要动画
	v.animate()
	       .alpha(1f)//设置最终透明度为1
	       .setDuration(FADE_IN_DURATION)//动画运行时间
	       .setInterpolator(Interpolators.ALPHA_IN)//设置插值器
	       .setStartDelay(FADE_IN_DELAY)//延迟多久开始动画
	       .withEndAction(null);//动画结束完成执行的动作,此处为空,历史原因才有这句话
	
	//如果锁屏的消失动画正在运行,则可以将两个动画,联合起来
	//重新设置对应的duration,startdelay
	//然后start()
	if (mKeyguardStateController.isKeyguardFadingAway()) {
    
    
	   v.animate()
	           .setDuration(mKeyguardStateController.getKeyguardFadingAwayDuration())
	           .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
	           .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
	           .start();
	}
}

The above function is also very simple and only does one thing:

  1. Animate the transparency property.

The same animateHiddenState() function is the reverse process of this function. It also sets the attribute animation of transparency and does not expand it again.

Let's continue to look at the rest of onResume(). It's very simple to add a callback to mStatusBarStateController, so let's look at the next sentence, initOngoingCallChip. As follows:

Initialization of initOngoingCallChip

The source code is as follows. The entire source code is very simple. Add monitoring and View to the corresponding controller.

private void initOngoingCallChip() {
    
    
    mOngoingCallController.addCallback(mOngoingCallListener);
    mOngoingCallController.setChipView(mOngoingCallChip);
}

The function is also very simple and there is nothing to go into detail about.

At this point, the entire Fragment creation and initialization life cycle is completed.

The next step is to add the corresponding Window to the screen

Add UI to screen

In the previous analysis, we only created the entire UI system, but we have not yet added it to the screen. Adding it to the screen is also in the createAndAddWindwos() function of StatusBar, as follows

public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
    
    
    //创建对应的UI
    makeStatusBarView(result);
    //将NotificationShadeWindow加入屏幕
    mNotificationShadeWindowController.attach();
    //将StatusBarWinidow加入屏幕
    mStatusBarWindowController.attach();
}

For the StatusBar window, call mStatusBarWindowController.attach(); to add it to the screen. The code content is:

public void attach() {
    
    
    //创建对应的WindowManager.LayoutParams对象
    //该对象用于描述窗口的属性和参数
    //在这里,我们要求其宽度为:MATCH_PARENT
    //高度,为一个固定值:即mBarHeight
    //Window类型为:TYPE_STATUS_BAR,这是抓们为StatusBar设计的窗口类型
    //window的标志有:不能被聚焦,支持窗口之外的触摸,支持自己绘制SystemBar背景
    //像素格式为:PixelFormat.TRANSLUCENT它有更多的bit用来存放透明信息
    mLp = new WindowManager.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            mBarHeight,
            WindowManager.LayoutParams.TYPE_STATUS_BAR,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                    | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
            PixelFormat.TRANSLUCENT);
    mLp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
    //然后一个窗口都需要一个Binder作为token
    mLp.token = new Binder();
    mLp.gravity = Gravity.TOP;
    mLp.setFitInsetsTypes(0 /* types */);
    //窗口的名字
    mLp.setTitle("StatusBar");
    mLp.packageName = mContext.getPackageName();
    mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
	
	//调用WindowManager的addView从此刻开始,就能在屏幕中看到对应的UI了
    mWindowManager.addView(mStatusBarView, mLp);
    //保存旧的Window参数,用于在后面参数更改之后,进行对比
    mLpChanged.copyFrom(mLp);
	//下面两句的内容,我们将会在下一篇文章的StatusBar的UI交互中进行介绍,现在不需要知道
    mContentInsetsProvider.addCallback(this::calculateStatusBarLocationsForAllRotations);
    calculateStatusBarLocationsForAllRotations();
}

The above function is also very simple and accomplishes one thing:

  1. Add the corresponding View to the screen.

At this point, the entire StatusBar UI creation process has been introduced. The reverse process of the StatusBar UI, that is, the destruction or reconstruction of the StatusBar, is almost the reverse process of the above content, or a repeated process, and there is no need to introduce it again.

The next article will introduce the interaction of UI in StatusBar

PS: There will inevitably be errors in the article. If you find any, please criticize and correct them.

Guess you like

Origin blog.csdn.net/xiaowanbiao123/article/details/132974770