SystemUI是Android为用户提供系统级别的信息显示与交互的一套UI组件,它包含屏幕顶端的状态栏,底部的导航栏,图片壁纸及RecentPanel(近期使用的APP).SystemUI的表现形式与普通应用类似,也是以一个APK的形式存在于系统之中。其中比较特殊的是状态栏和导航栏的启动方式,它们运行在一个SystemUIService中。现以导航栏的启动方式为例。 如下:
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);
}
public class SystemUIService extends Service {
@Override
public void onCreate() {
super.onCreate();
((SystemUIApplication) getApplication()).startServicesIfNeeded();
//获取Application调用startServicesIfNeeded
}
/*打印堆栈信息*/
@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);
}
}
}
}
}
分析:1 是启动SystemUIApplication 2 是打印堆栈信息
SystemUIApplication.java
@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);
}
分析: 1设置主题 2 注册开机广播,及设置标志位
SystemUIApplication.java
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。
SystemUIApplication.java
/**
* 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类。
com.android.systemui.statusbar
SystemBars.java
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.java
...
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));
}
}
};
ServiceMOnitor.java
应用装载时,通过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。
res\values\config.xml
com.android.systemui.statusbar.phone.PhoneStatusBar
分析:从上面分析得知,当实例化PhoneStatusBar类后会调用start方法,我们就从PhoneStatusBar的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方法
public abstract class BaseStatusBar extends SystemUI implements
CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
RecentsComponent.Callbacks, ExpandableNotificationRow.ExpansionLogger,
NotificationData.Environment {
public void start() {
Log.d(TAG, "[*zxDebug*] BaseStatusBar start()");
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);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mRecents = getComponent(RecentsComponent.class);
mRecents.setCallback(this);
final Configuration currentConfig = mContext.getResources().getConfiguration();
StatusBarIconList iconList = new StatusBarIconList();
mCommandQueue = new CommandQueue(this, iconList);
int[] switches = new int[8];
ArrayList binders = new ArrayList();
try {
mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders);
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
Log.d(TAG, "[*zxDebug*] BaseStatusBar start() begin createAndAddWindows");
createAndAddWindows();
Log.d(TAG, "[*zxDebug*] BaseStatusBar start() over createAndAddWindows");
disable(switches[0], 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);
...
}
}
分析: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的方法实现
@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)));
}
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;
.....
分析:关键部分是在判断导航栏是否显示那一块。由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的值
ture
分析:这里给我们提供一个方法去控制导航栏是否显示。