我们知道当Android系统启动的时候会启动SystemServer,其中系统的主要服务都是通过它来启动的,本文就从这里开始一步一步研究系统状态栏和导航栏是怎么启动的。
首先我们先定位到SystemServer.run()方法中来,如下是方法的定义。
private void run() {
try {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "InitBeforeStartServices");
.......
........
..........
// Initialize the system context.
//创建系统上下文
createSystemContext();
// Create the system service manager.
mSystemServiceManager =new SystemServiceManager(mSystemContext);
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
}finally {
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
// Start services.
try {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartServices");
startBootstrapServices();
startCoreServices();
startOtherServices();
.......
........
}
方法里面的内容比较多,我就列出主要的几个地方.
startBootstrapServices,startCoreServices,startOtherServices这个三个方法都是启动系统服务的,我们的系统状态栏和导航栏就是通过startOtherServices这个方法来间接启动的。
如下是方法的定义,当然也是列出我们主要关注的部分
private void startOtherServices() {
......
......
.......
//系统启动时候调用ActivityManagerService的systemReady方法
mActivityManagerService.systemReady(new Runnable() {
@Override
public void run() {
Slog.i(TAG, "Making services ready");
mSystemServiceManager.startBootPhase(
SystemService.PHASE_ACTIVITY_MANAGER_READY);
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "PhaseActivityManagerReady");
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartObservingNativeCrashes");
try {
mActivityManagerService.startObservingNativeCrashes();
}catch (Throwable e) {
reportWtf("observing native crashes", e);
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
if (!mOnlyCore) {
Slog.i(TAG, "WebViewFactory preparation");
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "WebViewFactoryPreparation");
mWebViewUpdateService.prepareWebViewInSystemServer();
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartSystemUI");
try {
startSystemUi(context);
}catch (Throwable e) {
reportWtf("starting System UI", e);
}
.......
......
.......
}
上面我们可以看到mActivityManagerService.systemReady,该方法里面会调用这个方法,这个是ActivityManagerService的方法,有名字就可以知道他是做一些系统启动的操作,在方法定义里面我们可以看到startSystemUi这个方法,点进去看看,方法的定义如下。
static final void startSystemUi(Context context) {
Intent intent =new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
//Slog.d(TAG, "Starting service: " + intent);
context.startServiceAsUser(intent, UserHandle.SYSTEM);
}
我们看到他是通过Intent启动SystemUIService这个类,看名字就可以知道他是一个服务。
我们进入这个类的onCreate方法看看,
@Override
public void onCreate() {
super.onCreate();
((SystemUIApplication) getApplication()).startServicesIfNeeded();
}
我们看到他只调用了一个方法,我们接着点进去看看,
public void startServicesIfNeeded() {
startServicesIfNeeded(SERVICES);
}
SERVICES变量是个static,final类型的变量,
我们接着点进去看看
private void startServicesIfNeeded(Class[] services) {
if (mServicesStarted) {
return;
}
if (!mBootCompleted) {
// check to see if maybe it was already completed long before we began
// see ActivityManagerService.finishBooting()
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 for user " +
Process.myUserHandle().getIdentifier() +".");
final int N = services.length;
for (int i=0; i
Class cl = services[i];
if (DEBUG) Log.d(TAG, "loading: " + cl);
try {
Object newService = SystemUIFactory.getInstance().createInstance(cl);
mServices[i] = (SystemUI) ((newService ==null) ? cl.newInstance() : newService);
}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;
}
他的主要作用就是遍历SERVICES数组,启动里面的所有服务,如下是SERVICES数组的定义。
/**
* 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,
Divider.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,
com.android.systemui.keyboard.KeyboardUI.class,
com.android.systemui.tv.pip.PipUI.class,
com.android.systemui.shortcut.ShortcutKeyDispatcher.class,
com.android.systemui.VendorServices.class
};
我们看到这个数组里面定义了很多的服务(其实它们都不是服务,全是SystemUI),其中SystemBars就是我们今天要关注的服务,因为Android系统的状态栏和导航栏就是在这里面启动的。
从上面我们可以知道,在遍历初始化数组里面的类后,先调用了他们的start(),,如果系统启动完成了就会调用onBootCompleted()方法。我们先看看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
}
我们可以看到它是实例化了一个对象并且调用的它的start方法。
我们先看看它的构造方法
public ServiceMonitor(String ownerTag, boolean debug,
Context context, String settingKey, Callbacks callbacks) {
mTag = ownerTag +".ServiceMonitor";
mDebug = debug;
mContext = context;
mSettingKey = settingKey;
mCallbacks = callbacks;
}
我们看到构造方法只是做了一个初始化操作,我们接着进入它的start方法。
public void start() {
// listen for setting changes
ContentResolver cr =mContext.getContentResolver();
cr.registerContentObserver(Settings.Secure.getUriFor(mSettingKey),
false /*notifyForDescendents*/, mSettingObserver, UserHandle.USER_ALL);
// listen for package/component changes
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);
}
我们看到这个方法里面主要做了两件事情,一个就是注册广播监听安装包的添加,改变,移除,另外一件事情就是发送消息。所以我们进入handler去看看,它是怎么处理这条空消息的,
case MSG_START_SERVICE:
startService();
break;
我们看到它调用了startService方法,我们接着点进去看看
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);
}
}
我们看到当mServiceName为null的时候会执行这个mCallbacks.onNoService()方法,由上面介绍的该类初始化我们知道这个回掉接口是从SystemBars传过来的,我们回到SystemBars类找到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");
//从xml文件获取到类名
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;
//调用它的start方法
mStatusBar.start();
if (DEBUG) Log.d(TAG, "started " +mStatusBar.getClass().getSimpleName());
}
这里xml定义的类名是PhoneStatusBar,我们直接进入它的start方法
@Override
public void start() {
//获取屏幕信息Display对象,里面保存了和屏幕有关的所有信息
mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
//这个方法的作用是将屏幕的信息置为默认值。
.getDefaultDisplay();
//更新屏幕信息
updateDisplaySize();
mScrimSrcModeEnabled = mContext.getResources().getBoolean(
R.bool.config_status_bar_scrim_behind_use_src);
//这里面主要做了两件事,一个是初始化,一个是注册广播监听器
super.start(); // calls createAndAddWindows()
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.
//这是PhoneStatusBar的代理对象,一看就知道这里采用了代理模式
mIconPolicy =new PhoneStatusBarPolicy(mContext, mIconController, mCastController,
mHotspotController, mUserInfoController, mBluetoothController,
mRotationLockController, mNetworkController.getDataSaverController());
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();
KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
mDozeServiceHost =new DozeServiceHost();
putComponent(DozeHost.class, mDozeServiceHost);
putComponent(PhoneStatusBar.class, this);
setControllerUsers();
notifyUserAboutHiddenNotifications();
mScreenPinningRequest =new ScreenPinningRequest(mContext);
mFalsingManager = FalsingManager.getInstance(mContext);
}
,我们看到上面调用了super.start()方法,我们的状态栏就是在这里初始化的,我们进入这个方法定义
public void start() {
mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
mDisplay = mWindowManager.getDefaultDisplay();
mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService( Context.DEVICE_POLICY_SERVICE);
mNotificationData = new NotificationData(this);
.........
.........
.......
createAndAddWindows();
.........
.........
.......
}
中间省略了大部分代码,我们主要看createAndAddWindows这个方法,看方法名称就可以知道,这个类是的功能是创建视图并且添加到window窗口,一下是方法的定义
@Override
public void createAndAddWindows() {
addStatusBarWindow();
}
接着我们进入addStatusBarWindow这个方法,
private void addStatusBarWindow() {
//这个类主要是创建了系统状态栏,并且设置了一些监听器,比如状态栏的手势事件,电池电量改变监听器,sim卡状态改变监听器等等。
makeStatusBarView();
mStatusBarWindowManager = new StatusBarWindowManager(mContext);
mRemoteInputController = new RemoteInputController(mStatusBarWindowManager,
mHeadsUpManager);
//getStatusBarHeight这个方法是从xml中获取已经定义好的状态栏高度值,mStatusBarWindowManager.add这个方法的作用是将状态栏添加到Window中
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}
到此系统状态栏的创建流程就结束了,但是状态栏的手势事件是在哪里监听的呢,下面就来研究下手势状态栏手势事件的监听流程
由上面我们可以知道在添加状态栏的方法addStatusBarWindow中有个创建状态栏的方法makeStatusBarView,我们进入这个方法查看下,方法定义如下
protected PhoneStatusBarView makeStatusBarView() {
final Context context = mContext;
updateDisplaySize(); // populates mDisplayMetrics
updateResources();
inflateStatusBarWindow(context);
mStatusBarWindow.setService(this);
mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
checkUserAutohide(v, event);
//如果手势是刚点击状态栏(没有事件拦截)并且状态栏是展开的情况下,动画回收状态栏,否则跳到mStatusBarWindow的
onTouchEvent方法
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (mExpandedVisible) {
animateCollapsePanels();
}
}
return mStatusBarWindow.onTouchEvent(event);
}
});
...
...
...
}
从这个方法中我们看到系统在创建状态栏的时候一道给它设置了触摸事件。如果状态栏实在展开的情况下,当用户点击了状态栏的没有事件拦截区域,此时状态栏会收缩并且带有动画效果。否则会进入StatusBarWindowView的onTouchEvent处理,那我们进入这个方法查看,如下是方法的定义
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
if (mService.getBarState() == StatusBarState.KEYGUARD) {
handled = mDragDownHelper.onTouchEvent(ev);
}
if (!handled) {
handled = super.onTouchEvent(ev);
}
final int action = ev.getAction();
if (!handled && (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL)) {
mService.setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
}
return handled;
}
我们先进入mDragDownHelper.onTouchEvent(ev)这个方法查看,方法定义如下
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!mDraggingDown) {
return false;
}
final float x = event.getX();
final float y = event.getY();
switch (event.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
mLastHeight = y - mInitialTouchY;
captureStartingChild(mInitialTouchX, mInitialTouchY);
if (mStartingChild != null) {
handleExpansion(mLastHeight, mStartingChild);
} else {
mDragDownCallback.setEmptyDragAmount(mLastHeight);
}
if (mLastHeight > mMinDragDistance) {
if (!mDraggedFarEnough) {
mDraggedFarEnough = true;
mDragDownCallback.onCrossedThreshold(true);
}
} else {
if (mDraggedFarEnough) {
mDraggedFarEnough = false;
mDragDownCallback.onCrossedThreshold(false);
}
}
return true;
case MotionEvent.ACTION_UP:
if (!isFalseTouch() && mDragDownCallback.onDraggedDown(mStartingChild,
(int) (y - mInitialTouchY))) {
if (mStartingChild == null) {
mDragDownCallback.setEmptyDragAmount(0f);
} else {
mCallback.setUserLockedChild(mStartingChild, false);
}
mDraggingDown = false;
} else {
stopDragging();
return false;
}
break;
case MotionEvent.ACTION_CANCEL:
stopDragging();
return false;
}
return false;
}
当手势移动的时候会进入handleExpansion这个方法,它的定义如下
private void handleExpansion(float heightDelta, ExpandableView child) {
if (heightDelta < 0) {
heightDelta = 0;
}
boolean expandable = child.isContentExpandable();
float rubberbandFactor = expandable
? RUBBERBAND_FACTOR_EXPANDABLE
: RUBBERBAND_FACTOR_STATIC;
float rubberband = heightDelta * rubberbandFactor;
if (expandable
&& (rubberband + child.getCollapsedHeight()) > child.getMaxContentHeight()) {
float overshoot =
(rubberband + child.getCollapsedHeight()) - child.getMaxContentHeight();
overshoot *= (1 - RUBBERBAND_FACTOR_STATIC);
rubberband -= overshoot;
}
child.setActualHeight((int) (child.getCollapsedHeight() + rubberband));
}
我们看到了经过计算最终调用child.setActualHeight这个方法,设置状态栏的高度,来动态改变状态栏的显示高度,而松开手会调用接口回调这里就不深入研究了。到此系统状态栏的创建添加手势监听流程就彻底结束了。
接下来继续研究系统导航栏的创建添加过程。
接着上面我们看如下定义
public void start() {
.
.
createAndAddWindows();
.
.
.
}
此处省略了大部分代码,我们只关心和我们今天主题有关系的即可。
createAndAddWindows的实现实在PhoneStatusBar里
我们新进入addNavigationBar这个方法
// For small-screen devices (read: phones) that lack hardware navigation buttons
protected void addNavigationBar() {
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
if (mNavigationBarView ==null)return;
try {
WindowManagerGlobal.getWindowManagerService()
.watchRotation(new IRotationWatcher.Stub() {
@Override
public void onRotationChanged(int rotation)throws RemoteException {
// We need this to be scheduled as early as possible to beat the redrawing of
// window in response to the orientation change.
Message msg = Message.obtain(mHandler, () -> {
if (mNavigationBarView !=null
&& mNavigationBarView.needsReorient(rotation)) {
repositionNavigationBar();
}
});
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
});
}catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
//
prepareNavigationBarView();
mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
}
我们接着看prepareNavigationBarView这个方法,定义如下
private void prepareNavigationBarView() {
mNavigationBarView.reorient();
ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
recentsButton.setOnClickListener(mRecentsClickListener);
recentsButton.setOnTouchListener(mRecentsPreloadOnTouchListener);
recentsButton.setLongClickable(true);
recentsButton.setOnLongClickListener(mRecentsLongClickListener);
ButtonDispatcher backButton = mNavigationBarView.getBackButton();
backButton.setLongClickable(true);
backButton.setOnLongClickListener(mLongPressBackListener);
ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
homeButton.setOnTouchListener(mHomeActionListener);
homeButton.setOnLongClickListener(mLongPressHomeListener);
mAssistManager.onConfigurationChanged();
}
从这里我们看到它设置了系统状态栏的点击事件,长按事件,触摸事件等。
到此系统状态栏,导航栏的创建到事件监听大致流程分析完毕了,今天就到此结束吧。