欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Android SystemUI系统介绍

程序员文章站 2022-06-09 21:25:46
1. SystemUI 系统框架1.1 启动流程在 SystemServer 启动时: startBootstrapServices(); startCoreServices(); startOtherServices();SystemUI 在 startOtherServices 中启动:先启动与 SystemUI 的服务statusBar = new StatusBarManagerService(context, wm);ServiceManager.ad...

1. SystemUI 系统框架

1.1 启动流程

在 SystemServer 启动时:
     startBootstrapServices();
     startCoreServices();
     startOtherServices();
SystemUI 在 startOtherServices 中启动:
先启动与 SystemUI 的服务
	statusBar = new StatusBarManagerService(context, wm);
	ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
再启动 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();
    }

在 SystemUI 模块中
启动组件如下:

    <string-array name="config_systemUIServiceComponents" translatable="false">
        <item>com.android.systemui.Dependency</item>
        <item>com.android.systemui.util.NotificationChannels</item>
        <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
        <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
        <item>com.android.systemui.recents.Recents</item>
        <item>com.android.systemui.volume.VolumeUI</item>
        <item>com.android.systemui.stackdivider.Divider</item>
        <item>com.android.systemui.SystemBars</item>
        <item>com.android.systemui.usb.StorageNotification</item>
        <item>com.android.systemui.power.PowerUI</item>
        <item>com.android.systemui.media.RingtonePlayer</item>
        <item>com.android.systemui.keyboard.KeyboardUI</item>
        <item>com.android.systemui.pip.PipUI</item>
        <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
        <item>@string/config_systemUIVendorServiceComponent</item>
        <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
        <item>com.android.systemui.LatencyTester</item>
        <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
        <item>com.android.systemui.ScreenDecorations</item>
        <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
        <item>com.android.systemui.SliceBroadcastRelayHandler</item>
    </string-array>
   为单个用户启动的组件 (是全部组件的子类)
    <string-array name="config_systemUIServiceComponentsPerUser" translatable="false">
        <item>com.android.systemui.Dependency</item>
        <item>com.android.systemui.util.NotificationChannels</item>
        <item>com.android.systemui.recents.Recents</item>
    </string-array> 

所有组件都是 SystemUI 的子类, 通过列表来初始化启动组件

			String clsName = services[i];
            Class cls;
            try {
                cls = Class.forName(clsName);
                mServices[i] = (SystemUI) cls.newInstance();
            } catch(ClassNotFoundException ex){
                throw new RuntimeException(ex);
            } 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();

自此, SystemUI 完在启动和各个模块的初始化动作.

2.系统模块分析

2.1 通知

通知系统涉及内容较多, framework 部分单独成文介绍, 这里仅介绍 SystemUI部分.

通知注册监听:

创建一个 NotificationListenerService 子类, 主要用于 NotificationListenerService 的具体实现
public class NotificationListenerWithPlugins extends NotificationListenerService

继承自 NotificationListenerWithPlugins, 主要用于通知的分发
public class NotificationListener extends NotificationListenerWithPlugins

将通和相关的组件注册到 Dependency 组件中.
providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context));
providers.put(NotificationBlockingHelperManager.class,
        () -> new NotificationBlockingHelperManager(context));
providers.put(NotificationRemoteInputManager.class,
        () -> new NotificationRemoteInputManager(context));
providers.put(SmartReplyConstants.class,
        () -> new SmartReplyConstants(Dependency.get(Dependency.MAIN_HANDLER), context));
providers.put(NotificationListener.class, () -> new NotificationListener(context));

将通知监听注册为系统服务

	mNotificationListener =  Dependency.get(NotificationListener.class);
	mNotificationListener.setUpWithPresenter(this, mEntryManager);

到这里, 通知的注册监听就完在了.

那么当一条通知来的时候, SystemUI 是如何将它显示在界面上呢?
从前面的监听服务可以知道, 当一个通知将要显示出来, 源头便是监听服务

通过 onNotificationPosted 接口监听来到的通知

if (isUpdate) {
    mEntryManager.updateNotification(sbn, rankingMap);// 已经在显地, 只需要更新一下
} else {
    mEntryManager.addNotification(sbn, rankingMap); // 新增通知
}

以新增通知为例来看通知流程

1. 排序
	mNotificationData.updateRanking(ranking);
	在通知排序前需要对通知进行过滤 shouldFilterOut(Entry entry), 过滤规则来自于 StatusBar 实现的 Environment 接口
	排序规则:
	    悬浮通知 > 媒体通知 > Rank排序 > 发送设置时间.
	    
	    private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {
        private final Ranking mRankingA = new Ranking();
        private final Ranking mRankingB = new Ranking();

        @Override
        public int compare(Entry a, Entry b) {
            final StatusBarNotification na = a.notification;
            final StatusBarNotification nb = b.notification;
            int aImportance = NotificationManager.IMPORTANCE_DEFAULT;
            int bImportance = NotificationManager.IMPORTANCE_DEFAULT;
            int aRank = 0;
            int bRank = 0;

            if (mRankingMap != null) {
                // RankingMap as received from NoMan
                getRanking(a.key, mRankingA);
                getRanking(b.key, mRankingB);
                aImportance = mRankingA.getImportance();
                bImportance = mRankingB.getImportance();
                aRank = mRankingA.getRank();
                bRank = mRankingB.getRank();
            }

            String mediaNotification = mEnvironment.getCurrentMediaNotificationKey();

            // IMPORTANCE_MIN media streams are allowed to drift to the bottom
            final boolean aMedia = a.key.equals(mediaNotification)
                    && aImportance > NotificationManager.IMPORTANCE_MIN;
            final boolean bMedia = b.key.equals(mediaNotification)
                    && bImportance > NotificationManager.IMPORTANCE_MIN;

            boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH &&
                    isSystemNotification(na);
            boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH &&
                    isSystemNotification(nb);

            boolean isHeadsUp = a.row.isHeadsUp();
            if (isHeadsUp != b.row.isHeadsUp()) {
                return isHeadsUp ? -1 : 1;
            } else if (isHeadsUp) {
                // Provide consistent ranking with headsUpManager
                return mHeadsUpManager.compare(a, b);
            } else if (aMedia != bMedia) {
                // Upsort current media notification.
                return aMedia ? -1 : 1;
            } else if (aSystemMax != bSystemMax) {
                // Upsort PRIORITY_MAX system notifications
                return aSystemMax ? -1 : 1;
            } else if (aRank != bRank) {
                return aRank - bRank;
            } else {
                return Long.compare(nb.getNotification().when, na.getNotification().when);
            }
        }
    };
2. 创建通知视图
   createNotificationViews(StatusBarNotification sbn)
   初始化加载视图:
   new RowInflaterTask().inflate(mContext, parent, entry,
        row -> {
            bindRow(entry, pmUser, sbn, row);
            updateNotification(entry, pmUser, sbn, row);
        });  
  ExpandableNotificationRow 负责视图显示逻辑           

2.2 近期任务

入口:

    private void onRecentsClick(View v) {
        if (LatencyTracker.isEnabled(getContext())) {
            LatencyTracker.getInstance(getContext()).onActionStart(
                    LatencyTracker.ACTION_TOGGLE_RECENTS);
        }
        mStatusBar.awakenDreams();
        mCommandQueue.toggleRecentApps();
    }

获取系统有没有其它 Recents 支持

外部 Recents
        IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
        mOverviewProxyService.getProxy().onOverviewToggle();
内部Recents
            mImpl.toggleRecents(growTarget);        

Recents 本身是一个独立的 Activity, 与 SystemUI 本身关联性并不是很大, 这里以 SystemUI 内部的 recents 为例进行分析.
从SystemUI中启动 Recents

//toggleRecents
    protected void startRecentsActivityAndDismissKeyguardIfNeeded(
            final ActivityManager.RunningTaskInfo runningTask, final boolean isHomeStackVisible,
            final boolean animate, final int growTarget) {
        // Preload only if device for current user is unlocked
        final StatusBar statusBar = getStatusBar();
        if (statusBar != null && statusBar.isKeyguardShowing()) {
            statusBar.executeRunnableDismissingKeyguard(() -> {
                    // Flush trustmanager before checking device locked per user when preloading
                    mTrustManager.reportKeyguardShowingChanged();
                    mHandler.post(() -> startRecentsActivity(runningTask, isHomeStackVisible,
                            animate, growTarget));
                }, null,  true /* dismissShade */, false /* afterKeyguardGone */,
                true /* deferred */);
        } else {
            startRecentsActivity(runningTask, isHomeStackVisible, animate, growTarget);
        }
    }

加载近期作务

sInstanceLoadPlan = new RecentsTaskLoadPlan(mContext);

mRawTasks = ActivityManagerWrapper.getInstance().getRecentTasks(
        ActivityManager.getMaxRecentTasksStatic(), currentUserId);
获取近期任务图标
图片有缓存, 直接加载图片,图片没有缓存, 重新获取
Recents图片缓存路径:
/data/system_ce/0/snapshots        
没有图片缓存
 ThumbnailData thumbnailData = ActivityManagerWrapper.getInstance().getTaskThumbnail(
         taskKey.id, true /* reducedResolution */);

getTaskThumbnail 流程

WindowManagerService.java
public TaskSnapshot getTaskSnapshot(int taskId, int userId, boolean reducedResolution) {
    return mTaskSnapshotController.getSnapshot(taskId, userId, true /* restoreFromDisk */,
            reducedResolution);
    }

TaskSnapShotCache.java    
@Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
        boolean reducedResolution) {
    return mCache.getSnapshot(taskId, userId, restoreFromDisk, reducedResolution
            || DISABLE_FULL_SIZED_BITMAPS);
    }

获取并缓存图片
    private TaskSnapshot tryRestoreFromDisk(int taskId, int userId, boolean reducedResolution) {
        final TaskSnapshot snapshot = mLoader.loadTask(taskId, userId, reducedResolution);
        if (snapshot == null) {
            return null;
        }
        return snapshot;
    }
    

2.3 状态栏

状态栏显示, 在 SystemUI中占比了很大一部分, 其结构如下:
super_status_bar.xml:
Android SystemUI系统介绍其它 status_bar 通过 CollapsedStatusBarFragment 完成对 status_bar_container 的替换.

StatusBarWindowView 的加载:

StatusBar 组件启动时  createAndAddWindows();

加载布局
protected void inflateStatusBarWindow(Context context) {
       mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
               R.layout.super_status_bar, null);
   }
将窗口加入到 PhoneWindow 中, 窗口类型为 TYPE_STATUS_BAR
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());

	mLp = new WindowManager.LayoutParams(
	        ViewGroup.LayoutParams.MATCH_PARENT,
	        barHeight,
	        WindowManager.LayoutParams.TYPE_STATUS_BAR,
	        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
	                | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
	                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
	                | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
	                | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
	        PixelFormat.TRANSLUCENT);
	mLp.token = new Binder();
	mLp.gravity = Gravity.TOP;
	mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
	mLp.setTitle("StatusBar");
	mLp.packageName = mContext.getPackageName();
	mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
	mStatusBarView = statusBarView;
	mBarHeight = barHeight;
	mWindowManager.addView(mStatusBarView, mLp);
	mLpChanged = new WindowManager.LayoutParams();
	mLpChanged.copyFrom(mLp);
	mStatusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);

2.4 锁屏

先来看一下,手机是如何锁屏的
Android SystemUI系统介绍手机的锁屏事件来源于 Power 的Notifier 线程的通知, 当按下电源键或自动灭屏会触发这个事件.

从上述源程中, 可以清楚的看到整个锁屏的结构图, 在 framework 层, 有一个 KeyguardService 的代理, 通过这个代理接口, 实现和 SystemUI 的交互

解锁流程:
android 提供的安全验证集在中 LockPatternUtils 中, 通过其中的接口来获取加密类型和解锁正确与否.

解锁成功
    public void reportSuccessfulPasswordAttempt(int userId) {
        if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
            return;
        }
        getDevicePolicyManager().reportSuccessfulPasswordAttempt(userId);
        getTrustManager().reportUnlockAttempt(true /* authenticated */, userId);
    } 

2.5 QS 面板

QS Panel 是 StatusBar 中的一个模块, 将它单独列出来, 主要是它有一个快捷入口的功能, 提供一些快捷事件.
一个自定义 TileService 例子

@Implements(TileService.class)
public class ShadowTileService extends ShadowService {

    @RealObject TileService realService;

    private Tile mTile;

    public void __constructor__() { }

    @Implementation
    public final Tile getQsTile() {
        return mTile;
    }

    @Implementation
    public final void startActivityAndCollapse(Intent intent) {
        realService.startActivity(intent);
    }

    // Non-Android setter.
    public void setTile(Tile tile) {
        mTile = tile;
    }
}

QS Panel的视图结构:

Android SystemUI系统介绍SystemUI 的下拉面板由两个部分构成, 一个是 QsPanel, 另一个是通知面板
QSTileHost 管理当前的Tile
加载默认Tiles: tileList = res.getString(R.string.quick_settings_tiles);

2.6 分屏

进入分屏模式

    protected boolean toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) {
        if (mRecents == null) {
            return false;
        }
        int dockSide = WindowManagerProxy.getInstance().getDockSide();
        if (dockSide == WindowManager.DOCKED_INVALID) {
            final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition();
            if (navbarPos == NAV_BAR_POS_INVALID) {
                return false;
            }
            int createMode = navbarPos == NAV_BAR_POS_LEFT
                    ? ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT
                    : ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
            return mRecents.splitPrimaryTask(NavigationBarGestureHelper.DRAG_MODE_NONE, createMode,
                    null, metricsDockAction);
        } else {
            Divider divider = getComponent(Divider.class);
            if (divider != null && divider.isMinimized() && !divider.isHomeStackResizable()) {
                // Undocking from the minimized state is not supported
                return false;
            } else {
                EventBus.getDefault().send(new UndockingTaskEvent());
                if (metricsUndockAction != -1) {
                    mMetricsLogger.action(metricsUndockAction);
                }
            }
        }
        return true;
    }

启动分屏

    public void splitPrimaryTask(int taskId, int dragMode, int stackCreateMode,
            Rect initialBounds) {
        SystemServicesProxy ssp = Recents.getSystemServices();

        // Make sure we inform DividerView before we actually start the activity so we can change
        // the resize mode already.
        if (ssp.setTaskWindowingModeSplitScreenPrimary(taskId, stackCreateMode, initialBounds)) {
            EventBus.getDefault().send(new DockedTopTaskEvent(dragMode, initialBounds));
        }
    }

在窗口控制中, 将窗口属性改为 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY

功能实现在 ActivityManagerService 中.

3.杂项记录

//TODO
// 未完, 仅记录了一个大体框架, 后续再充实.

本文地址:https://blog.csdn.net/mzsyxiao/article/details/107033541