SystemUI流程分析

在前面的Android中getSystemService流程中,我们知道在系统启动的时候,会启动一些系统的必要服务,SystemUI也是在SystemServer#startOtherServices这里启动的

SystemUI启动时机

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());
}
上述是<string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>类

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<Tunable>());
        }
        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;
    }
<!-- The default tiles to display in QuickSettings -->
<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<QSTile> 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的快捷设置栏中添加一个锁屏的快捷菜单,拭目以待^_^

猜你喜欢

转载自blog.csdn.net/mockingbirds/article/details/79336960