SystemUI那些事

  说起SystemUI,想必大家是既熟悉又陌生。熟悉是因为我们在使用安卓手机的过程中经常和它打交道,陌生是因为我们不知道哪些系统组件属于SystemUI以及各组件的运作机制是怎么样的。今天阳哥就从SystemUI的启动过程出发,带领大家学习SystemUI。

  SystemUI位于/system/priv-app目录下,所以它是一个特权应用,它的AndroidManifest文件如下:

// 本文使用的源码版本为Android N
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
    package="com.android.systemui"
    android:sharedUserId="android.uid.systemui"
    coreApp="true">
    ......
    <application
        android:name=".SystemUIApplication"
        android:persistent="true"
        android:process="com.android.systemui"
        ......
        <service android:name="SystemUIService"
            android:exported="true" />
        ......
</manifest>

  从上面的清单文件中,我们可以看到SystemUI使用了共享的 Linux 用户 ID :com.uid.systemui。那么,还有哪个应用也使用了这个共享ID呢?它就是Keyguard,也就是大家常用的锁屏,它也是SystemUI体系的一部分。除此之外,它们还共享着同一个进程,组件默认都运行在com.android.systemui这个进程中。

  那么,SystemUI是在什么时候由谁启动的呢?我们知道,Zygote在初始化的时候会启动SystemServer进程,并调用它的main方法来执行一些核心的系统服务(比如:BatteryService、UsageStatsService等)和必要的其他服务(比如:CameraService、WindowManagerService等)的初始化工作。而SystemUI的启动就是在这些必要服务初始化完毕之后触发的:

public final class SystemServer {
    ......
    private void startOtherServices() {
        ......
        // We now tell the activity manager it is okay to run third party
        // code.  It will call back into us once it has gotten to the state
        // where third party code can really run (but before it has actually
        // started launching the initial applications), for us to complete our
        // initialization.
        mActivityManagerService.systemReady(new Runnable() {
            @Override
            public void run() {
                ......
                Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartSystemUI");
                try {
                    startSystemUi(context);

                } catch (Throwable e) {
                    reportWtf("starting System UI", e);
                }
                Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
                ......
            }
        });
    }
    ......
}

可以看到这里通过systemReady方法通知ActivityManagerService(以下简称AMS)是时候执行第三方的代码。


/**
 * {@link ActivityManagerService#systemReady} 
 */
public void systemReady(final Runnable goingCallback) {
    synchronized(this) {
        if (mSystemReady) {
            // If we're done calling all the receivers, run the next "boot phase" passed in
            // by the SystemServer
            if (goingCallback != null) {
                goingCallback.run();
            }
            return;
        }

        mLocalDeviceIdleController
                = LocalServices.getService(DeviceIdleController.LocalService.class);

        // Make sure we have the current profile info, since it is needed for security checks.
        mUserController.onSystemReady();
        mRecentTasks.onSystemReadyLocked();
        mAppOpsService.systemReady();
        mSystemReady = true;
    }

    // 回收非persistent进程
    ArrayList<ProcessRecord> procsToKill = null;
    synchronized(mPidsSelfLocked) {
        for (int i=mPidsSelfLocked.size()-1; i>=0; i--) {
            ProcessRecord proc = mPidsSelfLocked.valueAt(i);
            if (!isAllowedWhileBooting(proc.info)){
                if (procsToKill == null) {
                    procsToKill = new ArrayList<ProcessRecord>();
                }
                procsToKill.add(proc);
            }
        }
    }
    synchronized(this) {
        if (procsToKill != null) {
            for (int i=procsToKill.size()-1; i>=0; i--) {
                ProcessRecord proc = procsToKill.get(i);
                 Slog.i(TAG, "Removing system update proc: " + proc);
                removeProcessLocked(proc, true, false, "system update done");
            }
        }

        // Now that we have cleaned up any update processes, we
        // are ready to start launching real processes and know that
        // we won't trample on them any more.
        mProcessesReady = true;
    }
    
    // 解析全局设置 
    retrieveSettings();
    
    final int currentUserId;
    synchronized (this) {
        currentUserId = mUserController.getCurrentUserIdLocked();
        readGrantedUriPermissionsLocked();
    }

    if (goingCallback != null) goingCallback.run();

    ......
}

  AMS接收到通知后,在执行Runnable回调之前,会先执行一些准备工作,比如:回收boot过程中启动的非常驻进程,解析全局设置等。我们来看一看retrieveSettings方法做了些什么:


/**
 * {@link ActivityManagerService#retrieveSettings} 
 */
private void retrieveSettings() {
    final ContentResolver resolver = mContext.getContentResolver();
    // 检查自由形状模式开关是否打开
    final boolean freeformWindowManagement =
            mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
                    || Settings.Global.getInt(
                            resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
    // 检查画中画模式开关是否打开
    final boolean supportsPictureInPicture =
            mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
    // 检查多窗口模式开关是否打开
    final boolean supportsMultiWindow = ActivityManager.supportsMultiWindow();
    final String debugApp = Settings.Global.getString(resolver, DEBUG_APP);
    final boolean waitForDebugger = Settings.Global.getInt(resolver, WAIT_FOR_DEBUGGER, 0) != 0;
    final boolean alwaysFinishActivities =
            Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0;
    final boolean lenientBackgroundCheck =
            Settings.Global.getInt(resolver, LENIENT_BACKGROUND_CHECK, 0) != 0;
    final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
    // 检查是否强制调整应用大小
    final boolean forceResizable = Settings.Global.getInt(
            resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
    final boolean supportsLeanbackOnly =
            mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK_ONLY);

    // Transfer any global setting for forcing RTL layout, into a System Property
    SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");

    final Configuration configuration = new Configuration();
    Settings.System.getConfiguration(resolver, configuration);
    if (forceRtl) {
        // This will take care of setting the correct layout direction flags
        configuration.setLayoutDirection(configuration.locale);
    }

    synchronized (this) {
        mDebugApp = mOrigDebugApp = debugApp;
        mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger;
        mAlwaysFinishActivities = alwaysFinishActivities;
        mLenientBackgroundCheck = lenientBackgroundCheck;
        mSupportsLeanbackOnly = supportsLeanbackOnly;
        mForceResizableActivities = forceResizable;
        mWindowManager.setForceResizableTasks(mForceResizableActivities);
        if (supportsMultiWindow || forceResizable) {
            mSupportsMultiWindow = true;
            mSupportsFreeformWindowManagement = freeformWindowManagement || forceResizable;
            mSupportsPictureInPicture = supportsPictureInPicture || forceResizable;
        } else {
            mSupportsMultiWindow = false;
            mSupportsFreeformWindowManagement = false;
            mSupportsPictureInPicture = false;
        }
        // This happens before any activities are started, so we can
        // change mConfiguration in-place.
        updateConfigurationLocked(configuration, null, true);
        if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                "Initial config: " + mConfiguration);

        // Load resources only after the current configuration has been set.
        final Resources res = mContext.getResources();
        mHasRecents = res.getBoolean(com.android.internal.R.bool.config_hasRecents);

        ......
    }
}

不难发现,这个函数实际上就是在获取开发者选项中的设置,并将这些设置同步给AMS。在第44行,我么可以看到如果forceResizable开关打开,那么Android手机就同时支持多窗口、自由形状以及画中画三种模式。

  回到SystemServer的startOtherServices方法,AMS在执行完相关准备工作后,通过Runnable回调执行SystemUI的初始化工作:

/**
 * {@link SystemServer#startSystemUi} 
 */
static final void startSystemUi(Context context) {
    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);
}

  这里实际上就是通过一个显式的Intent来启动SystemUIService组件。而从文章开头的AndroidManifest文件中,可以看到SystemUI有自己的Application实现类:SystemUIApplication。一个应用进程的初始化过程是从Application开始的,熟悉Android应用进程启动过程的读者对此一定不会陌生,所以在com.android.systemui进程初始化时首先会执行SystemUIApplication的相关生命周期方法。

  我们先看看SystemUIApplication初始化时都干了些什么。

public class SystemUIApplication extends Application {

   private static final String TAG = "SystemUIService";
   private static final boolean DEBUG = false;

   /**
    * 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,
           // 多窗口
           Divider.class,
           // 状态栏和导航栏
           com.android.systemui.statusbar.SystemBars.class,
           // USB 存储通知
           com.android.systemui.usb.StorageNotification.class,
           // 电源事件
           com.android.systemui.power.PowerUI.class,
           // 铃声播放
           com.android.systemui.media.RingtonePlayer.class,
           // 软键盘
           com.android.systemui.keyboard.KeyboardUI.class,
           // 画中画
           com.android.systemui.tv.pip.PipUI.class,
           // 快捷键
           com.android.systemui.shortcut.ShortcutKeyDispatcher.class
   };

   /**
    * The classes of the stuff to start for each user.  This is a subset of the services listed
    * above.
    */
   private final Class<?>[] SERVICES_PER_USER = new Class[] {
           com.android.systemui.recents.Recents.class,
           com.android.systemui.tv.pip.PipUI.class
   };

   /**
    * Hold a reference on the stuff we start.
    */
   private final SystemUI[] mServices = new SystemUI[SERVICES.length];
   private boolean mServicesStarted;
   private boolean mBootCompleted;
   private final Map<Class<?>, Object> mComponents = new HashMap<>();

   @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);

       SystemUIFactory.createFromConfig(this);
       if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
           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++) {
                            mServices[i].onBootCompleted();
                        }
                    }
                }
            }, filter);
        } else {
            // For a secondary user, boot-completed will never be called because it has already
            // been broadcasted on startup for the primary SystemUI process.  Instead, for
            // components which require the SystemUI component to be initialized per-user, we
            // start those components now for the current non-system user.
            startServicesIfNeeded(SERVICES_PER_USER);
        }
    }

    /**
     * Makes sure that all the SystemUI services are running. If they are already running, this is a
     * no-op. This is needed to conditinally start all the services, as we only need to have it in
     * the main process.
     *
     * <p>This method must only be called from the main thread.</p>
     */
    public void startServicesIfNeeded() {
        startServicesIfNeeded(SERVICES);
    }

    /**
     * Ensures that all the Secondary user SystemUI services are running. If they are already
     * running, this is a no-op. This is needed to conditinally start all the services, as we only
     * need to have it in the main process.
     *
     * <p>This method must only be called from the main thread.</p>
     */
    void startSecondaryUserServicesIfNeeded() {
        startServicesIfNeeded(SERVICES_PER_USER);
    }

    private void startServicesIfNeeded(Class<?>[] services) {
        if (mServicesStarted) {
            return;
        }

        if (!mBootCompleted) {
            // check to see if maybe it was already completed long before we began
            // see ActivityManagerService.finishBooting()
            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 for user " +
                Process.myUserHandle().getIdentifier() + ".");
        final int N = services.length;
        for (int i=0; i<N; i++) {
            Class<?> cl = services[i];
            if (DEBUG) Log.d(TAG, "loading: " + cl);
            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();

            if (mBootCompleted) {
                mServices[i].onBootCompleted();
            }
        }
        mServicesStarted = true;
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        if (mServicesStarted) {
            int len = mServices.length;
            for (int i = 0; i < len; i++) {
                if (mServices[i] != null) {
                    mServices[i].onConfigurationChanged(newConfig);
                }
            }
        }
    }
    
    ......


}

  可以看到SystemUIApplication在初始化的时候,会依据用户的类型执行不同的操作。针对UserHandle.SYSTEM级用户,会注册BroadcastReceiver监听开机广播,在接收到开机广播之后会通过onBootCompleted接口通知各服务执行各自的操作。而对二级用户来说,则不会监听开机广播,而是直接初始化与用户紧密相关的服务SERVICES_PER_USER(画中画、最近任务)。除去在初始化时判断用户的级别,SystemUIApplication也会监听用户的切换行为,在切换用户时会执行startSecondaryUserServicesIfNeeded方法。有兴趣的读者可以去看UserSwitcherController的具体实现。
  那么,SERVICES中的服务是何时启动的呢?前文说到SystemServer通过一个显式的Intent来启动SystemUIService组件。我们来看一看SystemUIService做了什么工作:

public class SystemUIService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        ((SystemUIApplication) getApplication()).startServicesIfNeeded();
    }

    ......
}

  没错,SERVICES中各服务的初始化正是由SystemUIService通过startServicesIfNeeded方法来触发的,实际上这也是它的唯一工作。到这里,我们就大概明白了,可以认为SystemUIService就是SystemUI整个应用的初始化入口。
  SystemUIApplication除去启动各种服务之外,也执行了一些初始化工作,比如:SystemUIFactory.createFromConfig。

public class SystemUIFactory {

    static SystemUIFactory mFactory;
    public static SystemUIFactory getInstance() {
        return mFactory;
    }
    public SystemUIFactory() {}
    
    public static void createFromConfig(Context context) {
        final String clsName = context.getString(R.string.config_systemUIFactoryComponent);
        if (clsName == null || clsName.length() == 0) {
            throw new RuntimeException("No SystemUIFactory component configured");
        }

        try {
            Class<?> cls = null;
            cls = context.getClassLoader().loadClass(clsName);
            mFactory = (SystemUIFactory) cls.newInstance();
        } catch (Throwable t) {
            Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t);
            throw new RuntimeException(t);
        }
    }
    ......
}

  我们可以看到,createFromConfig方法主要就是从配置文件R.string.config_systemUIFactoryComponent中读取类名,然后通过反射的方式来获取实例并初始化mFactory,具体的类名如下:

<!--  config.xml -->
<resources>
    ......
    <!-- SystemUIFactory component -->
    <string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIFactory</string>
    
    ......

</resources>

  可以看到,配置文件中的类名实际上就是SystemUIFactory它自己。你可能会想:不就是个初始化,为啥搞得这么复杂,Android为什么要这样设计呢?我们知道Android是一个跨平台的操作系统,手机、智能手表、电视等等都可以搭载Android操作系统,那么相对应的SystemUIFactory可能就会有不同的实现。所以,为了方便不同平台的定制,才使用了这种方式初始化。
  好了,今天的分享就到这里,欢迎大家一起讨论交流!也欢迎大家关注阳哥的公众号:阳哥说技术。
阳哥说技术

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值