Android 系統狀態欄和導航欄啟動流程

我們知道當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();

? ? }

從這里我們看到它設置了系統狀態欄的點擊事件,長按事件,觸摸事件等。


到此系統狀態欄,導航欄的創建到事件監聽大致流程分析完畢了,今天就到此結束吧。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,283評論 6 530
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 97,947評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,094評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,485評論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,268評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,817評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,906評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,039評論 0 285
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,551評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,502評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,662評論 1 366
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,188評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,907評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,304評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,563評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,255評論 3 389
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,637評論 2 370

推薦閱讀更多精彩內容