Android之SystemUI加载流程和NavigationBar的分析

Android之SystemUI加载流程和NavigationBar的分析

本篇只分析SystemUI的加载过程和SystemUI的其中的一个模块StatusBar的小模块NavigationBar,以Android6.0代码进行分析

https://www.2cto.com/kf/201604/499625.html  挺详细的吧,懒得自己写了

AndroidManifest.xml

1

2

3

4

5

6

7

8

9

10

11

    <!-- Keep theme in sync with SystemUIApplication.onCreate().

         Setting the theme on the application does not affect views inflated by services.

         The application theme is set again from onCreate to take effect for those views. -->

    <!-- Broadcast receiver that gets the broadcast at boot time and starts

         up everything else.

         TODO: Should have an android:permission attribute

         -->

    <service android:exported="true" android:name="SystemUIService">

</service></application>

SystemUIService

跟StatusBar相关的服务为SystemUIService,我们查看SystemUIService源码

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

public class SystemUIService extends Service {

    @Override

    public void onCreate() {

        super.onCreate();

        ((SystemUIApplication) getApplication()).startServicesIfNeeded();

        //获取Application调用startServicesIfNeeded

    }

    @Override

    public IBinder onBind(Intent intent) {

        return null;

    }

    /*打印堆栈信息*/

    @Override

    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {

        SystemUI[] services = ((SystemUIApplication) getApplication()).getServices();

        if (args == null || args.length == 0) {

            for (SystemUI ui: services) {

                pw.println("dumping service: " + ui.getClass().getName());

                ui.dump(fd, pw, args);

            }

        } else {

            String svc = args[0];

            for (SystemUI ui: services) {

                String name = ui.getClass().getName();

                if (name.endsWith(svc)) {

                    ui.dump(fd, pw, args);

                }

            }

        }

    }

}

分析SystemUIService代码,可以知道SystemUI主要做了两件事

获取Application对象加载SystemUI相关的类,这个等下分析SystemUIApplication代码可以知道 dump打印SystenUISerice运行过程中相关的堆栈信息

那么SystemUIService又是哪里开始启动的呢?竟然SystemUIService是个服务,那么启动服务要么就是startService
要么就是bindService进行启动,其启动方式则需要通过Intent来传入类名或者包名,因此在源码中搜索SystemUIService可以对比发现,它在
frameworks\base\services\java\com\android\server\SystemServer.java中进行启动

?

1

2

3

4

5

6

7

static final void startSystemUi(Context context) {

    Intent intent = new Intent();

    intent.setComponent(new ComponentName("com.android.systemui",

                "com.android.systemui.SystemUIService"));

    //Slog.d(TAG, "Starting service: " + intent);

    context.startServiceAsUser(intent, UserHandle.OWNER);

}

在SystemServer的run方法中startOtherServices来启动SystemUIService服务,至于SystemServer则涉及到Android的启动流程,其大概流程为

?

1

2

int -> ServerManager -> Zygote -> SystemServer

SystemServer中会初始化一些Android的java层的服务,如ActivityManagerService、WindowManagerService等

这里SystemUI的加载过程就到此告一段落了,下面分析StatusBar的加载流程

上面讲到在SystemUIService的onCreate中获取SystemUIApplication对象来初始化SystemUI相关的类,这些类里面就包括了StatusBar相关的类,我们查看SystemUIApplication类

SystemUIApplication

onCreate

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

@Override

public void onCreate() {

    super.onCreate();

    // Set the application theme that is inherited by all services. Note that setting the

    // application theme in the manifest does only work for activities. Keep this in sync with

    // the theme set there.

    setTheme(R.style.systemui_theme);

    //注释广播

    IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);

    filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);

    registerReceiver(new BroadcastReceiver() {

        @Override

        public void onReceive(Context context, Intent intent) {

            //开启直接返回

            if (mBootCompleted) return;

            if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");

            unregisterReceiver(this);

            //标记启动

            mBootCompleted = true;

            //服务是否启动

            if (mServicesStarted) {

                final int N = mServices.length;

                for (int i = 0; i < N; i++) {

                    //回调各服务的onBootCompleted函数

                    mServices[i].onBootCompleted();

                }

            }

        }

    }, filter);

}

在SystemUIApplication的onCreate中主要做了

设置主题(这个会影响其SystemUI的界面显示效果) 注册开机广播,设置标志位

startServicesIfNeeded

SystemUIService中的onCreate启动了这个方法,我们着重分析这个方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

public void startServicesIfNeeded() {

    if (mServicesStarted) {

        return;

    }

    if (!mBootCompleted) {

        // check to see if maybe it was already completed long before we began

        // see ActivityManagerService.finishBooting()

        //获取系统文件中的sys.boot_completed的值

        if ("1".equals(SystemProperties.get("sys.boot_completed"))) {

            mBootCompleted = true;

            if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent");

        }

    }

    Log.v(TAG, "Starting SystemUI services.");

    final int N = SERVICES.length;

    for (int i=0; i<n;> cl = SERVICES[i];

        if (DEBUG) Log.d(TAG, "loading: " + cl);

        //实例化各个类实例,放入mServices数组中

        try {

            mServices[i] = (SystemUI)cl.newInstance();

        } catch (IllegalAccessException ex) {

            throw new RuntimeException(ex);

        } catch (InstantiationException ex) {

            throw new RuntimeException(ex);

        }

        mServices[i].mContext = this;

        mServices[i].mComponents = mComponents;

        if (DEBUG) Log.d(TAG, "running: " + mServices[i]);

        mServices[i].start();

        if (mBootCompleted) {

            mServices[i].onBootCompleted();

        }

    }

    //服务启动标志

    mServicesStarted = true;

}

这个方法中,首先判断mServicesStarted标志为来判断SystemUI相关的服务是否启动,同时根据系统配置文件来检查ActivityManagerService是否finishBooting,然后通过类加载机制来初始化SERVICES数组里面相关的类加入mServices中,然后start

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

/**

 * The classes of the stuff to start.

 */

private final Class<!--?-->[] SERVICES = new Class[] {

        com.android.systemui.tuner.TunerService.class,//定制状态栏服务

        com.android.systemui.keyguard.KeyguardViewMediator.class,//锁屏相关

        com.android.systemui.recents.Recents.class,//近期任务

        com.android.systemui.volume.VolumeUI.class,//音量条

        com.android.systemui.statusbar.SystemBars.class,//状态栏

        com.android.systemui.usb.StorageNotification.class,//通知栏

        com.android.systemui.power.PowerUI.class,//电源相关

        com.android.systemui.media.RingtonePlayer.class,//铃声播放相关

};

/**

 * Hold a reference on the stuff we start.

 */

private final SystemUI[] mServices = new SystemUI[SERVICES.length];

从mServices和SERVICES的定义可以发现SERVICES是一组包含全路径的相关的类,这些类包含一些我们常见的TunerService(定制状态栏服务)、
KeyguardViewMediator(锁屏相关)、Recents(近期任务)、VolumeUI(音量条)、SystemBars(状态栏)、StorageNotification(通知栏)、PowerUI(电源相关)、RingtonePlayer(铃声播放相关)类,它们都是继承与SystemUI抽象类,现在只分析StatusBar相关的SystemBars类

SystemBars

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

<code>public class SystemBars extends SystemUI implements ServiceMonitor.Callbacks {

    private static final String TAG = "SystemBars";

    private static final boolean DEBUG = false;

    private static final int WAIT_FOR_BARS_TO_DIE = 500;

    // manages the implementation coming from the remote process

    private ServiceMonitor mServiceMonitor;

    // in-process fallback implementation, per the product config

    private BaseStatusBar mStatusBar;

    @Override

    public void start() {

        if (DEBUG) Log.d(TAG, "start");

        //实例化ServiceMonitor

        mServiceMonitor = new ServiceMonitor(TAG, DEBUG,

                mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this);

        //start

        mServiceMonitor.start();  // will call onNoService if no remote service is found

    }

    /*服务没启动时,ServiceMonitor会回调onNoService*/

    @Override

    public void onNoService() {

        if (DEBUG) Log.d(TAG, "onNoService");

        createStatusBarFromConfig();  // fallback to using an in-process implementation

    }

    /*服务已经启动的回调*/

    @Override

    public long onServiceStartAttempt() {

        if (DEBUG) Log.d(TAG, "onServiceStartAttempt mStatusBar="+mStatusBar);

        if (mStatusBar != null) {

            // tear down the in-process version, we'll recreate it again if needed

            mStatusBar.destroy();

            mStatusBar = null;

            return WAIT_FOR_BARS_TO_DIE;

        }

        return 0;

    }

    /*系统配置改变*/

    @Override

    protected void onConfigurationChanged(Configuration newConfig) {

        if (mStatusBar != null) {

            mStatusBar.onConfigurationChanged(newConfig);

        }

    }

    /*打印堆栈*/

    @Override

    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {

        if (mStatusBar != null) {

            mStatusBar.dump(fd, pw, args);

        }

    }

    /*从xml文件中获取PhoneStatusBar全路径,通过类加载器实例化类,调用其start*/

    private void createStatusBarFromConfig() {

        if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");

        final String clsName = mContext.getString(R.string.config_statusBarComponent);

        if (clsName == null || clsName.length() == 0) {

            throw andLog("No status bar component configured", null);

        }

        Class<!--?--> cls = null;

        try {

            cls = mContext.getClassLoader().loadClass(clsName);

        } catch (Throwable t) {

            throw andLog("Error loading status bar component: " + clsName, t);

        }

        try {

            mStatusBar = (BaseStatusBar) cls.newInstance();

        } catch (Throwable t) {

            throw andLog("Error creating status bar component: " + clsName, t);

        }

        mStatusBar.mContext = mContext;

        mStatusBar.mComponents = mComponents;

        mStatusBar.start();

        if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());

    }

    private RuntimeException andLog(String msg, Throwable t) {

        Log.w(TAG, msg, t);

        throw new RuntimeException(msg, t);

    }

}

</code>

我们先从start方法开始分析

?

1

2

3

4

5

6

7

8

<code>    @Override

    public void start() {

        if (DEBUG) Log.d(TAG, "start");

        mServiceMonitor = new ServiceMonitor(TAG, DEBUG,

                mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this);

        mServiceMonitor.start();  // will call onNoService if no remote service is found

    }

</code>

这里实例化ServiceMonitor类start,继续分析ServiceMonitor

ServiceMonitor

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

<code class="hljs r">    ...

    public ServiceMonitor(String ownerTag, boolean debug,

            Context context, String settingKey, Callbacks callbacks) {

        mTag = ownerTag + ".ServiceMonitor";

        mDebug = debug;

        mContext = context;

        mSettingKey = settingKey; // Settings.Secure.BAR_SERVICE_COMPONENT

        mCallbacks = callbacks;

    }

    public void start() {

        // listen for setting changes

        /*Settings.Secure.BAR_SERVICE_COMPONENT改变时回调*/

        ContentResolver cr = mContext.getContentResolver();

        cr.registerContentObserver(Settings.Secure.getUriFor(mSettingKey),

                false /*notifyForDescendents*/, mSettingObserver, UserHandle.USER_ALL);

        // listen for package/component changes

        //应用安装,改变,卸载会触发mBroadcastReceiver广播

        IntentFilter filter = new IntentFilter();

        filter.addAction(Intent.ACTION_PACKAGE_ADDED);

        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);

        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);

        filter.addDataScheme("package");

        mContext.registerReceiver(mBroadcastReceiver, filter);

        mHandler.sendEmptyMessage(MSG_START_SERVICE);

    }

    ...</code>

ServiceMOnitor是一个监听Settings.Secure.BAR_SERVICE_COMPONENT是否改变的类,在start中通过监听系统系统时应用的变化来启动服务

?

1

2

3

4

5

6

7

8

9

<code>private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {

    public void onReceive(Context context, Intent intent) {

        String pkg = intent.getData().getSchemeSpecificPart();

        if (mServiceName != null && mServiceName.getPackageName().equals(pkg)) {

            mHandler.sendMessage(mHandler.obtainMessage(MSG_PACKAGE_INTENT, intent));

        }

    }

};

</code>

应用装载时,通过Handler发送MSG_PACKAGE_INTENT消息事件,我们查看Handler消息回调

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

<code class="hljs java">    // internal handler + messages used to serialize access to internal state

    public static final int MSG_START_SERVICE = 1; //启动服务,并非真正启动,会根据ServiceName进行判断

    public static final int MSG_CONTINUE_START_SERVICE = 2; //启动服务

    public static final int MSG_STOP_SERVICE = 3;//停止服务消息

    public static final int MSG_PACKAGE_INTENT = 4;//包安装事件消息

    public static final int MSG_CHECK_BOUND = 5;//包改变或者卸载时,重新启动服务消息

    public static final int MSG_SERVICE_DISCONNECTED = 6;//服务断开消息

    private final Handler mHandler = new Handler() {

        public void handleMessage(Message msg) {

            switch(msg.what) {

                case MSG_START_SERVICE:

                    startService();

                    break;

                case MSG_CONTINUE_START_SERVICE:

                    continueStartService();

                    break;

                case MSG_STOP_SERVICE:

                    stopService();

                    break;

                case MSG_PACKAGE_INTENT:

                    packageIntent((Intent)msg.obj);

                    break;

                case MSG_CHECK_BOUND:

                    checkBound();

                    break;

                case MSG_SERVICE_DISCONNECTED:

                    serviceDisconnected((ComponentName)msg.obj);

                    break;

            }

        }

    };

    private void packageIntent(Intent intent) {

        if (mDebug) Log.d(mTag, "packageIntent intent=" + intent

                + " extras=" + bundleToString(intent.getExtras()));

        if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {

            mHandler.sendEmptyMessage(MSG_START_SERVICE);//发送启动服务消息

        } else if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())

                || Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {

            final PackageManager pm = mContext.getPackageManager();

            final boolean serviceEnabled = isPackageAvailable()

                    && pm.getApplicationEnabledSetting(mServiceName.getPackageName())

                            != PackageManager.COMPONENT_ENABLED_STATE_DISABLED

                    && pm.getComponentEnabledSetting(mServiceName)

                            != PackageManager.COMPONENT_ENABLED_STATE_DISABLED;

            if (mBound && !serviceEnabled) {

                stopService();

                scheduleCheckBound();

            } else if (!mBound && serviceEnabled) {

                startService();

            }

        }

    }</code>

当我们SystemUI应用检测到有新应用装载时,会发送MSG_START_SERVICE消息来启动服务,我们接着分析Handler的回调MSG_START_SERVICE消息

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<code class="hljs cs">    private void startService() {

        mServiceName = getComponentNameFromSetting();

        if (mDebug) Log.d(mTag, "startService mServiceName=" + mServiceName);

        if (mServiceName == null) {

            mBound = false;

            mCallbacks.onNoService();

        } else {

            long delay = mCallbacks.onServiceStartAttempt();

            mHandler.sendEmptyMessageDelayed(MSG_CONTINUE_START_SERVICE, delay);

        }

    }

    /*从ContentProvider数据库中取得包名*/

    private ComponentName getComponentNameFromSetting() {

        String cn = Settings.Secure.getStringForUser(mContext.getContentResolver(),

                mSettingKey, UserHandle.USER_CURRENT);

        return cn == null ? null : ComponentName.unflattenFromString(cn);

    }</code>

首先从ContentProvider数据库中取得包名,如果没有启动,则回调CallBaback的onNoService服务,否则发送MSG_CONTINUE_START_SERVICE消息启动服务

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

<code class="hljs cs">    private void continueStartService() {

        if (mDebug) Log.d(mTag, "continueStartService");

        Intent intent = new Intent().setComponent(mServiceName);

        try {

            mServiceConnection = new SC();

            mBound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);

            if (mDebug) Log.d(mTag, "mBound: " + mBound);

        } catch (Throwable t) {

            Log.w(mTag, "Error binding to service: " + mServiceName, t);

        }

        if (!mBound) {

            mCallbacks.onNoService();

        }

    }</code>

至此可以知道,当远程服务没有启动时,会回调SystemBar的onNoService函数,我们回到SystemBar,分析onNoService函数

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

<code class="hljs java">     ...

     @Override

        public void onNoService() {

            if (DEBUG) Log.d(TAG, "onNoService");

            createStatusBarFromConfig();  // fallback to using an in-process implementation

        }

     ...

     private void createStatusBarFromConfig() {

        if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");

        final String clsName = mContext.getString(R.string.config_statusBarComponent);//从xml文件读取类名

        if (clsName == null || clsName.length() == 0) {

            throw andLog("No status bar component configured", null);

        }

        //通过类加载器实例化类

        Class<!--?--> cls = null;

        try {

            cls = mContext.getClassLoader().loadClass(clsName);

        } catch (Throwable t) {

            throw andLog("Error loading status bar component: " + clsName, t);

        }

        try {

            mStatusBar = (BaseStatusBar) cls.newInstance();

        } catch (Throwable t) {

            throw andLog("Error creating status bar component: " + clsName, t);

        }

        mStatusBar.mContext = mContext;

        mStatusBar.mComponents = mComponents;

        mStatusBar.start();//调用类的start方法

        if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());

    }</code>

上面分析可以得知,当远程服务没有启动时,首先从xml文件读取要启动的类名,我们来查看这个xml文件
res\values\config.xml

?

1

2

3

<code class="hljs xml">    <!-- Component to be used as the status bar service.  Must implement the IStatusBar

     interface.  This name is in the ComponentName flattened format (package/class)  -->

    <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string></code>

从上面可以知道,最终程序会加载PhoneStatusBar这个类,接下来分析PhoneStatusBar

PhoneStatusBar

首先我们从上面分析得知,当实例化PhoneStatusBar类后会调用start方法,我们就从PhoneStatusBar的start方法开始分析

start

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

<code class="hljs avrasm">    ...

    @Override

    public void start() {

        //获取WindowManager,初始化当前显示界面大小

        mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))

                .getDefaultDisplay();

        updateDisplaySize();

        //src绘图模式

        mScrimSrcModeEnabled = mContext.getResources().getBoolean(

                R.bool.config_status_bar_scrim_behind_use_src);

        //调用父类start方法

        super.start(); // calls createAndAddWindows()

        //MediaSession相关

        mMediaSessionManager

                = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);

        // TODO: use MediaSessionManager.SessionListener to hook us up to future updates

        // in session state

        //添加导航栏

        addNavigationBar();

        // Lastly, call to the icon policy to install/update all the icons.

        //更新状态栏图标

        mIconPolicy = new PhoneStatusBarPolicy(mContext, mCastController, mHotspotController,

                mUserInfoController, mBluetoothController);

        mIconPolicy.setCurrentUserSetup(mUserSetup);

        mSettingsObserver.onChange(false); // set up

        mHeadsUpObserver.onChange(true); // set up

        if (ENABLE_HEADS_UP) {

            mContext.getContentResolver().registerContentObserver(

                    Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true,

                    mHeadsUpObserver);

            mContext.getContentResolver().registerContentObserver(

                    Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,

                    mHeadsUpObserver);

        }

        mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);

        mUnlockMethodCache.addListener(this);

        //锁屏

        startKeyguard();

        mDozeServiceHost = new DozeServiceHost();

        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mDozeServiceHost);

        putComponent(DozeHost.class, mDozeServiceHost);

        putComponent(PhoneStatusBar.class, this);

        /// M:add for multi window @{

        if(MultiWindowProxy.isSupported()) {

            registerMWProxyAgain();

        }

        /// @}

        setControllerUsers();

        notifyUserAboutHiddenNotifications();

        mScreenPinningRequest = new ScreenPinningRequest(mContext);

    }

    ...</code>

我们接着分析PhoneStatusBar父类的BaseStatusBar的start方法

BaseStatusBar

start

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

<code class="hljs cs">    ...

    public void start() {

        //获取Dispaly

        mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);

        mWindowManagerService = WindowManagerGlobal.getWindowManagerService();

        mDisplay = mWindowManager.getDefaultDisplay();

        mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(

                Context.DEVICE_POLICY_SERVICE);

        mNotificationColorUtil = NotificationColorUtil.getInstance(mContext);

        mNotificationData = new NotificationData(this);

        mAccessibilityManager = (AccessibilityManager)

                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);

        mDreamManager = IDreamManager.Stub.asInterface(

                ServiceManager.checkService(DreamService.DREAM_SERVICE));

        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);

        //监听设置文件的改变,以便更新ContenProvider数据库

        mContext.getContentResolver().registerContentObserver(

                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,

                mSettingsObserver);

        mContext.getContentResolver().registerContentObserver(

                Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,

                mSettingsObserver);

        mContext.getContentResolver().registerContentObserver(

                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,

                mSettingsObserver,

                UserHandle.USER_ALL);

        mContext.getContentResolver().registerContentObserver(

                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),

                true,

                mLockscreenSettingsObserver,

                UserHandle.USER_ALL);

        //加载startbarService服务

        mBarService = IStatusBarService.Stub.asInterface(

                ServiceManager.getService(Context.STATUS_BAR_SERVICE));

        //设置近期任务回调

        mRecents = getComponent(Recents.class);

        mRecents.setCallback(this);

        //获取本地配置

        final Configuration currentConfig = mContext.getResources().getConfiguration();

        mLocale = currentConfig.locale;

        mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);

        mFontScale = currentConfig.fontScale;

        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);

        //加载动画

        mLinearOutSlowIn = AnimationUtils.loadInterpolator(mContext,

                android.R.interpolator.linear_out_slow_in);

        mFastOutLinearIn = AnimationUtils.loadInterpolator(mContext,

                android.R.interpolator.fast_out_linear_in);

        // Connect in to the status bar manager service

        StatusBarIconList iconList = new StatusBarIconList();

        mCommandQueue = new CommandQueue(this, iconList);

        int[] switches = new int[8];

        ArrayList<ibinder> binders = new ArrayList<ibinder>();

        try {

            mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders);

        } catch (RemoteException ex) {

            // If the system process isn't there we're doomed anyway.

        }

        //调用createAndAddWindows方法

        createAndAddWindows();

        mSettingsObserver.onChange(false); // set up

        disable(switches[0], switches[6], false /* animate */);

        setSystemUiVisibility(switches[1], 0xffffffff);

        topAppWindowChanged(switches[2] != 0);

        // StatusBarManagerService has a back up of IME token and it's restored here.

        setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);

        // Set up the initial icon state

        int N = iconList.size();

        int viewIndex = 0;

        for (int i=0; i<n; i++)="" {="" statusbaricon="" icon="iconList.getIcon(i);" if="" (icon="" !="null)" addicon(iconlist.getslot(i),="" i,="" viewindex,="" icon);="" viewindex++;="" }="" set="" up="" the="" initial="" notification="" state.="" try="" mnotificationlistener.registerassystemservice(mcontext,="" new="" componentname(mcontext.getpackagename(),="" getclass().getcanonicalname()),="" userhandle.user_all);="" catch="" (remoteexception="" e)="" log.e(tag,="" "unable="" to="" register="" listener",="" e);="" (debug)="" log.d(tag,="" string.format(="" "init:="" icons="%d" disabled="0x%08x" lights="0x%08x" menu="0x%08x" imebutton="0x%08x&quot;," iconlist.size(),="" switches[0],="" switches[1],="" switches[2],="" switches[3]="" ));="" mcurrentuserid="ActivityManager.getCurrentUser();" setheadsupuser(mcurrentuserid);="" intentfilter="" filter="new" intentfilter();="" filter.addaction(intent.action_user_switched);="" filter.addaction(intent.action_user_added);="" filter.addaction(intent.action_user_present);="" filter.addaction(banner_action_cancel);="" filter.addaction(banner_action_setup);="" mcontext.registerreceiver(mbroadcastreceiver,="" filter);="" allusersfilter="new" allusersfilter.addaction(="" devicepolicymanager.action_device_policy_manager_state_changed);="" mcontext.registerreceiverasuser(mallusersreceiver,="" userhandle.all,="" allusersfilter,="" null,="" null);="" updatecurrentprofilescache();="" ...</ibinder></ibinder></code>

BaseStatusBar关于StatusBar相关的最主要是调用了createAndAddWindows方法,我们看下这个方法的定义

?

1

2

3

4

5

<code class="hljs cs"><code class="hljs java">    /**

     * Create all windows necessary for the status bar (including navigation, overlay panels, etc)

     * and add them to the window manager.

     */

    protected abstract void createAndAddWindows();</code></code>

这是一个抽象方法,也就是说,它会回调到子类的createAndAddWindows的实现方法中,我们重新回到PhoneStatusBar中,找到createAndAddWindows的方法实现

createAndAddWindows

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

<code class="hljs cs"><code class="hljs avrasm">    ...

    @Override

    public void createAndAddWindows() {

        addStatusBarWindow();

    }

    private void addStatusBarWindow() {

        makeStatusBarView();//创建statusbar视图

        mStatusBarWindowManager = new StatusBarWindowManager(mContext);

        //通过StatusBarWindowManager类的add方法加载到Window窗体中

        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());

    }

    ...

    protected PhoneStatusBarView makeStatusBarView() {

        final Context context = mContext;

        //通过Resources更新显示大小和一些资源文件

        Resources res = context.getResources();

        updateDisplaySize(); // populates mDisplayMetrics

        updateResources();

        //加载StartBarWindowView视图

        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,

                R.layout.super_status_bar, null);

        mStatusBarWindow.setService(this);

        //监听下拉事件

        mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {

            @Override

            public boolean onTouch(View v, MotionEvent event) {

                checkUserAutohide(v, event);

                if (event.getAction() == MotionEvent.ACTION_DOWN) {

                    if (mExpandedVisible) {

                        animateCollapsePanels();

                    }

                }

                return mStatusBarWindow.onTouchEvent(event);

            }

        });

        //状态栏

        mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);

        mStatusBarView.setBar(this);

        //

        PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);

        mStatusBarView.setPanelHolder(holder);

        //通知栏

        mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(

                R.id.notification_panel);

        mNotificationPanel.setStatusBar(this);

        //  M: setBackground in 512 low ram device

        if (!ActivityManager.isHighEndGfx() && !FeatureOptions.LOW_RAM_SUPPORT) {

            mStatusBarWindow.setBackground(null);

            mNotificationPanel.setBackground(new FastColorDrawable(context.getColor(

                    R.color.notification_panel_solid_background)));

        }

        //状态栏通知

        mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow);

        mHeadsUpManager.setBar(this);

        mHeadsUpManager.addListener(this);

        mHeadsUpManager.addListener(mNotificationPanel);

        mNotificationPanel.setHeadsUpManager(mHeadsUpManager);

        mNotificationData.setHeadsUpManager(mHeadsUpManager);

        if (MULTIUSER_DEBUG) {

            mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(

                    R.id.header_debug_info);

            mNotificationPanelDebugText.setVisibility(View.VISIBLE);

        }

        try {

            //是否显示导航栏

            boolean showNav = mWindowManagerService.hasNavigationBar();

             Log.v(TAG, "hasNavigationBar=" + showNav);

            if (showNav) {

                /// M: add for multi window @{

                //加载导航栏布局

                int layoutId = R.layout.navigation_bar;

                if(MultiWindowProxy.isSupported()) {

                    layoutId = R.layout.navigation_bar_float_window;

                }

                mNavigationBarView = (NavigationBarView) View.inflate(context,

                        /*R.layout.navigation_bar*/layoutId, null);

                /// @}

                mNavigationBarView.setDisabledFlags(mDisabled1);

                mNavigationBarView.setBar(this);

                mNavigationBarView.setOnVerticalChangedListener(

                        new NavigationBarView.OnVerticalChangedListener() {

                    @Override

                    public void onVerticalChanged(boolean isVertical) {

                        if (mAssistManager != null) {

                            mAssistManager.onConfigurationChanged();

                        }

                        mNotificationPanel.setQsScrimEnabled(!isVertical);

                    }

                });

                //设置导航栏触摸事件

                mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {

                    @Override

                    public boolean onTouch(View v, MotionEvent event) {

                        checkUserAutohide(v, event);

                        return false;

                    }});

            }

        } catch (RemoteException ex) {

            // no window manager? good luck with that

        }

        mAssistManager = new AssistManager(this, context);

        // figure out which pixel-format to use for the status bar.

        mPixelFormat = PixelFormat.OPAQUE;

        //下拉通知栏

        mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(

                R.id.notification_stack_scroller);

        mStackScroller.setLongPressListener(getNotificationLongClicker());

        mStackScroller.setPhoneStatusBar(this);

        mStackScroller.setGroupManager(mGroupManager);

        mStackScroller.setHeadsUpManager(mHeadsUpManager);

        mGroupManager.setOnGroupChangeListener(mStackScroller);

        mKeyguardIconOverflowContainer =

                (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(

                        R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);

        mKeyguardIconOverflowContainer.setOnActivatedListener(this);

        mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);

        mStackScroller.setOverflowContainer(mKeyguardIconOverflowContainer);

        SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate(

                        R.layout.status_bar_notification_speed_bump, mStackScroller, false);

        mStackScroller.setSpeedBumpView(speedBump);

        mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(

                R.layout.status_bar_no_notifications, mStackScroller, false);

        mStackScroller.setEmptyShadeView(mEmptyShadeView);

        //下拉清除键

        mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(

                R.layout.status_bar_notification_dismiss_all, mStackScroller, false);

        mDismissView.setOnButtonClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                MetricsLogger.action(mContext, MetricsLogger.ACTION_DISMISS_ALL_NOTES);

                clearAllNotifications();

            }

        });

        mStackScroller.setDismissView(mDismissView);

        mExpandedContents = mStackScroller;

        mBackdrop = (BackDropView) mStatusBarWindow.findViewById(R.id.backdrop);

        mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front);

        mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back);

        ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind);

        ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front);

        View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim);

        mScrimController = new ScrimController(scrimBehind, scrimInFront, headsUpScrim,

                mScrimSrcModeEnabled);

        mHeadsUpManager.addListener(mScrimController);

        mStackScroller.setScrimController(mScrimController);

        mScrimController.setBackDropView(mBackdrop);

        mStatusBarView.setScrimController(mScrimController);

        mDozeScrimController = new DozeScrimController(mScrimController, context);

        mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);

        mHeader.setActivityStarter(this);

        //锁屏相关

        mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);

        mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);

        mKeyguardBottomArea =

                (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);

        mKeyguardBottomArea.setActivityStarter(this);

        mKeyguardBottomArea.setAssistManager(mAssistManager);

        mKeyguardIndicationController = new KeyguardIndicationController(mContext,

                (KeyguardIndicationTextView) mStatusBarWindow.findViewById(

                        R.id.keyguard_indication_text),

                mKeyguardBottomArea.getLockIcon());

        mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController);

        // set the inital view visibility

        setAreThereNotifications();

        //主要是控制一些系统图标,第三方图标等的显示和更新

        mIconController = new StatusBarIconController(

                mContext, mStatusBarView, mKeyguardStatusBar, this);

        // Background thread for any controllers that need it.

        mHandlerThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);

        mHandlerThread.start();

        // Other icons

        //位置控制

        mLocationController = new LocationControllerImpl(mContext,

                mHandlerThread.getLooper()); // will post a notification

        //电池

        mBatteryController = new BatteryController(mContext);

        mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() {

            @Override

            public void onPowerSaveChanged() {

                mHandler.post(mCheckBarModes);

                if (mDozeServiceHost != null) {

                    mDozeServiceHost.firePowerSaveChanged(mBatteryController.isPowerSave());

                }

            }

            @Override

            public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {

                // noop

            }

        });

        //网络

        mNetworkController = new NetworkControllerImpl(mContext, mHandlerThread.getLooper());

        //热点

        mHotspotController = new HotspotControllerImpl(mContext);

        //蓝牙

        mBluetoothController = new BluetoothControllerImpl(mContext, mHandlerThread.getLooper());

        mSecurityController = new SecurityControllerImpl(mContext);

        /// M: add extra tiles @{

        // add HotKnot in quicksetting

        if (SIMHelper.isMtkHotKnotSupport()) {

            Log.d(TAG, "makeStatusBarView : HotKnotControllerImpl");

            mHotKnotController = new HotKnotControllerImpl(mContext);

        } else {

            mHotKnotController = null;

        }

        // add AudioProfile in quicksetting

        if (SIMHelper.isMtkAudioProfilesSupport()) {

            Log.d(TAG, "makeStatusBarView : AudioProfileControllerImpl");

            mAudioProfileController = new AudioProfileControllerImpl(mContext);

        } else {

            mAudioProfileController = null;

        }

        SIMHelper.setContext(mContext);

        // /@}

        if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) {

            mRotationLockController = new RotationLockControllerImpl(mContext);

        }

        mUserInfoController = new UserInfoController(mContext);

        mVolumeComponent = getComponent(VolumeComponent.class);

        if (mVolumeComponent != null) {

            mZenModeController = mVolumeComponent.getZenController();

        }

        Log.d(TAG, "makeStatusBarView : CastControllerImpl +");

        mCastController = new CastControllerImpl(mContext);

        Log.d(TAG, "makeStatusBarView : CastControllerImpl -");

        final SignalClusterView signalCluster =

                (SignalClusterView) mStatusBarView.findViewById(R.id.signal_cluster);

        final SignalClusterView signalClusterKeyguard =

                (SignalClusterView) mKeyguardStatusBar.findViewById(R.id.signal_cluster);

        final SignalClusterView signalClusterQs =

                (SignalClusterView) mHeader.findViewById(R.id.signal_cluster);

        mNetworkController.addSignalCallback(signalCluster);

        mNetworkController.addSignalCallback(signalClusterKeyguard);

        mNetworkController.addSignalCallback(signalClusterQs);

        signalCluster.setSecurityController(mSecurityController);

        signalCluster.setNetworkController(mNetworkController);

        signalClusterKeyguard.setSecurityController(mSecurityController);

        signalClusterKeyguard.setNetworkController(mNetworkController);

        signalClusterQs.setSecurityController(mSecurityController);

        signalClusterQs.setNetworkController(mNetworkController);

        final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();

        if (isAPhone) {

            mNetworkController.addEmergencyListener(mHeader);

        }

        /// M: Support "Operator plugin - Customize Carrier Label for PLMN" @{

        mStatusBarPlmnPlugin = PluginFactory.getStatusBarPlmnPlugin(context);

        if (supportCustomizeCarrierLabel()) {

            mCustomizeCarrierLabel = mStatusBarPlmnPlugin.customizeCarrierLabel(

                    mNotificationPanel, null);

        }

        /// M: Support "Operator plugin - Customize Carrier Label for PLMN" @}

        //手电筒

        mFlashlightController = new FlashlightController(mContext);

        //键盘

        mKeyguardBottomArea.setFlashlightController(mFlashlightController);

        mKeyguardBottomArea.setPhoneStatusBar(this);

        mKeyguardBottomArea.setUserSetupComplete(mUserSetup);

        mAccessibilityController = new AccessibilityController(mContext);

        mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);

        mNextAlarmController = new NextAlarmController(mContext);

        mKeyguardMonitor = new KeyguardMonitor(mContext);

        if (UserSwitcherController.isUserSwitcherAvailable(UserManager.get(mContext))) {

            mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor,

                    mHandler);

        }

        mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,

                (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),

                mKeyguardStatusBar, mNotificationPanel, mUserSwitcherController);

        // Set up the quick settings tile panel

        mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);

        if (mQSPanel != null) {

            final QSTileHost qsh = new QSTileHost(mContext, this,

                    mBluetoothController, mLocationController, mRotationLockController,

                    mNetworkController, mZenModeController, mHotspotController,

                    mCastController, mFlashlightController,

                    mUserSwitcherController, mKeyguardMonitor,

                    mSecurityController,

                    /// M: add HotKnot in quicksetting

                    mHotKnotController,

                    /// M: add AudioProfile in quicksetting

                    mAudioProfileController

            );

            mQSPanel.setHost(qsh);

            mQSPanel.setTiles(qsh.getTiles());

            mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);

            mQSPanel.setBrightnessMirror(mBrightnessMirrorController);

            mHeader.setQSPanel(mQSPanel);

            qsh.setCallback(new QSTileHost.Callback() {

                @Override

                public void onTilesChanged() {

                    mQSPanel.setTiles(qsh.getTiles());

                }

            });

        }

        // User info. Trigger first load.

        mHeader.setUserInfoController(mUserInfoController);

        mKeyguardStatusBar.setUserInfoController(mUserInfoController);

        mKeyguardStatusBar.setUserSwitcherController(mUserSwitcherController);

        mUserInfoController.reloadUserInfo();

        mHeader.setBatteryController(mBatteryController);

        ((BatteryMeterView) mStatusBarView.findViewById(R.id.battery)).setBatteryController(

                mBatteryController);

        mKeyguardStatusBar.setBatteryController(mBatteryController);

        mHeader.setNextAlarmController(mNextAlarmController);

        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);

        mBroadcastReceiver.onReceive(mContext,

                new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));

        // receive broadcasts

        //注册系统广播

        IntentFilter filter = new IntentFilter();

        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);

        filter.addAction(Intent.ACTION_SCREEN_OFF);

        filter.addAction(Intent.ACTION_SCREEN_ON);

        context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);

        IntentFilter demoFilter = new IntentFilter();

        if (DEBUG_MEDIA_FAKE_ARTWORK) {

            demoFilter.addAction(ACTION_FAKE_ARTWORK);

        }

        demoFilter.addAction(ACTION_DEMO);

        context.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter,

                android.Manifest.permission.DUMP, null);

        // listen for USER_SETUP_COMPLETE setting (per-user)

        resetUserSetupObserver();

        // disable profiling bars, since they overlap and clutter the output on app windows

        ThreadedRenderer.overrideProperty("disableProfileBars", "true");

        // Private API call to make the shadows look better for Recents

        ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));

        mStatusBarPlmnPlugin.addPlmn((LinearLayout)mStatusBarView.

                                     findViewById(R.id.status_bar_contents), mContext);

        return mStatusBarView;

    }

    ...

</code></code>

因为这块涉及的太广,所以接下来只分析StatusBar相关的一块,以导航栏为例进行讲解,我们重新回到PhoneStatusBar的start方法中,找到导航栏这块,发现它是调用addNavigationBar函数,所以我们查看这个函数:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

<code class="hljs cs"><code class="hljs avrasm">    ...

    // For small-screen devices (read: phones) that lack hardware navigation buttons

    private void addNavigationBar() {

        if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);

        //1.判断mNavigationBarView是否为空,这个视图有上面分析的makeStatusBarView方法中进行创建

        if (mNavigationBarView == null) return;

        //2.加载导航栏的具体显示(导航栏的显示由横向显示和竖向显示,后面分析)

        prepareNavigationBarView();

        //3.根据LayoutParams,加载导航栏到窗体中

        mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());

    }

    ...

        private void prepareNavigationBarView() {

        mNavigationBarView.reorient();

        //设置导航栏三个图标的点击事件

        mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);

        mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);

        mNavigationBarView.getRecentsButton().setLongClickable(true);

        mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);

        mNavigationBarView.getBackButton().setLongClickable(true);

        mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);

        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);

        mNavigationBarView.getHomeButton().setOnLongClickListener(mLongPressHomeListener);

        mAssistManager.onConfigurationChanged();

        /// M: add for multi window @{

        if(MultiWindowProxy.isSupported()){

            mNavigationBarView.getFloatButton().setOnClickListener(mFloatClickListener);

            if(mIsSplitModeEnable){

                mNavigationBarView.getFloatModeButton().setOnClickListener(mFloatModeClickListener);

                mNavigationBarView.getSplitModeButton().setOnClickListener(mSplitModeClickListener);

            }

            MultiWindowProxy.getInstance().setSystemUiCallback(new MWSystemUiCallback());

        }

        /// @}

    }

我们根据上面的注释来进行分析,主要内容有
1. 导航栏布局的创建
2. 导航栏布局分析及加载
3. 导航栏LayoutParams分析

导航栏布局的创建

在PhoneStatusBar的makeStatusBarView方法中,我们可以看到导航栏是怎么创建的

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

<code class="hljs cs"><code class="hljs r">    ...

    protected PhoneStatusBarView makeStatusBarView() {

        ...

        ...

        try {

            //是否显示导航栏

            boolean showNav = mWindowManagerService.hasNavigationBar();

             Log.v(TAG, "hasNavigationBar=" + showNav);

            if (showNav) {

                /// M: add for multi window @{

                //加载导航栏布局

                int layoutId = R.layout.navigation_bar;

                //是否支持多窗口

                if(MultiWindowProxy.isSupported()) {

                    layoutId = R.layout.navigation_bar_float_window;

                }

                mNavigationBarView = (NavigationBarView) View.inflate(context,

                        /*R.layout.navigation_bar*/layoutId, null);

                /// @}

                mNavigationBarView.setDisabledFlags(mDisabled1);

                mNavigationBarView.setBar(this);

                mNavigationBarView.setOnVerticalChangedListener(

                        new NavigationBarView.OnVerticalChangedListener() {

                    @Override

                    public void onVerticalChanged(boolean isVertical) {

                        if (mAssistManager != null) {

                            mAssistManager.onConfigurationChanged();

                        }

                        mNotificationPanel.setQsScrimEnabled(!isVertical);

                    }

                });

                //设置导航栏触摸事件

                mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {

                    @Override

                    public boolean onTouch(View v, MotionEvent event) {

                        checkUserAutohide(v, event);

                        return false;

                    }});

            }

        } catch (RemoteException ex) {

            // no window manager? good luck with that

        }

        ...

        ...

    }

    ...</code></code>

首先由mWindowManagerService的hasNavigationBar来决定是否显示导航栏,同时通过加载navigation_bar(多窗口加载navigation_bar_float_window)布局来显示导航栏,我们来查看hasNavigationBar方法,因为mWidnwoManagerService是IWindowManagerService由PhoneWindowManager进行调用:
frameworks\base\service\core\java\com\android\server\PhoneWindowManager.java

PhoneWindowManager

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

<code class="hljs cs"><code class="hljs r">    ...

    // Use this instead of checking config_showNavigationBar so that it can be consistently

    // overridden by qemu.hw.mainkeys in the emulator.

    @Override

    public boolean hasNavigationBar() {

        return mHasNavigationBar;

    }

    ...

    mHasNavigationBar = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);

    // Allow a system property to override this. Used by the emulator.

    // See also hasNavigationBar().

    String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");

    if ("1".equals(navBarOverride)) {

         mHasNavigationBar = false;

     } else if ("0".equals(navBarOverride)) {

         mHasNavigationBar = true;

    }

    ...</code></code>

从framework\base\core\res\res\valuse\config.xml中获取mHashNavigationBar的值

?

1

2

3

<code class="hljs cs"><code class="hljs xml">    <!-- Whether a software navigation bar should be shown. NOTE: in the future this may be

         autodetected from the Configuration. -->

    <bool name="config_showNavigationBar">ture</bool></code></code>

然后从系统配置位置中取qemu.hw.mainkeys的值,所以这里给我们提供了一个隐藏状态栏的新思路,除了在createAndAddWindows中注释掉addNavigationBar函数外,我们也可以通过修改framework下的config.xml的config_showNavigationBar的值和修改系统配置文件的值来达到隐藏状态栏的目的

导航栏布局分析及加载

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

  ...

        private void prepareNavigationBarView() {

        mNavigationBarView.reorient();

        //设置导航栏三个图标的点击事件

        mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);

        mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);

        mNavigationBarView.getRecentsButton().setLongClickable(true);

        mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);

        mNavigationBarView.getBackButton().setLongClickable(true);

        mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);

        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);

        mNavigationBarView.getHomeButton().setOnLongClickListener(mLongPressHomeListener);

        mAssistManager.onConfigurationChanged();

        /// M: add for multi window @{

        if(MultiWindowProxy.isSupported()){

            mNavigationBarView.getFloatButton().setOnClickListener(mFloatClickListener);

            if(mIsSplitModeEnable){

                mNavigationBarView.getFloatModeButton().setOnClickListener(mFloatModeClickListener);

                mNavigationBarView.getSplitModeButton().setOnClickListener(mSplitModeClickListener);

            }

            MultiWindowProxy.getInstance().setSystemUiCallback(new MWSystemUiCallback());

        }

        /// @}

    }

    ...

导航栏布局的确切显示在prepareNavigationBarView中的mNavigationBarView.reorient();来决定,我们查看reorient方法

reorient

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

<code class="hljs cs"><code class="hljs r">    ...

    public void reorient() {

        //获取屏幕方向

        final int rot = mDisplay.getRotation();

        //隐藏导航栏布局

        for (int i=0; i<4; i++) {

            mRotatedViews[i].setVisibility(View.GONE);

        }

        //根据屏幕方向显示导航栏布局

        mCurrentView = mRotatedViews[rot];

        mCurrentView.setVisibility(View.VISIBLE);

        setLayoutTransitionsEnabled(mLayoutTransitionsEnabled);

        getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);

        mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone);

        // force the low profile & disabled states into compliance

        mBarTransitions.init();

        setDisabledFlags(mDisabledFlags, true /* force */);

        setMenuVisibility(mShowMenu, true /* force */);

        if (DEBUG) {

            Log.d(TAG, "reorient(): rot=" + mDisplay.getRotation());

        }

        updateTaskSwitchHelper();

        setNavigationIconHints(mNavigationIconHints, true);

    }

    ...</code></code>

导航栏的显示由屏幕的方向来决定,而导航栏有两种不同的显示方式,横向显示和竖向显示,我们可以从mRotatedViews进行追查到

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

    ...

    View[] mRotatedViews = new View[4];

    ...

    @Override

    public void onFinishInflate() {

        //屏幕方位0和180方向显示的导航栏为rot0

        mRotatedViews[Surface.ROTATION_0] =

        mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);

        //屏幕访问90和270显示的导航栏为rot90

        mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);

        mRotatedViews[Surface.ROTATION_270] = mRotatedViews[Surface.ROTATION_90];

        //mCurrentView = mRotatedViews[Surface.ROTATION_0];

        mCurrentView = mRotatedViews[Surface.ROTATION_90];

        getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);

        updateRTLOrder();

    }

    ...

布局加载完成后,会回调onFinishInflate方法,在这方法中对屏幕的几个方向初始化4个导航栏view,其中0和180为横向布局,90和270为纵向布局,我们可以从导航栏(NavigationBarView)布局文件中可以看出
res\layout\navigation_bar.xml和res\layout\navigation_bar

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

<code class="hljs cs"><code class="hljs xml"><com.android.systemui.statusbar.phone.navigationbarview android:background="@drawable/system_bar_background" android:layout_height="match_parent" android:layout_width="match_parent" xmlns:android="https://schemas.android.com/apk/res/android" xmlns:systemui="https://schemas.android.com/apk/res-auto">

    <!--横向导航栏-->

    <framelayout android:id="@+id/rot0" android:layout_height="match_parent" android:layout_width="match_parent">

        <linearlayout android:animatelayoutchanges="true" android:clipchildren="false" android:cliptopadding="false" android:id="@+id/nav_buttons" android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="horizontal">

            <!-- navigation controls -->

            <view android:layout_height="match_parent" android:layout_weight="0" android:layout_width="@dimen/navigation_side_padding" android:visibility="invisible">

            <!--back按钮-->

            <com.android.systemui.statusbar.policy.keybuttonview android:contentdescription="@string/accessibility_back" android:id="@+id/back" android:layout_height="match_parent" android:layout_weight="0" android:layout_width="@dimen/navigation_key_width" android:scaletype="center" android:src="@drawable/ic_sysbar_back" systemui:keycode="4">

            <view android:layout_height="match_parent" android:layout_weight="1" android:layout_width="0dp" android:visibility="invisible">

            <!--home按钮-->

            <com.android.systemui.statusbar.policy.keybuttonview android:contentdescription="@string/accessibility_home" android:id="@+id/home" android:layout_height="match_parent" android:layout_weight="0" android:layout_width="@dimen/navigation_key_width" android:scaletype="center" android:src="@drawable/ic_sysbar_home" systemui:keycode="3" systemui:keyrepeat="false">

            <view android:layout_height="match_parent" android:layout_weight="1" android:layout_width="0dp" android:visibility="invisible">

            <!--recent按钮-->

            <com.android.systemui.statusbar.policy.keybuttonview android:contentdescription="@string/accessibility_recent" android:id="@+id/recent_apps" android:layout_height="match_parent" android:layout_weight="0" android:layout_width="@dimen/navigation_key_width" android:scaletype="center" android:src="@drawable/ic_sysbar_recent">

            <framelayout android:layout_height="match_parent" android:layout_weight="0" android:layout_width="@dimen/navigation_side_padding">

                <com.android.systemui.statusbar.policy.keybuttonview android:contentdescription="@string/accessibility_menu" android:id="@+id/menu" android:layout_gravity="end" android:layout_height="match_parent" android:layout_width="@dimen/navigation_extra_key_width" android:scaletype="centerInside" android:src="@drawable/ic_sysbar_menu" android:visibility="invisible" systemui:keycode="82">

                <com.android.systemui.statusbar.policy.keybuttonview android:contentdescription="@string/accessibility_ime_switch_button" android:id="@+id/ime_switcher" android:layout_gravity="end" android:layout_height="match_parent" android:layout_width="@dimen/navigation_extra_key_width" android:scaletype="centerInside" android:src="@drawable/ic_ime_switcher_default" android:visibility="invisible">

            </com.android.systemui.statusbar.policy.keybuttonview></com.android.systemui.statusbar.policy.keybuttonview></framelayout>

        </com.android.systemui.statusbar.policy.keybuttonview></view></com.android.systemui.statusbar.policy.keybuttonview></view></com.android.systemui.statusbar.policy.keybuttonview></view></linearlayout>

        <!-- lights out layout to match exactly -->

        <linearlayout android:id="@+id/lights_out" android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="horizontal" android:visibility="gone">

            <imageview android:contentdescription="@string/accessibility_back" android:layout_height="match_parent" android:layout_marginstart="@dimen/navigation_side_padding" android:layout_weight="0" android:layout_width="@dimen/navigation_key_width" android:scaletype="center" android:src="@drawable/ic_sysbar_lights_out_dot_small">

            <view android:layout_height="match_parent" android:layout_weight="1" android:layout_width="match_parent" android:visibility="invisible">

            <imageview android:contentdescription="@string/accessibility_home" android:layout_height="match_parent" android:layout_weight="0" android:layout_width="@dimen/navigation_key_width" android:scaletype="center" android:src="@drawable/ic_sysbar_lights_out_dot_large">

            <view android:layout_height="match_parent" android:layout_weight="1" android:layout_width="match_parent" android:visibility="invisible">

            <imageview android:contentdescription="@string/accessibility_recent" android:layout_height="match_parent" android:layout_marginend="@dimen/navigation_side_padding" android:layout_weight="0" android:layout_width="@dimen/navigation_key_width" android:scaletype="center" android:src="@drawable/ic_sysbar_lights_out_dot_small">

        </imageview></view></imageview></view></imageview></linearlayout>

        <com.android.systemui.statusbar.policy.deadzone android:id="@+id/deadzone" android:layout_gravity="top" android:layout_height="match_parent" android:layout_width="match_parent" systemui:decaytime="@integer/navigation_bar_deadzone_decay" systemui:holdtime="@integer/navigation_bar_deadzone_hold" systemui:maxsize="@dimen/navigation_bar_deadzone_size_max" systemui:minsize="@dimen/navigation_bar_deadzone_size" systemui:orientation="horizontal">

    </com.android.systemui.statusbar.policy.deadzone></framelayout>

    <!--纵向显示-->

    <framelayout android:id="@+id/rot90" android:layout_height="match_parent" android:layout_width="match_parent" android:paddingtop="0dp" android:visibility="gone">

        <linearlayout android:animatelayoutchanges="true" android:clipchildren="false" android:cliptopadding="false" android:id="@+id/nav_buttons" android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical">

            <!-- navigation controls -->

            <framelayout android:layout_height="@dimen/navigation_side_padding" android:layout_weight="0" android:layout_width="match_parent">

                <com.android.systemui.statusbar.policy.keybuttonview android:contentdescription="@string/accessibility_ime_switch_button" android:id="@+id/ime_switcher" android:layout_gravity="top" android:layout_height="@dimen/navigation_extra_key_width" android:layout_width="match_parent" android:scaletype="centerInside" android:src="@drawable/ic_ime_switcher_default" android:visibility="invisible">

                <com.android.systemui.statusbar.policy.keybuttonview android:contentdescription="@string/accessibility_menu" android:id="@+id/menu" android:layout_gravity="top" android:layout_height="40dp" android:layout_width="match_parent" android:scaletype="centerInside" android:src="@drawable/ic_sysbar_menu_land" android:visibility="invisible" systemui:keycode="82">

            </com.android.systemui.statusbar.policy.keybuttonview></com.android.systemui.statusbar.policy.keybuttonview></framelayout>

            <com.android.systemui.statusbar.policy.keybuttonview android:contentdescription="@string/accessibility_recent" android:id="@+id/recent_apps" android:layout_height="@dimen/navigation_key_width" android:layout_weight="0" android:layout_width="match_parent" android:scaletype="center" android:src="@drawable/ic_sysbar_recent_land">

            <view android:layout_height="match_parent" android:layout_weight="1" android:layout_width="match_parent" android:visibility="invisible">

            <com.android.systemui.statusbar.policy.keybuttonview android:contentdescription="@string/accessibility_home" android:id="@+id/home" android:layout_height="@dimen/navigation_key_width" android:layout_weight="0" android:layout_width="match_parent" android:scaletype="center" android:src="@drawable/ic_sysbar_home_land" systemui:keycode="3" systemui:keyrepeat="false">

            <view android:layout_height="match_parent" android:layout_weight="1" android:layout_width="match_parent" android:visibility="invisible">

            <com.android.systemui.statusbar.policy.keybuttonview android:contentdescription="@string/accessibility_back" android:id="@+id/back" android:layout_height="@dimen/navigation_key_width" android:layout_weight="0" android:layout_width="match_parent" android:scaletype="center" android:src="@drawable/ic_sysbar_back_land" systemui:keycode="4">

            <view android:layout_height="@dimen/navigation_side_padding" android:layout_weight="0" android:layout_width="match_parent" android:visibility="invisible">

        </view></com.android.systemui.statusbar.policy.keybuttonview></view></com.android.systemui.statusbar.policy.keybuttonview></view></com.android.systemui.statusbar.policy.keybuttonview></linearlayout>

        <!-- lights out layout to match exactly -->

        <linearlayout android:id="@+id/lights_out" android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" android:visibility="gone">

            <imageview android:contentdescription="@string/accessibility_recent" android:layout_height="@dimen/navigation_key_width" android:layout_margintop="@dimen/navigation_side_padding" android:layout_weight="0" android:layout_width="match_parent" android:scaletype="center" android:src="@drawable/ic_sysbar_lights_out_dot_small">

            <view android:layout_height="match_parent" android:layout_weight="1" android:layout_width="match_parent" android:visibility="invisible">

            <imageview android:contentdescription="@string/accessibility_home" android:layout_height="@dimen/navigation_key_width" android:layout_weight="0" android:layout_width="match_parent" android:scaletype="center" android:src="@drawable/ic_sysbar_lights_out_dot_large">

            <view android:layout_height="match_parent" android:layout_weight="1" android:layout_width="match_parent" android:visibility="invisible">

            <imageview android:contentdescription="@string/accessibility_back" android:layout_height="@dimen/navigation_key_width" android:layout_marginbottom="@dimen/navigation_side_padding" android:layout_weight="0" android:layout_width="match_parent" android:scaletype="center" android:src="@drawable/ic_sysbar_lights_out_dot_small">

        </imageview></view></imageview></view></imageview></linearlayout>

        <com.android.systemui.statusbar.policy.deadzone android:id="@+id/deadzone" android:layout_gravity="top" android:layout_height="match_parent" android:layout_width="match_parent" systemui:decaytime="@integer/navigation_bar_deadzone_decay" systemui:holdtime="@integer/navigation_bar_deadzone_hold" systemui:maxsize="@dimen/navigation_bar_deadzone_size_max" systemui:minsize="@dimen/navigation_bar_deadzone_size" systemui:orientation="vertical">

    </com.android.systemui.statusbar.policy.deadzone></framelayout>

</com.android.systemui.statusbar.phone.navigationbarview></code></code>

所以说,当我们的需求为0或者90度方向,要想导航栏纵向显示,我们只需要修改成导航栏纵向布局即可,当然我们也可以按需求来隐藏某些导航栏按键(布局中设置某些控件为gone)

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

  @Override

    public void onFinishInflate() {

        mRotatedViews[Surface.ROTATION_0] =

        mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);

        mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);

        mRotatedViews[Surface.ROTATION_270] = mRotatedViews[Surface.ROTATION_90];

        //mCurrentView = mRotatedViews[Surface.ROTATION_0];

        //显示纵向的导航栏

        mCurrentView = mRotatedViews[Surface.ROTATION_90];

        getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);

        updateRTLOrder();

    }

导航栏LayoutParams分析

我们回到PhoneStatusBar的addNavigationBar继续分析最后一个导航栏的LayoutParameters,它决定了导航栏在窗体上的显示位置

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

<code class="hljs cs"><code class="hljs avrasm">    private WindowManager.LayoutParams getNavigationBarLayoutParams() {

        //充满父布局

        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(

                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,

                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,//导航栏

                    0

                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING//当手机处于睡眠状态时,如果屏幕被按下,那么该window将第一个收到到事件

                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE//不获取焦点

                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL//即使在该window在可获得焦点情况下,仍然把该window之外的任何event发送到该window之后的其他window

                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH//不接受事件,转发到其他window

                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,//当该window在可以接受触摸屏情况下,让因在该window之外,而发送到后面的window的触摸屏可以支持split touch.

                PixelFormat.TRANSLUCENT);

        // this will allow the navbar to run in an overlay on devices that support this

        if (ActivityManager.isHighEndGfx()) {

            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;//硬件加速

        }

        //lp.gravity = Gravity.RIGHT;

        lp.setTitle("NavigationBar");

        lp.windowAnimations = 0;

        return lp;

    }</code></code>

上面的LayoutParames决定了导航栏在窗体的大小(受父布局影响)和显示的位置效果,当我们的需求如果要把导航栏显示在屏幕的右边时,我们可以在上面代码中加上下面一句

1  lp.gravity = Gravity.RIGHT;

SystemUI包含了太多内容,本篇只是分析了SystemUI的加载流程,同时初步的分析了StatusBar中一个小模块NavigationBar,后续会针对SystemUI的其他模块进行分析。

猜你喜欢

转载自blog.csdn.net/kongbaidepao/article/details/82113623