在前面的Android中getSystemService流程中,我们知道在系统启动的时候,会启动一些系统的必要服务,SystemUI也是在SystemServer#startOtherServices这里启动的
static final void startSystemUi(Context context, WindowManagerService windowManager) {
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);
windowManager.onSystemUiStarted();
}
上面启动了SystemUIService服务,下面看看那SystemUIService服务具体做了什么
public class SystemUIService extends Service {
@Override
public void onCreate() {
super.onCreate();
((SystemUIApplication) getApplication()).startServicesIfNeeded();
}
}
SystemUIApplication#startServicesIfNeeded
public void startServicesIfNeeded() {
startServicesIfNeeded(SERVICES);
}
private void startServicesIfNeeded(Class>[] services) {
....
final int N = services.length;
for (int i = 0; i < N; i++) {
Class> cl = services[i];
if (DEBUG) Log.d(TAG, "loading: " + cl);
log.traceBegin("StartServices" + cl.getSimpleName());
long ti = System.currentTimeMillis();
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(); // 分别调用每个类的start方法
}
}
这里以快捷开关栏SystemBars为例,举例说明:
SystemBars#start
@Override
public void start() {
if (DEBUG) Log.d(TAG, "start");
createStatusBarFromConfig();
}
SystemBars#createStatusBarFromConfig
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 = (SystemUI) 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());
}
上述是"config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar 类
StatusBar#start
@Override
public void start() {
....
createAndAddWindows();
....
}
StatusBar#createAndAddWindows
public void createAndAddWindows() {
addStatusBarWindow();
}
StatusBar#addStatusBarWindow
private void addStatusBarWindow() {
makeStatusBarView();
mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
mRemoteInputController = new RemoteInputController(mHeadsUpManager);
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}
StatusBar#makeStatusBarView
protected void makeStatusBarView() {
....
// Set up the quick settings tile panel
View container = mStatusBarWindow.findViewById(R.id.qs_frame);
if (container != null) {
FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
Dependency.get(ExtensionController.class).newExtension(QS.class)
.withPlugin(QS.class)
.withFeature(
PackageManager.FEATURE_AUTOMOTIVE, () -> new CarQSFragment())
.withDefault(() -> new QSFragment())
.build());
final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
mIconController); // 获取默认显示的快捷按钮,并添加到qsh对象的mTiles变量中
mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow,
mScrimController);
fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
QS qs = (QS) f;
if (qs instanceof QSFragment) {
((QSFragment) qs).setHost(qsh); // 从qsh对象获取mTiles中保存的所有默认显示的快捷按钮并且添加到布局中显示
mQSPanel = ((QSFragment) qs).getQsPanel();
mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
mKeyguardStatusBar.setQSPanel(mQSPanel);
}
});
}
....
}
上述创建final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
mIconController);
SystemUIFactory#createQSTileHost
public QSTileHost createQSTileHost(Context context, StatusBar statusBar,
StatusBarIconController iconController) {
return new QSTileHost(context, statusBar, iconController);
}
public QSTileHost(Context context, StatusBar statusBar,
StatusBarIconController iconController) {
mIconController = iconController;
mContext = context;
mStatusBar = statusBar;
mServices = new TileServices(this, Dependency.get(Dependency.BG_LOOPER));
mQsFactories.add(new QSFactoryImpl(this));
Dependency.get(PluginManager.class).addPluginListener(this, QSFactory.class, true);
Dependency.get(TunerService.class).addTunable(this, TILES_SETTING);
// AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
mAutoTiles = new AutoTileManager(context, this);
}
TunerServiceImpl#addTunable
private void addTunable(Tunable tunable, String key) {
if (!mTunableLookup.containsKey(key)) {
mTunableLookup.put(key, new ArraySet());
}
mTunableLookup.get(key).add(tunable);
if (LeakDetector.ENABLED) {
mTunables.add(tunable);
Dependency.get(LeakDetector.class).trackCollection(mTunables, "TunerService.mTunables");
}
Uri uri = Settings.Secure.getUriFor(key);
if (!mListeningUris.containsKey(uri)) {
mListeningUris.put(uri, key);
mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
}
// Send the first state.
String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
tunable.onTuningChanged(key, value);
}
QSTileHost#onTuningChanged
@Override
public void onTuningChanged(String key, String newValue) {
if (!TILES_SETTING.equals(key)) {
return;
}
if (DEBUG) Log.d(TAG, "Recreating tiles");
if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
}
final List<String> tileSpecs = loadTileSpecs(mContext, newValue); // 获取快捷栏的按钮
int currentUser = ActivityManager.getCurrentUser();
if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
tile -> {
if (DEBUG) Log.d(TAG, "Destroying tile: " + tile.getKey());
tile.getValue().destroy();
});
final LinkedHashMap<String, QSTile> newTiles = new LinkedHashMap<>();
for (String tileSpec : tileSpecs) {
QSTile tile = mTiles.get(tileSpec);
if (tile != null && (!(tile instanceof CustomTile)
|| ((CustomTile) tile).getUser() == currentUser)) {
if (tile.isAvailable()) {
if (DEBUG) Log.d(TAG, "Adding " + tile);
tile.removeCallbacks();
if (!(tile instanceof CustomTile) && mCurrentUser != currentUser) {
tile.userSwitch(currentUser);
}
newTiles.put(tileSpec, tile);
} else {
tile.destroy();
}
} else {
if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);
try {
tile = createTile(tileSpec);
if (tile != null) {
if (tile.isAvailable()) {
tile.setTileSpec(tileSpec);
newTiles.put(tileSpec, tile);
} else {
tile.destroy();
}
}
} catch (Throwable t) {
Log.w(TAG, "Error creating tile for spec: " + tileSpec, t);
}
}
}
mCurrentUser = currentUser;
mTileSpecs.clear();
mTileSpecs.addAll(tileSpecs);
mTiles.clear();
mTiles.putAll(newTiles);
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onTilesChanged();
}
}
TunerServiceImpl#loadTileSpecs
protected List<String> loadTileSpecs(Context context, String tileList) {
final Resources res = context.getResources();
final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);// 获取默认显示的快捷按钮
if (tileList == null) {
tileList = res.getString(R.string.quick_settings_tiles);
if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList);
} else {
if (DEBUG) Log.d(TAG, "Loaded tile specs from setting: " + tileList);
}
final ArrayList<String> tiles = new ArrayList<String>();
boolean addedDefault = false;
for (String tile : tileList.split(",")) {
tile = tile.trim();
if (tile.isEmpty()) continue;
if (tile.equals("default")) {
if (!addedDefault) {
tiles.addAll(Arrays.asList(defaultTileList.split(",")));
addedDefault = true;
}
} else {
tiles.add(tile);
}
}
return tiles;
}
<string name="quick_settings_tiles_default" translatable="false">
wifi,bt,dnd,flashlight,rotation,battery,cell,airplane,cast
string>
这里quick_settings_tiles_default是默认显示的快捷按钮
上述的 onTuningChanged方法,其实主要做了这样的事,获取获取默认显示的快捷按钮(其实就是字符串标识),然后添加到mTiles变量中
下面是从qsh对象获取mTiles中保存的所有默认显示的快捷按钮并且添加到布局中显示
QSFragment#setHost
public void setHost(QSTileHost qsh) {
mQSPanel.setHost(qsh, mQSCustomizer);
mHeader.setQSPanel(mQSPanel);
mFooter.setQSPanel(mQSPanel);
mQSDetail.setHost(qsh);
if (mQSAnimator != null) {
mQSAnimator.setHost(qsh);
}
}
QSPanel#setHost
public void setHost(QSTileHost host, QSCustomizer customizer) {
mHost = host;
mHost.addCallback(this);
setTiles(mHost.getTiles()); // 获取获取默认显示的快捷按钮
mFooter.setHostEnvironment(host);
mCustomizePanel = customizer;
if (mCustomizePanel != null) {
mCustomizePanel.setHost(mHost);
}
}
QSPanel#setTiles
public void setTiles(Collection tiles, boolean collapsedView) {
for (TileRecord record : mRecords) {
mTileLayout.removeTile(record);
record.tile.removeCallback(record.callback);
}
mRecords.clear();
for (QSTile tile : tiles) {
addTile(tile, collapsedView);
}
}
QSPanel#addTile
protected TileRecord addTile(final QSTile tile, boolean collapsedView) {
final TileRecord r = new TileRecord();
r.tile = tile;
r.tileView = createTileView(tile, collapsedView); // 根据读取到的tile创建对应的tile实现
final QSTile.Callback callback = new QSTile.Callback() {
@Override
public void onStateChanged(QSTile.State state) {
drawTile(r, state);
}
@Override
public void onShowDetail(boolean show) {
// Both the collapsed and full QS panels get this callback, this check determines
// which one should handle showing the detail.
if (shouldShowDetail()) {
QSPanel.this.showDetail(show, r);
}
}
@Override
public void onToggleStateChanged(boolean state) {
if (mDetailRecord == r) {
fireToggleStateChanged(state);
}
}
@Override
public void onScanStateChanged(boolean state) {
r.scanState = state;
if (mDetailRecord == r) {
fireScanStateChanged(r.scanState);
}
}
@Override
public void onAnnouncementRequested(CharSequence announcement) {
if (announcement != null) {
mHandler.obtainMessage(H.ANNOUNCE_FOR_ACCESSIBILITY, announcement)
.sendToTarget();
}
}
};
r.tile.addCallback(callback);
r.callback = callback;
r.tileView.init(r.tile);
r.tile.refreshState();
mRecords.add(r);
if (mTileLayout != null) {
mTileLayout.addTile(r);
}
return r;
}
上述 QSPanel#addTile方法中,首先会创建一个TileRecord,TileRecord就是对定义的tile字符串和对应的tile实现类一一对应起来,另外,上面将所有的快捷开关,其实最终是通过addView的形式保存到mTileLayout中的,mTileLayout是一个布局,在QSPanel中初始化的,其实,systemui中,我们能看到的所有下拉界面的显示,都是添加到mTileLayout中的
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
setOrientation(VERTICAL);
mBrightnessView = LayoutInflater.from(context).inflate(
R.layout.quick_settings_brightness_dialog, this, false);
addView(mBrightnessView); // 调节亮度的
setupTileLayout(); // 加载mTileLayout对应的布局
mPageIndicator = LayoutInflater.from(context).inflate(
R.layout.mTileLayoutqs_page_indicator, this, false);
addView(mPageIndicator);
if (mTileLayout instanceof PagedTileLayout) {
((PagedTileLayout) mTileLayout).setPageIndicator((PageIndicator) mPageIndicator);
} // 快捷栏一页显示不下,这里是添加滑动显示的标识圆点
addDivider(); // 分割线
mFooter = new QSSecurityFooter(this, context);
addView(mFooter.getView()); // 底部编辑
updateResources();
mBrightnessController = new BrightnessController(getContext(),
findViewById(R.id.brightness_icon),
findViewById(R.id.brightness_slider));
}
另外,上面将所有的快捷开关,其实最终是通过addView的形式保存到mTileLayout中的,mTileLayout是一个布局,在QSPanel中初始化的,其实,systemui中,我们能看到的
所有下拉界面的显示,都是添加到mTileLayout中的
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
setOrientation(VERTICAL);
mBrightnessView = LayoutInflater.from(context).inflate(
R.layout.quick_settings_brightness_dialog, this, false);
addView(mBrightnessView); // 调节亮度的
setupTileLayout(); // 加载mTileLayout对应的布局
mPageIndicator = LayoutInflater.from(context).inflate(
R.layout.mTileLayoutqs_page_indicator, this, false);
addView(mPageIndicator);
if (mTileLayout instanceof PagedTileLayout) {
((PagedTileLayout) mTileLayout).setPageIndicator((PageIndicator) mPageIndicator);
} // 快捷栏一页显示不下,这里是添加滑动显示的标识圆点
addDivider(); // 分割线
mFooter = new QSSecurityFooter(this, context);
addView(mFooter.getView()); // 底部编辑
updateResources();
mBrightnessController = new BrightnessController(getContext(),
findViewById(R.id.brightness_icon),
findViewById(R.id.brightness_slider));
}
通过setupTileLayout方法加载mTileLayout对应的布局
再回到PagedTileLayout#addTile添加快捷设置按钮
@Override
public void addTile(TileRecord tile) {
mTiles.add(tile); // 将所有的默认显示快捷设置保存到PagedTileLayout私有变量mTiles
postDistributeTiles(); //postDistributeTiles方法会开启一个线程,执行distributeTiles方法
}
PagedTileLayout#distributeTiles
private void distributeTiles() {
if (DEBUG) Log.d(TAG, "Distributing tiles");
final int NP = mPages.size();
for (int i = 0; i < NP; i++) {
mPages.get(i).removeAllViews();
}
int index = 0;
final int NT = mTiles.size();
for (int i = 0; i < NT; i++) {
TileRecord tile = mTiles.get(i);
if (mPages.get(index).isFull()) { //一页显示不下
if (++index == mPages.size()) {
if (DEBUG) Log.d(TAG, "Adding page for "
+ tile.tile.getClass().getSimpleName());
mPages.add((TilePage) LayoutInflater.from(getContext())
.inflate(R.layout.qs_paged_page, this, false));
}
}
if (DEBUG) Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
+ index);
mPages.get(index).addTile(tile); // 添加当前tile到布局中,mPages.get(index)获取TilePage extends TileLayout
}
if (mNumPages != index + 1) {
mNumPages = index + 1;
while (mPages.size() > mNumPages) {
mPages.remove(mPages.size() - 1);
}
if (DEBUG) Log.d(TAG, "Size: " + mNumPages);
mPageIndicator.setNumPages(mNumPages);
mPageIndicator.setVisibility(mNumPages > 1 ? View.VISIBLE : View.GONE);
setAdapter(mAdapter);
mAdapter.notifyDataSetChanged();
setCurrentItem(0, false);
}
}
TileLayout#addView
public void addTile(TileRecord tile) {
mRecords.add(tile);
tile.tile.setListening(this, mListening);
addView(tile.tileView);
}
TileLayout继承自ViewGroup,所以到这里addView就把对应的tile添加到布局中显示了
好了,有了这里的流程分析,后面,我会实现一个在systemui的快捷设置栏中添加一个锁屏的快捷菜单,拭目以待^_^