Android之SystemUI载入流程和NavigationBar的分析
Android之SystemUI载入流程和NavigationBar的分析
本篇仅仅分析SystemUI的载入过程和SystemUI的当中的一个模块StatusBar的小模块NavigationBar,以Android6.0代码进行分析
AndroidManifest.xml
<application
android:name=".SystemUIApplication"
android:persistent="true"
android:allowClearUserData="false"
android:allowBackup="false"
android:hardwareAccelerated="true"
android:label="@string/app_label"
android:icon="@drawable/icon"
android:process="com.android.systemui"
android:supportsRtl="true"
android:theme="@style/systemui_theme">
<!-- Keep theme in sync with SystemUIApplication.onCreate().
Setting the theme on the application does not affect views inflated by services.
The application theme is set again from onCreate to take effect for those views. -->
<!-- Broadcast receiver that gets the broadcast at boot time and starts
up everything else.
TODO: Should have an android:permission attribute
-->
<service android:name="SystemUIService"
android:exported="true"
/>
SystemUIService
跟StatusBar相关的服务为SystemUIService
,我们查看SystemUIService
源代码
public class SystemUIService extends Service {
@Override
public void onCreate() {
super.onCreate();
((SystemUIApplication) getApplication()).startServicesIfNeeded();
//获取Application调用startServicesIfNeeded
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
/*打印堆栈信息*/
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
SystemUI[] services = ((SystemUIApplication) getApplication()).getServices();
if (args == null || args.length == 0) {
for (SystemUI ui: services) {
pw.println("dumping service: " + ui.getClass().getName());
ui.dump(fd, pw, args);
}
} else {
String svc = args[0];
for (SystemUI ui: services) {
String name = ui.getClass().getName();
if (name.endsWith(svc)) {
ui.dump(fd, pw, args);
}
}
}
}
}
分析SystemUIService
代码,能够知道SystemUI主要做了两件事
- 获取
Application
对象载入SystemUI相关的类,这个等下分析SystemUIApplication
代码能够知道 dump
打印SystenUISerice
执行过程中相关的堆栈信息
那么SystemUIService
又是哪里開始启动的呢?居然SystemUIService是个服务,那么启动服务要么就是startService
要么就是bindService进行启动,其启动方式则须要通过Intent来传入类名或者包名。因此在源代码中搜索SystemUIService能够对照发现。它在
frameworks\base\services\java\com\android\server\SystemServer.java中进行启动
static final void startSystemUi(Context context) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
//Slog.d(TAG, "Starting service: " + intent);
context.startServiceAsUser(intent, UserHandle.OWNER);
}
在SystemServer的run方法中startOtherServices来启动SystemUIService服务,至于SystemServer则涉及到Android的启动流程。其大概流程为
int -> ServerManager -> Zygote -> SystemServer
SystemServer中会初始化一些Android的java层的服务,如ActivityManagerService、WindowManagerService等
这里SystemUI的载入过程就到此告一段落了,以下分析StatusBar的载入流程
上面讲到在SystemUIService的onCreate中获取SystemUIApplication对象来初始化SystemUI相关的类,这些类里面就包括了StatusBar相关的类,我们查看SystemUIApplication类
SystemUIApplication
onCreate
@Override
public void onCreate() {
super.onCreate();
// Set the application theme that is inherited by all services. Note that setting the
// application theme in the manifest does only work for activities. Keep this in sync with
// the theme set there.
setTheme(R.style.systemui_theme);
//凝视广播
IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//开启直接返回
if (mBootCompleted) return;
if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
unregisterReceiver(this);
//标记启动
mBootCompleted = true;
//服务是否启动
if (mServicesStarted) {
final int N = mServices.length;
for (int i = 0; i < N; i++) {
//回调各服务的onBootCompleted函数
mServices[i].onBootCompleted();
}
}
}
}, filter);
}
在SystemUIApplication的onCreate中主要做了
- 设置主题(这个会影响其SystemUI的界面显示效果)
- 注冊开机广播。设置标志位
startServicesIfNeeded
SystemUIService中的onCreate启动了这种方法。我们着重分析这种方法
public void startServicesIfNeeded() {
if (mServicesStarted) {
return;
}
if (!mBootCompleted) {
// check to see if maybe it was already completed long before we began
// see ActivityManagerService.finishBooting()
//获取系统文件里的sys.boot_completed的值
if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
mBootCompleted = true;
if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent");
}
}
Log.v(TAG, "Starting SystemUI services.");
final int N = SERVICES.length;
for (int i=0; i<N; i++) {
Class<?
> cl = SERVICES[i];
if (DEBUG) Log.d(TAG, "loading: " + cl);
//实例化各个类实例,放入mServices数组中
try {
mServices[i] = (SystemUI)cl.newInstance();
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InstantiationException ex) {
throw new RuntimeException(ex);
}
mServices[i].mContext = this;
mServices[i].mComponents = mComponents;
if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
mServices[i].start();
if (mBootCompleted) {
mServices[i].onBootCompleted();
}
}
//服务启动标志
mServicesStarted = true;
}
这种方法中,首先推断mServicesStarted
标志为来推断SystemUI相关的服务是否启动,同一时候依据系统配置文件来检查ActivityManagerService是否finishBooting。然后通过类载入机制来初始化SERVICES数组里面相关的类加入mServices中,然后start
/**
* The classes of the stuff to start.
*/
private final Class<?>[] SERVICES = new Class[] {
com.android.systemui.tuner.TunerService.class,//定制状态栏服务
com.android.systemui.keyguard.KeyguardViewMediator.class,//锁屏相关
com.android.systemui.recents.Recents.class,//最近任务
com.android.systemui.volume.VolumeUI.class,//音量条
com.android.systemui.statusbar.SystemBars.class,//状态栏
com.android.systemui.usb.StorageNotification.class,//通知栏
com.android.systemui.power.PowerUI.class,//电源相关
com.android.systemui.media.RingtonePlayer.class,//铃声播放相关
};
/**
* Hold a reference on the stuff we start.
*/
private final SystemUI[] mServices = new SystemUI[SERVICES.length];
从mServices和SERVICES的定义能够发现SERVICES是一组包括全路径的相关的类,这些类包括一些我们常见的TunerService(定制状态栏服务)、
KeyguardViewMediator(锁屏相关)、Recents(最近任务)、VolumeUI(音量条)、SystemBars(状态栏)、StorageNotification(通知栏)、PowerUI(电源相关)、RingtonePlayer(铃声播放相关)类,它们都是继承与SystemUI抽象类。如今仅仅分析StatusBar相关的SystemBars类
SystemBars
public class SystemBars extends SystemUI implements ServiceMonitor.Callbacks {
private static final String TAG = "SystemBars";
private static final boolean DEBUG = false;
private static final int WAIT_FOR_BARS_TO_DIE = 500;
// manages the implementation coming from the remote process
private ServiceMonitor mServiceMonitor;
// in-process fallback implementation, per the product config
private BaseStatusBar mStatusBar;
@Override
public void start() {
if (DEBUG) Log.d(TAG, "start");
//实例化ServiceMonitor
mServiceMonitor = new ServiceMonitor(TAG, DEBUG,
mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this);
//start
mServiceMonitor.start(); // will call onNoService if no remote service is found
}
/*服务没启动时。ServiceMonitor会回调onNoService*/
@Override
public void onNoService() {
if (DEBUG) Log.d(TAG, "onNoService");
createStatusBarFromConfig(); // fallback to using an in-process implementation
}
/*服务已经启动的回调*/
@Override
public long onServiceStartAttempt() {
if (DEBUG) Log.d(TAG, "onServiceStartAttempt mStatusBar="+mStatusBar);
if (mStatusBar != null) {
// tear down the in-process version, we'll recreate it again if needed
mStatusBar.destroy();
mStatusBar = null;
return WAIT_FOR_BARS_TO_DIE;
}
return 0;
}
/*系统配置改变*/
@Override
protected void onConfigurationChanged(Configuration newConfig) {
if (mStatusBar != null) {
mStatusBar.onConfigurationChanged(newConfig);
}
}
/*打印堆栈*/
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mStatusBar != null) {
mStatusBar.dump(fd, pw, args);
}
}
/*从xml文件里获取PhoneStatusBar全路径。通过类载入器实例化类,调用其start*/
private void createStatusBarFromConfig() {
if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
final String clsName = mContext.getString(R.string.config_statusBarComponent);
if (clsName == null || clsName.length() == 0) {
throw andLog("No status bar component configured", null);
}
Class<?> cls = null;
try {
cls = mContext.getClassLoader().loadClass(clsName);
} catch (Throwable t) {
throw andLog("Error loading status bar component: " + clsName, t);
}
try {
mStatusBar = (BaseStatusBar) cls.newInstance();
} catch (Throwable t) {
throw andLog("Error creating status bar component: " + clsName, t);
}
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mComponents;
mStatusBar.start();
if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
}
private RuntimeException andLog(String msg, Throwable t) {
Log.w(TAG, msg, t);
throw new RuntimeException(msg, t);
}
}
我们先从start方法開始分析
@Override
public void start() {
if (DEBUG) Log.d(TAG, "start");
mServiceMonitor = new ServiceMonitor(TAG, DEBUG,
mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this);
mServiceMonitor.start(); // will call onNoService if no remote service is found
}
这里实例化ServiceMonitor类start,继续分析ServiceMonitor
ServiceMonitor
...
public ServiceMonitor(String ownerTag, boolean debug,
Context context, String settingKey, Callbacks callbacks) {
mTag = ownerTag + ".ServiceMonitor";
mDebug = debug;
mContext = context;
mSettingKey = settingKey; // Settings.Secure.BAR_SERVICE_COMPONENT
mCallbacks = callbacks;
}
public void start() {
// listen for setting changes
/*Settings.Secure.BAR_SERVICE_COMPONENT改变时回调*/
ContentResolver cr = mContext.getContentResolver();
cr.registerContentObserver(Settings.Secure.getUriFor(mSettingKey),
false /*notifyForDescendents*/, mSettingObserver, UserHandle.USER_ALL);
// listen for package/component changes
//应用安装。改变。卸载会触发mBroadcastReceiver广播
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
mContext.registerReceiver(mBroadcastReceiver, filter);
mHandler.sendEmptyMessage(MSG_START_SERVICE);
}
...
ServiceMOnitor是一个监听Settings.Secure.BAR_SERVICE_COMPONENT是否改变的类,在start中通过监听系统系统时应用的变化来启动服务
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String pkg = intent.getData().getSchemeSpecificPart();
if (mServiceName != null && mServiceName.getPackageName().equals(pkg)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_PACKAGE_INTENT, intent));
}
}
};
应用装载时,通过Handler发送MSG_PACKAGE_INTENT消息事件,我们查看Handler消息回调
// internal handler + messages used to serialize access to internal state
public static final int MSG_START_SERVICE = 1; //启动服务,并不是真正启动。会依据ServiceName进行推断
public static final int MSG_CONTINUE_START_SERVICE = 2; //启动服务
public static final int MSG_STOP_SERVICE = 3;//停止服务消息
public static final int MSG_PACKAGE_INTENT = 4;//包安装事件消息
public static final int MSG_CHECK_BOUND = 5;//包改变或者卸载时。又一次启动服务消息
public static final int MSG_SERVICE_DISCONNECTED = 6;//服务断开消息
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch(msg.what) {
case MSG_START_SERVICE:
startService();
break;
case MSG_CONTINUE_START_SERVICE:
continueStartService();
break;
case MSG_STOP_SERVICE:
stopService();
break;
case MSG_PACKAGE_INTENT:
packageIntent((Intent)msg.obj);
break;
case MSG_CHECK_BOUND:
checkBound();
break;
case MSG_SERVICE_DISCONNECTED:
serviceDisconnected((ComponentName)msg.obj);
break;
}
}
};
private void packageIntent(Intent intent) {
if (mDebug) Log.d(mTag, "packageIntent intent=" + intent
+ " extras=" + bundleToString(intent.getExtras()));
if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
mHandler.sendEmptyMessage(MSG_START_SERVICE);//发送启动服务消息
} else if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())
|| Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
final PackageManager pm = mContext.getPackageManager();
final boolean serviceEnabled = isPackageAvailable()
&& pm.getApplicationEnabledSetting(mServiceName.getPackageName())
!= PackageManager.COMPONENT_ENABLED_STATE_DISABLED
&& pm.getComponentEnabledSetting(mServiceName)
!= PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
if (mBound && !serviceEnabled) {
stopService();
scheduleCheckBound();
} else if (!mBound && serviceEnabled) {
startService();
}
}
}
当我们SystemUI应用检測到有新应用装载时,会发送MSG_START_SERVICE消息来启动服务,我们接着分析Handler的回调MSG_START_SERVICE消息
private void startService() {
mServiceName = getComponentNameFromSetting();
if (mDebug) Log.d(mTag, "startService mServiceName=" + mServiceName);
if (mServiceName == null) {
mBound = false;
mCallbacks.onNoService();
} else {
long delay = mCallbacks.onServiceStartAttempt();
mHandler.sendEmptyMessageDelayed(MSG_CONTINUE_START_SERVICE, delay);
}
}
/*从ContentProvider数据库中取得包名*/
private ComponentName getComponentNameFromSetting() {
String cn = Settings.Secure.getStringForUser(mContext.getContentResolver(),
mSettingKey, UserHandle.USER_CURRENT);
return cn == null ? null : ComponentName.unflattenFromString(cn);
}
首先从ContentProvider数据库中取得包名,假设没有启动,则回调CallBaback的onNoService服务,否则发送MSG_CONTINUE_START_SERVICE消息启动服务
private void continueStartService() {
if (mDebug) Log.d(mTag, "continueStartService");
Intent intent = new Intent().setComponent(mServiceName);
try {
mServiceConnection = new SC();
mBound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
if (mDebug) Log.d(mTag, "mBound: " + mBound);
} catch (Throwable t) {
Log.w(mTag, "Error binding to service: " + mServiceName, t);
}
if (!mBound) {
mCallbacks.onNoService();
}
}
至此能够知道。当远程服务没有启动时,会回调SystemBar的onNoService
函数。我们回到SystemBar,分析onNoService
函数
...
@Override
public void onNoService() {
if (DEBUG) Log.d(TAG, "onNoService");
createStatusBarFromConfig(); // fallback to using an in-process implementation
}
...
private void createStatusBarFromConfig() {
if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
final String clsName = mContext.getString(R.string.config_statusBarComponent);//从xml文件读取类名
if (clsName == null || clsName.length() == 0) {
throw andLog("No status bar component configured", null);
}
//通过类载入器实例化类
Class<?> cls = null;
try {
cls = mContext.getClassLoader().loadClass(clsName);
} catch (Throwable t) {
throw andLog("Error loading status bar component: " + clsName, t);
}
try {
mStatusBar = (BaseStatusBar) cls.newInstance();
} catch (Throwable t) {
throw andLog("Error creating status bar component: " + clsName, t);
}
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mComponents;
mStatusBar.start();//调用类的start方法
if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
}
上面分析能够得知。当远程服务没有启动时,首先从xml文件读取要启动的类名,我们来查看这个xml文件
res\values\config.xml
<!-- Component to be used as the status bar service. Must implement the IStatusBar
interface. This name is in the ComponentName flattened format (package/class) -->
<string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string>
从上面能够知道,终于程序会载入PhoneStatusBar这个类,接下来分析PhoneStatusBar
PhoneStatusBar
首先我们从上面分析得知,当实例化PhoneStatusBar类后会调用start方法,我们就从PhoneStatusBar的start方法開始分析
start
...
@Override
public void start() {
//获取WindowManager。初始化当前显示界面大小
mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
updateDisplaySize();
//src画图模式
mScrimSrcModeEnabled = mContext.getResources().getBoolean(
R.bool.config_status_bar_scrim_behind_use_src);
//调用父类start方法
super.start(); // calls createAndAddWindows()
//MediaSession相关
mMediaSessionManager
= (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
// TODO: use MediaSessionManager.SessionListener to hook us up to future updates
// in session state
//加入导航栏
addNavigationBar();
// Lastly, call to the icon policy to install/update all the icons.
//更新状态栏图标
mIconPolicy = new PhoneStatusBarPolicy(mContext, mCastController, mHotspotController,
mUserInfoController, mBluetoothController);
mIconPolicy.setCurrentUserSetup(mUserSetup);
mSettingsObserver.onChange(false); // set up
mHeadsUpObserver.onChange(true); // set up
if (ENABLE_HEADS_UP) {
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true,
mHeadsUpObserver);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
mHeadsUpObserver);
}
mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
mUnlockMethodCache.addListener(this);
//锁屏
startKeyguard();
mDozeServiceHost = new DozeServiceHost();
KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mDozeServiceHost);
putComponent(DozeHost.class, mDozeServiceHost);
putComponent(PhoneStatusBar.class, this);
/// M:add for multi window @{
if(MultiWindowProxy.isSupported()) {
registerMWProxyAgain();
}
/// @}
setControllerUsers();
notifyUserAboutHiddenNotifications();
mScreenPinningRequest = new ScreenPinningRequest(mContext);
}
...
我们接着分析PhoneStatusBar父类的BaseStatusBar的start方法
BaseStatusBar
start
...
public void start() {
//获取Dispaly
mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
mDisplay = mWindowManager.getDefaultDisplay();
mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
Context.DEVICE_POLICY_SERVICE);
mNotificationColorUtil = NotificationColorUtil.getInstance(mContext);
mNotificationData = new NotificationData(this);
mAccessibilityManager = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
mDreamManager = IDreamManager.Stub.asInterface(
ServiceManager.checkService(DreamService.DREAM_SERVICE));
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
//监听设置文件的改变。以便更新ContenProvider数据库
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
mSettingsObserver);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
mSettingsObserver);
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
mSettingsObserver,
UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
true,
mLockscreenSettingsObserver,
UserHandle.USER_ALL);
//载入startbarService服务
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
//设置最近任务回调
mRecents = getComponent(Recents.class);
mRecents.setCallback(this);
//获取本地配置
final Configuration currentConfig = mContext.getResources().getConfiguration();
mLocale = currentConfig.locale;
mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
mFontScale = currentConfig.fontScale;
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
//载入动画
mLinearOutSlowIn = AnimationUtils.loadInterpolator(mContext,
android.R.interpolator.linear_out_slow_in);
mFastOutLinearIn = AnimationUtils.loadInterpolator(mContext,
android.R.interpolator.fast_out_linear_in);
// Connect in to the status bar manager service
StatusBarIconList iconList = new StatusBarIconList();
mCommandQueue = new CommandQueue(this, iconList);
int[] switches = new int[8];
ArrayList<IBinder> binders = new ArrayList<IBinder>();
try {
mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders);
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
//调用createAndAddWindows方法
createAndAddWindows();
mSettingsObserver.onChange(false); // set up
disable(switches[0], switches[6], false /* animate */);
setSystemUiVisibility(switches[1], 0xffffffff);
topAppWindowChanged(switches[2] != 0);
// StatusBarManagerService has a back up of IME token and it's restored here.
setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
// Set up the initial icon state
int N = iconList.size();
int viewIndex = 0;
for (int i=0; i<N; i++) {
StatusBarIcon icon = iconList.getIcon(i);
if (icon != null) {
addIcon(iconList.getSlot(i), i, viewIndex, icon);
viewIndex++;
}
}
// Set up the initial notification state.
try {
mNotificationListener.registerAsSystemService(mContext,
new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
UserHandle.USER_ALL);
} catch (RemoteException e) {
Log.e(TAG, "Unable to register notification listener", e);
}
if (DEBUG) {
Log.d(TAG, String.format(
"init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
iconList.size(),
switches[0],
switches[1],
switches[2],
switches[3]
));
}
mCurrentUserId = ActivityManager.getCurrentUser();
setHeadsUpUser(mCurrentUserId);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_PRESENT);
filter.addAction(BANNER_ACTION_CANCEL);
filter.addAction(BANNER_ACTION_SETUP);
mContext.registerReceiver(mBroadcastReceiver, filter);
IntentFilter allUsersFilter = new IntentFilter();
allUsersFilter.addAction(
DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
null, null);
updateCurrentProfilesCache();
}
...
BaseStatusBar关于StatusBar相关的最主要是调用了createAndAddWindows
方法。我们看下这种方法的定义
/**
* Create all windows necessary for the status bar (including navigation, overlay panels, etc)
* and add them to the window manager.
*/
protected abstract void createAndAddWindows();
这是一个抽象方法,也就是说,它会回调到子类的createAndAddWindows的实现方法中,我们又一次回到PhoneStatusBar中,找到createAndAddWindows的方法实现
createAndAddWindows
...
@Override
public void createAndAddWindows() {
addStatusBarWindow();
}
private void addStatusBarWindow() {
makeStatusBarView();//创建statusbar视图
mStatusBarWindowManager = new StatusBarWindowManager(mContext);
//通过StatusBarWindowManager类的add方法载入到Window窗口中
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}
...
protected PhoneStatusBarView makeStatusBarView() {
final Context context = mContext;
//通过Resources更新显示大小和一些资源文件
Resources res = context.getResources();
updateDisplaySize(); // populates mDisplayMetrics
updateResources();
//载入StartBarWindowView视图
mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
R.layout.super_status_bar, null);
mStatusBarWindow.setService(this);
//监听下拉事件
mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
checkUserAutohide(v, event);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (mExpandedVisible) {
animateCollapsePanels();
}
}
return mStatusBarWindow.onTouchEvent(event);
}
});
//状态栏
mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
mStatusBarView.setBar(this);
//
PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
mStatusBarView.setPanelHolder(holder);
//通知栏
mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
R.id.notification_panel);
mNotificationPanel.setStatusBar(this);
// M: setBackground in 512 low ram device
if (!ActivityManager.isHighEndGfx() && !FeatureOptions.LOW_RAM_SUPPORT) {
mStatusBarWindow.setBackground(null);
mNotificationPanel.setBackground(new FastColorDrawable(context.getColor(
R.color.notification_panel_solid_background)));
}
//状态栏通知
mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow);
mHeadsUpManager.setBar(this);
mHeadsUpManager.addListener(this);
mHeadsUpManager.addListener(mNotificationPanel);
mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
mNotificationData.setHeadsUpManager(mHeadsUpManager);
if (MULTIUSER_DEBUG) {
mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(
R.id.header_debug_info);
mNotificationPanelDebugText.setVisibility(View.VISIBLE);
}
try {
//是否显示导航栏
boolean showNav = mWindowManagerService.hasNavigationBar();
Log.v(TAG, "hasNavigationBar=" + showNav);
if (showNav) {
/// M: add for multi window @{
//载入导航栏布局
int layoutId = R.layout.navigation_bar;
if(MultiWindowProxy.isSupported()) {
layoutId = R.layout.navigation_bar_float_window;
}
mNavigationBarView = (NavigationBarView) View.inflate(context,
/*R.layout.navigation_bar*/layoutId, null);
/// @}
mNavigationBarView.setDisabledFlags(mDisabled1);
mNavigationBarView.setBar(this);
mNavigationBarView.setOnVerticalChangedListener(
new NavigationBarView.OnVerticalChangedListener() {
@Override
public void onVerticalChanged(boolean isVertical) {
if (mAssistManager != null) {
mAssistManager.onConfigurationChanged();
}
mNotificationPanel.setQsScrimEnabled(!isVertical);
}
});
//设置导航栏触摸事件
mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
checkUserAutohide(v, event);
return false;
}});
}
} catch (RemoteException ex) {
// no window manager? good luck with that
}
mAssistManager = new AssistManager(this, context);
// figure out which pixel-format to use for the status bar.
mPixelFormat = PixelFormat.OPAQUE;
//下拉通知栏
mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
R.id.notification_stack_scroller);
mStackScroller.setLongPressListener(getNotificationLongClicker());
mStackScroller.setPhoneStatusBar(this);
mStackScroller.setGroupManager(mGroupManager);
mStackScroller.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setOnGroupChangeListener(mStackScroller);
mKeyguardIconOverflowContainer =
(NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
mKeyguardIconOverflowContainer.setOnActivatedListener(this);
mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
mStackScroller.setOverflowContainer(mKeyguardIconOverflowContainer);
SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_speed_bump, mStackScroller, false);
mStackScroller.setSpeedBumpView(speedBump);
mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_no_notifications, mStackScroller, false);
mStackScroller.setEmptyShadeView(mEmptyShadeView);
//下拉清除键
mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
mDismissView.setOnButtonClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MetricsLogger.action(mContext, MetricsLogger.ACTION_DISMISS_ALL_NOTES);
clearAllNotifications();
}
});
mStackScroller.setDismissView(mDismissView);
mExpandedContents = mStackScroller;
mBackdrop = (BackDropView) mStatusBarWindow.findViewById(R.id.backdrop);
mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front);
mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back);
ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind);
ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front);
View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim);
mScrimController = new ScrimController(scrimBehind, scrimInFront, headsUpScrim,
mScrimSrcModeEnabled);
mHeadsUpManager.addListener(mScrimController);
mStackScroller.setScrimController(mScrimController);
mScrimController.setBackDropView(mBackdrop);
mStatusBarView.setScrimController(mScrimController);
mDozeScrimController = new DozeScrimController(mScrimController, context);
mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
mHeader.setActivityStarter(this);
//锁屏相关
mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
mKeyguardBottomArea =
(KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
mKeyguardBottomArea.setActivityStarter(this);
mKeyguardBottomArea.setAssistManager(mAssistManager);
mKeyguardIndicationController = new KeyguardIndicationController(mContext,
(KeyguardIndicationTextView) mStatusBarWindow.findViewById(
R.id.keyguard_indication_text),
mKeyguardBottomArea.getLockIcon());
mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController);
// set the inital view visibility
setAreThereNotifications();
//主要是控制一些系统图标。第三方图标等的显示和更新
mIconController = new StatusBarIconController(
mContext, mStatusBarView, mKeyguardStatusBar, this);
// Background thread for any controllers that need it.
mHandlerThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
mHandlerThread.start();
// Other icons
//位置控制
mLocationController = new LocationControllerImpl(mContext,
mHandlerThread.getLooper()); // will post a notification
//电池
mBatteryController = new BatteryController(mContext);
mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() {
@Override
public void onPowerSaveChanged() {
mHandler.post(mCheckBarModes);
if (mDozeServiceHost != null) {
mDozeServiceHost.firePowerSaveChanged(mBatteryController.isPowerSave());
}
}
@Override
public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
// noop
}
});
//网络
mNetworkController = new NetworkControllerImpl(mContext, mHandlerThread.getLooper());
//热点
mHotspotController = new HotspotControllerImpl(mContext);
//蓝牙
mBluetoothController = new BluetoothControllerImpl(mContext, mHandlerThread.getLooper());
mSecurityController = new SecurityControllerImpl(mContext);
/// M: add extra tiles @{
// add HotKnot in quicksetting
if (SIMHelper.isMtkHotKnotSupport()) {
Log.d(TAG, "makeStatusBarView : HotKnotControllerImpl");
mHotKnotController = new HotKnotControllerImpl(mContext);
} else {
mHotKnotController = null;
}
// add AudioProfile in quicksetting
if (SIMHelper.isMtkAudioProfilesSupport()) {
Log.d(TAG, "makeStatusBarView : AudioProfileControllerImpl");
mAudioProfileController = new AudioProfileControllerImpl(mContext);
} else {
mAudioProfileController = null;
}
SIMHelper.setContext(mContext);
// /@}
if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) {
mRotationLockController = new RotationLockControllerImpl(mContext);
}
mUserInfoController = new UserInfoController(mContext);
mVolumeComponent = getComponent(VolumeComponent.class);
if (mVolumeComponent != null) {
mZenModeController = mVolumeComponent.getZenController();
}
Log.d(TAG, "makeStatusBarView : CastControllerImpl +");
mCastController = new CastControllerImpl(mContext);
Log.d(TAG, "makeStatusBarView : CastControllerImpl -");
final SignalClusterView signalCluster =
(SignalClusterView) mStatusBarView.findViewById(R.id.signal_cluster);
final SignalClusterView signalClusterKeyguard =
(SignalClusterView) mKeyguardStatusBar.findViewById(R.id.signal_cluster);
final SignalClusterView signalClusterQs =
(SignalClusterView) mHeader.findViewById(R.id.signal_cluster);
mNetworkController.addSignalCallback(signalCluster);
mNetworkController.addSignalCallback(signalClusterKeyguard);
mNetworkController.addSignalCallback(signalClusterQs);
signalCluster.setSecurityController(mSecurityController);
signalCluster.setNetworkController(mNetworkController);
signalClusterKeyguard.setSecurityController(mSecurityController);
signalClusterKeyguard.setNetworkController(mNetworkController);
signalClusterQs.setSecurityController(mSecurityController);
signalClusterQs.setNetworkController(mNetworkController);
final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();
if (isAPhone) {
mNetworkController.addEmergencyListener(mHeader);
}
/// M: Support "Operator plugin - Customize Carrier Label for PLMN" @{
mStatusBarPlmnPlugin = PluginFactory.getStatusBarPlmnPlugin(context);
if (supportCustomizeCarrierLabel()) {
mCustomizeCarrierLabel = mStatusBarPlmnPlugin.customizeCarrierLabel(
mNotificationPanel, null);
}
/// M: Support "Operator plugin - Customize Carrier Label for PLMN" @}
//手电筒
mFlashlightController = new FlashlightController(mContext);
//键盘
mKeyguardBottomArea.setFlashlightController(mFlashlightController);
mKeyguardBottomArea.setPhoneStatusBar(this);
mKeyguardBottomArea.setUserSetupComplete(mUserSetup);
mAccessibilityController = new AccessibilityController(mContext);
mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);
mNextAlarmController = new NextAlarmController(mContext);
mKeyguardMonitor = new KeyguardMonitor(mContext);
if (UserSwitcherController.isUserSwitcherAvailable(UserManager.get(mContext))) {
mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor,
mHandler);
}
mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
(ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
mKeyguardStatusBar, mNotificationPanel, mUserSwitcherController);
// Set up the quick settings tile panel
mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
if (mQSPanel != null) {
final QSTileHost qsh = new QSTileHost(mContext, this,
mBluetoothController, mLocationController, mRotationLockController,
mNetworkController, mZenModeController, mHotspotController,
mCastController, mFlashlightController,
mUserSwitcherController, mKeyguardMonitor,
mSecurityController,
/// M: add HotKnot in quicksetting
mHotKnotController,
/// M: add AudioProfile in quicksetting
mAudioProfileController
);
mQSPanel.setHost(qsh);
mQSPanel.setTiles(qsh.getTiles());
mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);
mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
mHeader.setQSPanel(mQSPanel);
qsh.setCallback(new QSTileHost.Callback() {
@Override
public void onTilesChanged() {
mQSPanel.setTiles(qsh.getTiles());
}
});
}
// User info. Trigger first load.
mHeader.setUserInfoController(mUserInfoController);
mKeyguardStatusBar.setUserInfoController(mUserInfoController);
mKeyguardStatusBar.setUserSwitcherController(mUserSwitcherController);
mUserInfoController.reloadUserInfo();
mHeader.setBatteryController(mBatteryController);
((BatteryMeterView) mStatusBarView.findViewById(R.id.battery)).setBatteryController(
mBatteryController);
mKeyguardStatusBar.setBatteryController(mBatteryController);
mHeader.setNextAlarmController(mNextAlarmController);
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mBroadcastReceiver.onReceive(mContext,
new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
// receive broadcasts
//注冊系统广播
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
IntentFilter demoFilter = new IntentFilter();
if (DEBUG_MEDIA_FAKE_ARTWORK) {
demoFilter.addAction(ACTION_FAKE_ARTWORK);
}
demoFilter.addAction(ACTION_DEMO);
context.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter,
android.Manifest.permission.DUMP, null);
// listen for USER_SETUP_COMPLETE setting (per-user)
resetUserSetupObserver();
// disable profiling bars, since they overlap and clutter the output on app windows
ThreadedRenderer.overrideProperty("disableProfileBars", "true");
// Private API call to make the shadows look better for Recents
ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
mStatusBarPlmnPlugin.addPlmn((LinearLayout)mStatusBarView.
findViewById(R.id.status_bar_contents), mContext);
return mStatusBarView;
}
...
由于这块涉及的太广。所以接下来仅仅分析StatusBar相关的一块,以导航栏为例进行解说,我们又一次回到PhoneStatusBar的start方法中,找到导航栏这块。发现它是调用addNavigationBar
函数,所以我们查看这个函数:
...
// For small-screen devices (read: phones) that lack hardware navigation buttons
private void addNavigationBar() {
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
//1.推断mNavigationBarView是否为空,这个视图有上面分析的makeStatusBarView方法中进行创建
if (mNavigationBarView == null) return;
//2.载入导航栏的详细显示(导航栏的显示由横向显示和竖向显示,后面分析)
prepareNavigationBarView();
//3.依据LayoutParams,载入导航栏到窗口中
mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
}
...
private void prepareNavigationBarView() {
mNavigationBarView.reorient();
//设置导航栏三个图标的点击事件
mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
mNavigationBarView.getRecentsButton().setLongClickable(true);
mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);
mNavigationBarView.getBackButton().setLongClickable(true);
mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
mNavigationBarView.getHomeButton().setOnLongClickListener(mLongPressHomeListener);
mAssistManager.onConfigurationChanged();
/// M: add for multi window @{
if(MultiWindowProxy.isSupported()){
mNavigationBarView.getFloatButton().setOnClickListener(mFloatClickListener);
if(mIsSplitModeEnable){
mNavigationBarView.getFloatModeButton().setOnClickListener(mFloatModeClickListener);
mNavigationBarView.getSplitModeButton().setOnClickListener(mSplitModeClickListener);
}
MultiWindowProxy.getInstance().setSystemUiCallback(new MWSystemUiCallback());
}
/// @}
}
我们依据上面的凝视来进行分析,主要内容有
1. 导航栏布局的创建
2. 导航栏布局分析及载入
3. 导航栏LayoutParams分析
导航栏布局的创建
在PhoneStatusBar的makeStatusBarView方法中,我们能够看到导航栏是怎么创建的
...
protected PhoneStatusBarView makeStatusBarView() {
...
...
try {
//是否显示导航栏
boolean showNav = mWindowManagerService.hasNavigationBar();
Log.v(TAG, "hasNavigationBar=" + showNav);
if (showNav) {
/// M: add for multi window @{
//载入导航栏布局
int layoutId = R.layout.navigation_bar;
//是否支持多窗口
if(MultiWindowProxy.isSupported()) {
layoutId = R.layout.navigation_bar_float_window;
}
mNavigationBarView = (NavigationBarView) View.inflate(context,
/*R.layout.navigation_bar*/layoutId, null);
/// @}
mNavigationBarView.setDisabledFlags(mDisabled1);
mNavigationBarView.setBar(this);
mNavigationBarView.setOnVerticalChangedListener(
new NavigationBarView.OnVerticalChangedListener() {
@Override
public void onVerticalChanged(boolean isVertical) {
if (mAssistManager != null) {
mAssistManager.onConfigurationChanged();
}
mNotificationPanel.setQsScrimEnabled(!isVertical);
}
});
//设置导航栏触摸事件
mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
checkUserAutohide(v, event);
return false;
}});
}
} catch (RemoteException ex) {
// no window manager?
good luck with that
}
...
...
}
...
首先由mWindowManagerService的hasNavigationBar来决定是否显示导航栏,同一时候通过载入navigation_bar(多窗口载入navigation_bar_float_window)布局来显示导航栏,我们来查看hasNavigationBar方法,由于mWidnwoManagerService是IWindowManagerService由PhoneWindowManager进行调用:
frameworks\base\service\core\java\com\android\server\PhoneWindowManager.java
PhoneWindowManager
...
// Use this instead of checking config_showNavigationBar so that it can be consistently
// overridden by qemu.hw.mainkeys in the emulator.
@Override
public boolean hasNavigationBar() {
return mHasNavigationBar;
}
...
mHasNavigationBar = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);
// Allow a system property to override this. Used by the emulator.
// See also hasNavigationBar().
String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
if ("1".equals(navBarOverride)) {
mHasNavigationBar = false;
} else if ("0".equals(navBarOverride)) {
mHasNavigationBar = true;
}
...
从framework\base\core\res\res\valuse\config.xml中获取mHashNavigationBar的值
<!-- Whether a software navigation bar should be shown. NOTE: in the future this may be
autodetected from the Configuration. -->
<bool name="config_showNavigationBar">ture</bool>
然后从系统配置位置中取qemu.hw.mainkeys的值,所以这里给我们提供了一个隐藏状态栏的新思路,除了在createAndAddWindows中凝视掉addNavigationBar函数外,我们也能够通过改动framework下的config.xml的config_showNavigationBar的值和改动系统配置文件的值来达到隐藏状态栏的目的
导航栏布局分析及载入
...
private void prepareNavigationBarView() {
mNavigationBarView.reorient();
//设置导航栏三个图标的点击事件
mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
mNavigationBarView.getRecentsButton().setLongClickable(true);
mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);
mNavigationBarView.getBackButton().setLongClickable(true);
mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
mNavigationBarView.getHomeButton().setOnLongClickListener(mLongPressHomeListener);
mAssistManager.onConfigurationChanged();
/// M: add for multi window @{
if(MultiWindowProxy.isSupported()){
mNavigationBarView.getFloatButton().setOnClickListener(mFloatClickListener);
if(mIsSplitModeEnable){
mNavigationBarView.getFloatModeButton().setOnClickListener(mFloatModeClickListener);
mNavigationBarView.getSplitModeButton().setOnClickListener(mSplitModeClickListener);
}
MultiWindowProxy.getInstance().setSystemUiCallback(new MWSystemUiCallback());
}
/// @}
}
...
导航栏布局的确切显示在prepareNavigationBarView中的mNavigationBarView.reorient();来决定。我们查看reorient方法
reorient
...
public void reorient() {
//获取屏幕方向
final int rot = mDisplay.getRotation();
//隐藏导航栏布局
for (int i=0; i<4; i++) {
mRotatedViews[i].setVisibility(View.GONE);
}
//依据屏幕方向显示导航栏布局
mCurrentView = mRotatedViews[rot];
mCurrentView.setVisibility(View.VISIBLE);
setLayoutTransitionsEnabled(mLayoutTransitionsEnabled);
getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone);
// force the low profile & disabled states into compliance
mBarTransitions.init();
setDisabledFlags(mDisabledFlags, true /* force */);
setMenuVisibility(mShowMenu, true /* force */);
if (DEBUG) {
Log.d(TAG, "reorient(): rot=" + mDisplay.getRotation());
}
updateTaskSwitchHelper();
setNavigationIconHints(mNavigationIconHints, true);
}
...
导航栏的显示由屏幕的方向来决定,而导航栏有两种不同的显示方式,横向显示和竖向显示,我们能够从mRotatedViews进行追查到
...
View[] mRotatedViews = new View[4];
...
@Override
public void onFinishInflate() {
//屏幕方位0和180方向显示的导航栏为rot0
mRotatedViews[Surface.ROTATION_0] =
mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
//屏幕訪问90和270显示的导航栏为rot90
mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);
mRotatedViews[Surface.ROTATION_270] = mRotatedViews[Surface.ROTATION_90];
//mCurrentView = mRotatedViews[Surface.ROTATION_0];
mCurrentView = mRotatedViews[Surface.ROTATION_90];
getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
updateRTLOrder();
}
...
布局载入完毕后,会回调onFinishInflate方法,在这方法中对屏幕的几个方向初始化4个导航栏view,当中0和180为横向布局,90和270为纵向布局。我们能够从导航栏(NavigationBarView)布局文件里能够看出
res\layout\navigation_bar.xml和res\layout\navigation_bar
<com.android.systemui.statusbar.phone.NavigationBarView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:background="@drawable/system_bar_background"
>
<!--横向导航栏-->
<FrameLayout android:id="@+id/rot0"
android:layout_height="match_parent"
android:layout_width="match_parent"
>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="horizontal"
android:clipChildren="false"
android:clipToPadding="false"
android:id="@+id/nav_buttons"
android:animateLayoutChanges="true"
>
<!-- navigation controls -->
<View
android:layout_width="@dimen/navigation_side_padding"
android:layout_height="match_parent"
android:layout_weight="0"
android:visibility="invisible"
/>
<!--back按钮-->
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
android:layout_width="@dimen/navigation_key_width"
android:layout_height="match_parent"
android:src="@drawable/ic_sysbar_back"
systemui:keyCode="4"
android:layout_weight="0"
android:scaleType="center"
android:contentDescription="@string/accessibility_back"
/>
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:visibility="invisible"
/>
<!--home按钮-->
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
android:layout_width="@dimen/navigation_key_width"
android:layout_height="match_parent"
android:src="@drawable/ic_sysbar_home"
systemui:keyCode="3"
systemui:keyRepeat="false"
android:layout_weight="0"
android:scaleType="center"
android:contentDescription="@string/accessibility_home"
/>
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:visibility="invisible"
/>
<!--recent按钮-->
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
android:layout_width="@dimen/navigation_key_width"
android:layout_height="match_parent"
android:src="@drawable/ic_sysbar_recent"
android:layout_weight="0"
android:scaleType="center"
android:contentDescription="@string/accessibility_recent"
/>
<FrameLayout
android:layout_width="@dimen/navigation_side_padding"
android:layout_height="match_parent"
android:layout_weight="0" >
<com.android.systemui.statusbar.policy.KeyButtonView
android:id="@+id/menu"
android:layout_width="@dimen/navigation_extra_key_width"
android:layout_height="match_parent"
android:contentDescription="@string/accessibility_menu"
android:src="@drawable/ic_sysbar_menu"
android:visibility="invisible"
android:scaleType="centerInside"
android:layout_gravity="end"
systemui:keyCode="82" />
<com.android.systemui.statusbar.policy.KeyButtonView
android:id="@+id/ime_switcher"
android:layout_width="@dimen/navigation_extra_key_width"
android:layout_height="match_parent"
android:contentDescription="@string/accessibility_ime_switch_button"
android:scaleType="centerInside"
android:src="@drawable/ic_ime_switcher_default"
android:visibility="invisible"
android:layout_gravity="end" />
</FrameLayout>
</LinearLayout>
<!-- lights out layout to match exactly -->
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="horizontal"
android:id="@+id/lights_out"
android:visibility="gone"
>
<ImageView
android:layout_width="@dimen/navigation_key_width"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/navigation_side_padding"
android:src="@drawable/ic_sysbar_lights_out_dot_small"
android:scaleType="center"
android:layout_weight="0"
android:contentDescription="@string/accessibility_back"
/>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:visibility="invisible"
/>
<ImageView
android:layout_width="@dimen/navigation_key_width"
android:layout_height="match_parent"
android:src="@drawable/ic_sysbar_lights_out_dot_large"
android:scaleType="center"
android:layout_weight="0"
android:contentDescription="@string/accessibility_home"
/>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:visibility="invisible"
/>
<ImageView
android:layout_width="@dimen/navigation_key_width"
android:layout_marginEnd="@dimen/navigation_side_padding"
android:layout_height="match_parent"
android:src="@drawable/ic_sysbar_lights_out_dot_small"
android:scaleType="center"
android:layout_weight="0"
android:contentDescription="@string/accessibility_recent"
/>
</LinearLayout>
<com.android.systemui.statusbar.policy.DeadZone
android:id="@+id/deadzone"
android:layout_height="match_parent"
android:layout_width="match_parent"
systemui:minSize="@dimen/navigation_bar_deadzone_size"
systemui:maxSize="@dimen/navigation_bar_deadzone_size_max"
systemui:holdTime="@integer/navigation_bar_deadzone_hold"
systemui:decayTime="@integer/navigation_bar_deadzone_decay"
systemui:orientation="horizontal"
android:layout_gravity="top"
/>
</FrameLayout>
<!--纵向显示-->
<FrameLayout android:id="@+id/rot90"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:visibility="gone"
android:paddingTop="0dp"
>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
android:clipChildren="false"
android:clipToPadding="false"
android:id="@+id/nav_buttons"
android:animateLayoutChanges="true"
>
<!-- navigation controls -->
<FrameLayout
android:layout_weight="0"
android:layout_width="match_parent"
android:layout_height="@dimen/navigation_side_padding" >
<com.android.systemui.statusbar.policy.KeyButtonView
android:id="@+id/ime_switcher"
android:layout_width="match_parent"
android:layout_height="@dimen/navigation_extra_key_width"
android:contentDescription="@string/accessibility_ime_switch_button"
android:scaleType="centerInside"
android:src="@drawable/ic_ime_switcher_default"
android:layout_gravity="top"
android:visibility="invisible" />
<com.android.systemui.statusbar.policy.KeyButtonView
android:id="@+id/menu"
android:layout_width="match_parent"
android:layout_height="40dp"
android:contentDescription="@string/accessibility_menu"
android:src="@drawable/ic_sysbar_menu_land"
android:scaleType="centerInside"
android:layout_gravity="top"
android:visibility="invisible"
systemui:keyCode="82" />
</FrameLayout>
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
android:layout_height="@dimen/navigation_key_width"
android:layout_width="match_parent"
android:src="@drawable/ic_sysbar_recent_land"
android:scaleType="center"
android:layout_weight="0"
android:contentDescription="@string/accessibility_recent"
/>
<View
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_weight="1"
android:visibility="invisible"
/>
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
android:layout_height="@dimen/navigation_key_width"
android:layout_width="match_parent"
android:src="@drawable/ic_sysbar_home_land"
android:scaleType="center"
systemui:keyCode="3"
systemui:keyRepeat="false"
android:layout_weight="0"
android:contentDescription="@string/accessibility_home"
/>
<View
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_weight="1"
android:visibility="invisible"
/>
<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
android:layout_height="@dimen/navigation_key_width"
android:layout_width="match_parent"
android:src="@drawable/ic_sysbar_back_land"
android:scaleType="center"
systemui:keyCode="4"
android:layout_weight="0"
android:contentDescription="@string/accessibility_back"
/>
<View
android:layout_height="@dimen/navigation_side_padding"
android:layout_width="match_parent"
android:layout_weight="0"
android:visibility="invisible"
/>
</LinearLayout>
<!-- lights out layout to match exactly -->
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
android:id="@+id/lights_out"
android:visibility="gone"
>
<ImageView
android:layout_height="@dimen/navigation_key_width"
android:layout_marginTop="@dimen/navigation_side_padding"
android:layout_width="match_parent"
android:src="@drawable/ic_sysbar_lights_out_dot_small"
android:scaleType="center"
android:layout_weight="0"
android:contentDescription="@string/accessibility_recent"
/>
<View
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_weight="1"
android:visibility="invisible"
/>
<ImageView
android:layout_height="@dimen/navigation_key_width"
android:layout_width="match_parent"
android:src="@drawable/ic_sysbar_lights_out_dot_large"
android:scaleType="center"
android:layout_weight="0"
android:contentDescription="@string/accessibility_home"
/>
<View
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_weight="1"
android:visibility="invisible"
/>
<ImageView
android:layout_height="@dimen/navigation_key_width"
android:layout_marginBottom="@dimen/navigation_side_padding"
android:layout_width="match_parent"
android:src="@drawable/ic_sysbar_lights_out_dot_small"
android:scaleType="center"
android:layout_weight="0"
android:contentDescription="@string/accessibility_back"
/>
</LinearLayout>
<com.android.systemui.statusbar.policy.DeadZone
android:id="@+id/deadzone"
android:layout_height="match_parent"
android:layout_width="match_parent"
systemui:minSize="@dimen/navigation_bar_deadzone_size"
systemui:maxSize="@dimen/navigation_bar_deadzone_size_max"
systemui:holdTime="@integer/navigation_bar_deadzone_hold"
systemui:decayTime="@integer/navigation_bar_deadzone_decay"
systemui:orientation="vertical"
android:layout_gravity="top"
/>
</FrameLayout>
</com.android.systemui.statusbar.phone.NavigationBarView>
所以说,当我们的需求为0或者90度方向,要想导航栏纵向显示,我们仅仅须要改动成导航栏纵向布局就可以。当然我们也能够按需求来隐藏某些导航栏按键(布局中设置某些控件为gone)
@Override
public void onFinishInflate() {
mRotatedViews[Surface.ROTATION_0] =
mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);
mRotatedViews[Surface.ROTATION_270] = mRotatedViews[Surface.ROTATION_90];
//mCurrentView = mRotatedViews[Surface.ROTATION_0];
//显示纵向的导航栏
mCurrentView = mRotatedViews[Surface.ROTATION_90];
getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
updateRTLOrder();
}
导航栏LayoutParams分析
我们回到PhoneStatusBar的addNavigationBar继续分析最后一个导航栏的LayoutParameters,它决定了导航栏在窗口上的显示位置
private WindowManager.LayoutParams getNavigationBarLayoutParams() {
//充满父布局
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,//导航栏
0
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING//当手机处于睡眠状态时,假设屏幕被按下。那么该window将第一个收到到事件
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE//不获取焦点
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL//即使在该window在可获得焦点情况下。仍然把该window之外的不论什么event发送到该window之后的其它window
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH//不接受事件。转发到其它window
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,//当该window在能够接受触摸屏情况下。让因在该window之外,而发送到后面的window的触摸屏能够支持split touch.
PixelFormat.TRANSLUCENT);
// this will allow the navbar to run in an overlay on devices that support this
if (ActivityManager.isHighEndGfx()) {
lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;//硬件加速
}
//lp.gravity = Gravity.RIGHT;
lp.setTitle("NavigationBar");
lp.windowAnimations = 0;
return lp;
}
上面的LayoutParames决定了导航栏在窗口的大小(受父布局影响)和显示的位置效果。当我们的需求假设要把导航栏显示在屏幕的右边时。我们能够在上面代码中加上以下一句
lp.gravity = Gravity.RIGHT;
SystemUI包括了太多内容,本篇仅仅是分析了SystemUI的载入流程。同一时候初步的分析了StatusBar中一个小模块NavigationBar,兴许会针对SystemUI的其它模块进行分析。
Android之SystemUI载入流程和NavigationBar的分析的更多相关文章
- Android应用层View绘制流程与源码分析
1 背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原 ...
- Cocos2d-x3.3RC0的Android编译Activity启动流程分析
本文将从引擎源代码Jni分析Cocos2d-x3.3RC0的Android Activity的启动流程,以下是具体分析. 1.引擎源代码Jni.部分Java层和C++层代码分析 watermark/2 ...
- 【系统解读】SystemUI篇(一)SystemUI启动流程
前言 SystemUI是系统启动中第一个用户肉眼可见的应用,其功能包罗万象,比如开机后看到的锁屏界面,充电时充电界面,状态栏,导航栏,多任务栏等,都是与Android手机用户息息相关的功能.所以不止S ...
- android异步任务载入数据界面实现
android 异步任务的一个后台方法本质是开启一个线程完毕耗时操作,其它onPostExecute方法和onPreExecute方法执行在UI主线程用于更新UI界面.为了提高用户体验常见的异步任务载 ...
- Android ListView分页载入(服务端+android端)Demo
Android ListView分页载入功能 在实际开发中经经常使用到,是每一个开发人员必须掌握的内容,本Demo给出了服务端+Android端的两者的代码,并成功通过了測试. 服务端使用MyEcli ...
- Android开机动画启动流程
android开机动画启动流程 从android的Surface Flinger服务启动分析知道,开机动画是在SurfaceFlinger实例通过调用startBootAnim()启动的. 下面我 ...
- 浅谈Android的Activity运行流程(生命周期)
关于Android的Activity运行流程,我们可以写一些程序来直观的查看Activity的运行流程.在这里我们使用Log工具来获取Activity运行日志.假如我们新建一个Android项目,Pr ...
- Android应用UI设计流程
Android应用UI设计流程 设计原理 1.在移动设计中,使用环境是最关键的因素.原型设计方法必须考虑尺寸因素 2.用户测试必须涵盖运动.声音和多点触控等方面: 进行移动设计和测试时,请将你知道的有 ...
- Android系统开机启动流程及init进程浅析
Android系统启动概述 Android系统开机流程基于Linux系统,总体可分为三个阶段: Boot Loader引导程序启动Linux内核启动Android系统启动,Launcher/app启动 ...
随机推荐
- RS布局问题之块的不完美之完美
早上一来,便传来喜讯...说我们做的报表太美.客户不敢看----于是便开启征程,亲自尝试了一把,如下面的操作,首次运行报表,在不考虑UI美观度的情况下,报表还是 在预测范围内显示的 那么接下来我们选择 ...
- Hibernate_8_Person和IdCard实例_一对一关系:基于外键
1)建立Person类: public class Person { private Integer id; private String name; private IdCard IdCard; p ...
- 导入exce表格中的数据l到数据库
因为我的项目是JavaWeb的,所有是通过浏览器导入数据库到服务器端的数据库,这里我们采用struts来帮助我们完成. 1:首先定义一个文件上传的jsp页面.把我们的数据先上传到服务器端. <f ...
- Number Sequence_hdu_1005(规律)
9329854 2013-10-13 14:36:41 Accepted 1005 171MS 5072K 654 B Java zhangyi http://acm.hdu.edu.cn/showp ...
- Scala数据类型中的Symbol(符号文本)
1.属于基本类型,被映射成scala.Symbol 2.当两个Symbol值相等时,指向同一个实例 3.Symbol类型存在的意义:作为不可变的字符串,同时不必重复地为相同对象创建实例,节省资源.这类 ...
- ionic build android 失败 及 解决方案
原因:没有接受以下SDK组件的许可协议 解决方案: install Android Support Repository
- ie 已限制此网页运行脚本或Active控件
ie 已限制此网页运行脚本或Active控件 CreateTime--2018年3月12日16:49:43 Author:Marydon 情景还原: 在本地调试html页,如果其中包含js或fla ...
- js判断是否包含指定字符串
CreateTime--2017年2月28日09:37:06Author:Marydonjs判断是否包含指定字符串 var inputValue = "thunder://piaohua ...
- websphere设置jvm参数
http://www.quiee.com.cn/archives/592/ websphere 选择服务器-> 应用程序服务器-> Server1-> 进程定义->Java 虚 ...
- Bulk Load-HBase数据导入最佳实践
一.概述 HBase本身提供了非常多种数据导入的方式,通常有两种经常使用方式: 1.使用HBase提供的TableOutputFormat,原理是通过一个Mapreduce作业将数据导入HBase 2 ...