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

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

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

AndroidManifest.xml

 <application
    android:name=".SystemUIApplication"
    android:persistent="true"
    android:allowClearUserData="false"
    android:allowBackup="false"
    android:hardwareAccelerated="true"
    android:label="@string/app_label"
    android:icon="@drawable/icon"
    android:process="com.android.systemui"
    android:supportsRtl="true"
    android:theme="@style/systemui_theme">
    <!-- 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:name="SystemUIService"
        android:exported="true"
        />

SystemUIService

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

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中进行启动

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的启动流程,其大概流程为

int -> ServerManager -> Zygote -> SystemServer

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

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

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

SystemUIApplication

onCreate

@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启动了这个方法,我们着重分析这个方法

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; i++) {
        Class<?> 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

 /**
 * 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

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);
    }
}

我们先从start方法开始分析

    @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
    }

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

ServiceMonitor

    ...
    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);
    }
    ...

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

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));
        }
    }
};

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

    // 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();
            }
        }
    }

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

    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);
    }

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

    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();
        }
    }

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

     ...
     @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());
    }

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

res\values\config.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>

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

PhoneStatusBar

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

start

    ...
    @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);
    }
    ...

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

BaseStatusBar

start

    ...
    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 notification listener", e);
        }

        if (DEBUG) {
            Log.d(TAG, String.format(
                    "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
                   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);

        IntentFilter allUsersFilter = new IntentFilter();
        allUsersFilter.addAction(
                DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
        mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
                null, null);
        updateCurrentProfilesCache();
    }
    ...

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

    /**
     * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
     * and add them to the window manager.
     */
    protected abstract void createAndAddWindows();

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

createAndAddWindows

    ...
    @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;
    }
    ...

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

    ...
    // 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方法中,我们可以看到导航栏是怎么创建的

    ...
    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
        }

        ...
        ...
    }
    ...

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

frameworks\base\service\core\java\com\android\server\PhoneWindowManager.java

PhoneWindowManager

    ...
    // 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;
    }
    ...

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

    <!-- 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>

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

导航栏布局分析及加载

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

    ...
    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);
    }
    ...

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

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

<com.android.systemui.statusbar.phone.NavigationBarView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:background="@drawable/system_bar_background"
    >
    <!--横向导航栏-->
    <FrameLayout android:id="@+id/rot0"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        >

        <LinearLayout
            android:layout_height="match_parent"
            android:layout_width="match_parent"
            android:orientation="horizontal"
            android:clipChildren="false"
            android:clipToPadding="false"
            android:id="@+id/nav_buttons"
            android:animateLayoutChanges="true"
            >

            <!-- navigation controls -->
            <View
                android:layout_width="@dimen/navigation_side_padding"
                android:layout_height="match_parent"
                android:layout_weight="0"
                android:visibility="invisible"
                />
            <!--back按钮-->
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
                android:layout_width="@dimen/navigation_key_width"
                android:layout_height="match_parent"
                android:src="@drawable/ic_sysbar_back"
                systemui:keyCode="4"
                android:layout_weight="0"
                android:scaleType="center"
                android:contentDescription="@string/accessibility_back"
                />
            <View
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:visibility="invisible"
                />
            <!--home按钮-->
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
                android:layout_width="@dimen/navigation_key_width"
                android:layout_height="match_parent"
                android:src="@drawable/ic_sysbar_home"
                systemui:keyCode="3"
                systemui:keyRepeat="false"
                android:layout_weight="0"
                android:scaleType="center"
                android:contentDescription="@string/accessibility_home"
                />
            <View
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:visibility="invisible"
                />
            <!--recent按钮-->
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
                android:layout_width="@dimen/navigation_key_width"
                android:layout_height="match_parent"
                android:src="@drawable/ic_sysbar_recent"
                android:layout_weight="0"
                android:scaleType="center"
                android:contentDescription="@string/accessibility_recent"
                />
            <FrameLayout
                android:layout_width="@dimen/navigation_side_padding"
                android:layout_height="match_parent"
                android:layout_weight="0" >
                <com.android.systemui.statusbar.policy.KeyButtonView
                    android:id="@+id/menu"
                    android:layout_width="@dimen/navigation_extra_key_width"
                    android:layout_height="match_parent"
                    android:contentDescription="@string/accessibility_menu"
                    android:src="@drawable/ic_sysbar_menu"
                    android:visibility="invisible"
                    android:scaleType="centerInside"
                    android:layout_gravity="end"
                    systemui:keyCode="82" />

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

        </LinearLayout>

        <!-- lights out layout to match exactly -->
        <LinearLayout
            android:layout_height="match_parent"
            android:layout_width="match_parent"
            android:orientation="horizontal"
            android:id="@+id/lights_out"
            android:visibility="gone"
            >
            <ImageView
                android:layout_width="@dimen/navigation_key_width"
                android:layout_height="match_parent"
                android:layout_marginStart="@dimen/navigation_side_padding"
                android:src="@drawable/ic_sysbar_lights_out_dot_small"
                android:scaleType="center"
                android:layout_weight="0"
                android:contentDescription="@string/accessibility_back"
                />
            <View
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:visibility="invisible"
                />
            <ImageView
                android:layout_width="@dimen/navigation_key_width"
                android:layout_height="match_parent"
                android:src="@drawable/ic_sysbar_lights_out_dot_large"
                android:scaleType="center"
                android:layout_weight="0"
                android:contentDescription="@string/accessibility_home"
                />
            <View
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:visibility="invisible"
                />
            <ImageView
                android:layout_width="@dimen/navigation_key_width"
                android:layout_marginEnd="@dimen/navigation_side_padding"
                android:layout_height="match_parent"
                android:src="@drawable/ic_sysbar_lights_out_dot_small"
                android:scaleType="center"
                android:layout_weight="0"
                android:contentDescription="@string/accessibility_recent"
                />
        </LinearLayout>

        <com.android.systemui.statusbar.policy.DeadZone
            android:id="@+id/deadzone"
            android:layout_height="match_parent"
            android:layout_width="match_parent"
            systemui:minSize="@dimen/navigation_bar_deadzone_size"
            systemui:maxSize="@dimen/navigation_bar_deadzone_size_max"
            systemui:holdTime="@integer/navigation_bar_deadzone_hold"
            systemui:decayTime="@integer/navigation_bar_deadzone_decay"
            systemui:orientation="horizontal"
            android:layout_gravity="top"
            />
    </FrameLayout>
    <!--纵向显示-->
    <FrameLayout android:id="@+id/rot90"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:visibility="gone"
        android:paddingTop="0dp"
        >

        <LinearLayout
            android:layout_height="match_parent"
            android:layout_width="match_parent"
            android:orientation="vertical"
            android:clipChildren="false"
            android:clipToPadding="false"
            android:id="@+id/nav_buttons"
            android:animateLayoutChanges="true"
            >

            <!-- navigation controls -->
            <FrameLayout
                android:layout_weight="0"
                android:layout_width="match_parent"
                android:layout_height="@dimen/navigation_side_padding" >
                <com.android.systemui.statusbar.policy.KeyButtonView
                    android:id="@+id/ime_switcher"
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/navigation_extra_key_width"
                    android:contentDescription="@string/accessibility_ime_switch_button"
                    android:scaleType="centerInside"
                    android:src="@drawable/ic_ime_switcher_default"
                    android:layout_gravity="top"
                    android:visibility="invisible" />

                <com.android.systemui.statusbar.policy.KeyButtonView
                    android:id="@+id/menu"
                    android:layout_width="match_parent"
                    android:layout_height="40dp"
                    android:contentDescription="@string/accessibility_menu"
                    android:src="@drawable/ic_sysbar_menu_land"
                    android:scaleType="centerInside"
                    android:layout_gravity="top"
                    android:visibility="invisible"
                    systemui:keyCode="82" />
            </FrameLayout>

            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
                android:layout_height="@dimen/navigation_key_width"
                android:layout_width="match_parent"
                android:src="@drawable/ic_sysbar_recent_land"
                android:scaleType="center"
                android:layout_weight="0"
                android:contentDescription="@string/accessibility_recent"
                />
            <View
                android:layout_height="match_parent"
                android:layout_width="match_parent"
                android:layout_weight="1"
                android:visibility="invisible"
                />
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
                android:layout_height="@dimen/navigation_key_width"
                android:layout_width="match_parent"
                android:src="@drawable/ic_sysbar_home_land"
                android:scaleType="center"
                systemui:keyCode="3"
                systemui:keyRepeat="false"
                android:layout_weight="0"
                android:contentDescription="@string/accessibility_home"
                />
            <View
                android:layout_height="match_parent"
                android:layout_width="match_parent"
                android:layout_weight="1"
                android:visibility="invisible"
                />
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
                android:layout_height="@dimen/navigation_key_width"
                android:layout_width="match_parent"
                android:src="@drawable/ic_sysbar_back_land"
                android:scaleType="center"
                systemui:keyCode="4"
                android:layout_weight="0"
                android:contentDescription="@string/accessibility_back"
                />
            <View
                android:layout_height="@dimen/navigation_side_padding"
                android:layout_width="match_parent"
                android:layout_weight="0"
                android:visibility="invisible"
                />
        </LinearLayout>

        <!-- lights out layout to match exactly -->
        <LinearLayout
            android:layout_height="match_parent"
            android:layout_width="match_parent"
            android:orientation="vertical"
            android:id="@+id/lights_out"
            android:visibility="gone"
            >
            <ImageView
                android:layout_height="@dimen/navigation_key_width"
                android:layout_marginTop="@dimen/navigation_side_padding"
                android:layout_width="match_parent"
                android:src="@drawable/ic_sysbar_lights_out_dot_small"
                android:scaleType="center"
                android:layout_weight="0"
                android:contentDescription="@string/accessibility_recent"
                />
            <View
                android:layout_height="match_parent"
                android:layout_width="match_parent"
                android:layout_weight="1"
                android:visibility="invisible"
                />
            <ImageView
                android:layout_height="@dimen/navigation_key_width"
                android:layout_width="match_parent"
                android:src="@drawable/ic_sysbar_lights_out_dot_large"
                android:scaleType="center"
                android:layout_weight="0"
                android:contentDescription="@string/accessibility_home"
                />
            <View
                android:layout_height="match_parent"
                android:layout_width="match_parent"
                android:layout_weight="1"
                android:visibility="invisible"
                />
            <ImageView
                android:layout_height="@dimen/navigation_key_width"
                android:layout_marginBottom="@dimen/navigation_side_padding"
                android:layout_width="match_parent"
                android:src="@drawable/ic_sysbar_lights_out_dot_small"
                android:scaleType="center"
                android:layout_weight="0"
                android:contentDescription="@string/accessibility_back"
                />
        </LinearLayout>

        <com.android.systemui.statusbar.policy.DeadZone
            android:id="@+id/deadzone"
            android:layout_height="match_parent"
            android:layout_width="match_parent"
            systemui:minSize="@dimen/navigation_bar_deadzone_size"
            systemui:maxSize="@dimen/navigation_bar_deadzone_size_max"
            systemui:holdTime="@integer/navigation_bar_deadzone_hold"
            systemui:decayTime="@integer/navigation_bar_deadzone_decay"
            systemui:orientation="vertical"
            android:layout_gravity="top"
            />
    </FrameLayout>

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

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

    @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,它决定了导航栏在窗体上的显示位置

    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;
    }

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

    lp.gravity = Gravity.RIGHT;

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

时间: 2024-10-20 11:28:48

Android之SystemUI加载流程和NavigationBar的分析的相关文章

Android View的加载流程

什么是Activity? Activity是 用户操作的可视化界面:它为用户提供了一个放置视图和交互操作的窗口.采用setContentView的方法提供.因此,可以理解Activity.Window.View三者关系为.Activity提供Window ,View被添加到Window中. 以刷墙举例: Activity可以理解为房间,Window就是房间内的墙面, 我们在墙面上可以刷各种不同的图案,这些图案就是View. Activity View的加载流程 1.Activity在被创建之初,

android listview异步加载图片错位,重复,闪烁分析以及解决方案

我们在使用listview异步加载图片 的时候,在快速滑动或者网络不好的情况下,会出现图片错位,重复,闪烁等问题,其实这些问题总结起来就是一个问题, 比如listview上有100个item,一屏只显示10个item,我们知道getView()中converView是用来复用view对象的,因为一个item的view对象,而imageview控件就是view通过findViewById()获得的,而我们在复用view对象时,也就是说这个imageview也被复用了,比如第11个item的view

android源码解析(十七)--&gt;Activity布局加载流程

好吧,终于要开始讲讲Activity的布局加载流程了,大家都知道在Android体系中Activity扮演了一个界面展示的角色,这也是它与android中另外一个很重要的组件Service最大的不同,但是这个展示的界面的功能是Activity直接控制的么?界面的布局文件是如何加载到内存并被Activity管理的?android中的View是一个怎样的概念?加载到内存中的布局文件是如何绘制出来的? 要想回答这些问题,我们就需要对android的界面加载与绘制流程有所了解,这里我们先来学习一下Act

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

Android之SystemUI载入流程和NavigationBar的分析 本篇仅仅分析SystemUI的载入过程和SystemUI的当中的一个模块StatusBar的小模块NavigationBar,以Android6.0代码进行分析 AndroidManifest.xml <application android:name=".SystemUIApplication" android:persistent="true" android:allowClear

Android中图片加载框架Glide解析2----从源码的角度理解Glide的执行流程

转载地址:http://blog.csdn.net/guolin_blog/article/details/53939176 在本系列的上一篇文章中,我们学习了Glide的基本用法,体验了这个图片加载框架的强大功能,以及它非常简便的API.还没有看过上一篇文章的朋友,建议先去阅读 Android图片加载框架最全解析(一),Glide的基本用法 . 在多数情况下,我们想要在界面上加载并展示一张图片只需要一行代码就能实现,如下所示: Glide.with(this).load(url).into(i

webkit 子资源加载流程

一个网页由主文档和子资源组成.主文档描述网页的框架,布局.子资源是组成网页的子元素,包括图片.CSS.JS等.为了显示网页,先要把资源加载到内存.加载就是指把需要的资源加载到内存这一过程.Webkit用到很多缓存机制,加载可能是从网络加载,也可能是从本地缓存加载.Webkit的加载分为两条线,一条是主文档的加载,一条是子资源的加载. 首先需要解析主文档才知道用到哪些子资源.但并不一定要等到解析完主文档才加载子资源,也可能是边解析边加载子资源,即受到部分主文档就开始解析,解析到某个子资源就开始加载

Android 解码器component加载

转载:http://blog.sina.com.cn/s/blog_645b74b90101djrj.html 前面一篇博客画出了Android中openBinder框架图,最近也看了一些openMAX IL的资料,知道IL Client要完成音视频的播放首先需要完成一些初始化工作,比如IL Client要按照openMAX框架加载对应的component,这篇博客中将细化这部分,看看Android是如何加载动态so库的. 一:首先是插件的管理,包含codec vendor的插件和谷歌自带的软解

Android5.1图库Gallery2代码分析数据加载流程

图片数据加载流程. Gallery---->GalleryActivity------>AlbumSetPage------->AlbumPage--------->PhotoPage 相册集                        照片集                 某张图片 1,AlbumSetPage.java private void initializeData(Bundle data) { String mediaPath = data.getString(A

Android处理延时加载的方法

在项目开发中,经常需要通过延时加载来实现满足我们的项目要求.那到底怎样来实现延时,下面结合java与android的相关方法来实现延时问题. 一.利用线程的Sleep方法 <span style="font-family:Microsoft YaHei;font-size:18px;"><span style="white-space:pre"> </span>new Thread(new Runnable(){ @Overrid