Android之SystemUI载入流程和NavigationBar的分析
本篇仅仅分析SystemUI的载入过程和SystemUI的当中的一个模块StatusBar的小模块NavigationBar,以Android6.0代码进行分析
AndroidManifest.xml
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
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
@Overridepublic 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
> 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.xmlcom.android.systemui.statusbar.phone.PhoneStatusBar
从上面能够知道,终于程序会载入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]; ArrayListbinders = new ArrayList (); 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
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.javaPhoneWindowManager
... // 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的值
ture
然后从系统配置位置中取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所以说,当我们的需求为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的其它模块进行分析。