美文网首页
Android音量调节(二)音量处理及UI刷新

Android音量调节(二)音量处理及UI刷新

作者: MrChen丶 | 来源:发表于2021-07-13 10:28 被阅读0次

    前言

    第一篇:Android音量调节(一)音量键的处理流程

    由于时间跨度过久,这篇博客也由第一篇的基于Android 8升级为了Android11。

    本来是想衔接第一篇直接去写AudioService.java里面通知音量改变之后SystemUI里面的处理,但是当我去查看并分析源码的时候,慢慢的慢慢的就把SystemUI的启动过程给看完了,想了一下,于是决定先分析SystemUI的启动过程,然后再去衔接第一篇去分析音量条的处理过程。

    在整个系统App里面,SystemUI这个App非常的复杂,在Android11上,Google也用到了Dagger来处理里面丑陋的依赖关系。当时也花了比较久的时间去查看DI这一块,也一起写进去,就当做是一个学习的记录。

    如有写的存在问题的地方,还望大家能够指点一下,感激不尽。

    于是本篇博客内容分以下三大块:

    • SystemUI启动过程分析
    • SystemUI中Dagger相关代码分析
    • VolumeUI流程分析

    如果对第一章内容已经了解的同学可以直接享用本篇内容,本篇文章内容较多,希望能仔细阅读。

    有了解过Dagger或者使用过Dagger的同学可以看一下第二小节的内容,如果你从来没有接触过Dagger,那么可以跳过第二小节,这个也不影响整体流程。

    1.SystemUI启动过程分析

    SystemUI是在frameworks/base/packages/SystemUI/路径下,以apk的形式预置到了系统中。

    SystemUI最开始是在SystemServer.java里面去启动的。

    1.1 启动SystemUIService

    // frameworks/base/services/java/com/android/server/SystemServer.java
    public final class SystemServer {
     
        /**
         * The main entry point from zygote.
         */
        public static void main(String[] args) {
            new SystemServer().run();
        }
      
        private void run() {
            ...
            startBootstrapServices();
            startCoreServices();
            // SystemUIService在这里面启动
            startOtherServices();
            SystemServerInitThreadPool.shutdown();
            ...
        }
      
        private void startOtherServices() {
            ....
            traceBeginAndSlog("StartSystemUI");
            try {
                startSystemUi(context, windowManager);
            } catch (Throwable e) {
                reportWtf("starting System UI", e);
            }
            traceEnd();
            ...
        }
      
        static final void startSystemUi(Context context, WindowManagerService windowManager) {
            PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
            Intent intent = new Intent();
            intent.setComponent(pm.getSystemUiServiceComponent());
            intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
            //Slog.d(TAG, "Starting service: " + intent);
            context.startServiceAsUser(intent, UserHandle.SYSTEM);
            windowManager.onSystemUiStarted();
        }
    
    }
    

    这里我们来看一下SystemUI的Component是如何获取到的,先看一下PackageManagerInternal的实现

    // frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
    public class PackageManagerService extends IPackageManager.Stub
            implements PackageSender {
    
        public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
            ...
            // Expose private service for system components to use.
            mPmInternal = new PackageManagerInternalImpl();
            LocalServices.addService(PackageManagerInternal.class, mPmInternal);
            ...
        }
     
        private class PackageManagerInternalImpl extends PackageManagerInternal {
            ...
            @Override
            public ComponentName getSystemUiServiceComponent() {
                return ComponentName.unflattenFromString(mContext.getResources().getString(
                        com.android.internal.R.string.config_systemUIServiceComponent));
            }
            ...
        }
    
    }
    

    可以看到PackageManagerInternalImpl是PackageManagerService的内部类,在PackageManagerService的构造函数里面去实例化了PackageManagerInternalImpl,并且添加到了LocalServices里面。

    在PackageManagerInternalImpl内部是获取了字符串资源,文件内容如下

    <!-- frameworks/base/core/res/res/values/config.xml -->
    <!-- SystemUi service component -->
    <string name="config_systemUIServiceComponent" translatable="false"
            >com.android.systemui/com.android.systemui.SystemUIService</string>
    

    那接下来我们看看PackageManagerService是在哪里去实例化的,这边我找了一下,在PackageManagerService里面有这样一个方法

    // frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
    public class PackageManagerService extends IPackageManager.Stub
            implements PackageSender {
      
        public static PackageManagerService main(Context context, Installer installer,
                boolean factoryTest, boolean onlyCore) {
            ...
            // 这里实例化了PackageManagerService
            PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest);
            ...
            return m;
        }
    }
    

    可以看到在PackageManagerService里面有一个main的静态方法,在这个方法里面去实例化了PackageManagerService。

    最后是在SystemServer里面找到了调用PackageManagerService的main方法的地方,如下:

    // frameworks/base/services/java/com/android/server/SystemServer.java
    public final class SystemServer {
    
        private void run() {
            ...
            // PackageManagerService在这里面启动
            startBootstrapServices();
            startCoreServices();
            // SystemUIService在这里面启动
            startOtherServices();
            SystemServerInitThreadPool.shutdown();
            ...
        }
    
        private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
            ...
            try {
                Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain");
                // 启动PackageManagerService
                mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                        mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
            } finally {
                Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");
            }
            ...
        }
    
    }
    

    可以看到startBootstrapServices是在startOtherServices之前启动的,所以这边就暂时讲完了。

    可以知道最终启动的是com.android.systemui.SystemUIService这个service

    接下来说个题外话,启动service时,是service先运行,还是Application先运行呢?我们从代码中来分析

    1.2 启动Service流程

    整个Service的启动流程这边也不细说,这边就说一下ActivityThread.java里面handleCreateService方法的调用过程,该方法代码如下:

    // frameworks/base/core/java/android/app/ActivityThread.java
    @UnsupportedAppUsage
    private void handleCreateService(CreateServiceData data) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
    
        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
            // 创建Context
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            // 创建Application
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            // 创建service的实例
            service = packageInfo.getAppFactory()
                    .instantiateService(cl, data.info.name, data.intent);
            // Service resources must be initialized with the same loaders as the application
            // context.
            context.getResources().addLoaders(
                        app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
    
            context.setOuterContext(service);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
            // 调用service的onCreate()方法
            service.onCreate();
            mServices.put(data.token, service);
            try {
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(service, e)) {
                throw new RuntimeException(
                    "Unable to create service " + data.info.name
                    + ": " + e.toString(), e);
            }
        }
    }
    

    这边可以看到第15行的注释有说明,先创建了Application的实例,然后创建Service的实例,最后调用了Service的onCreate()方法

    我们看看创建Application时,makeApplication方法里面做了哪些操作,代码如下:

    // frameworks/base/core/java/android/app/LoadedApk.java
    @UnsupportedAppUsage
    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        ...
        if (instrumentation != null) {
            try {
                instrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                ...
            }
        }
        ......
    }
    
    // frameworks/base/core/java/android/app/Instrumentation.java
    public void callApplicationOnCreate(Application app) {
        app.onCreate();
    }
    

    这边再来整理一下实际的顺序:

    1. 创建Application实例;
    2. 调用Application的onCreate()方法;
    3. 创建Service实例;
    4. 调用Service的onCreate()方法;

    当启动SystemUIService时候就是这样的一个流程。

    下面就来看看SystemUI的Application的onCreate()方法。

    2.SystemUI中Dagger相关代码分析

    文章开始有提过,如果没有了解过Dagger的,可能看不懂这一小节,可以直接跳过,对整体流程没有影响。

    2.1 appComponentFactory标签

    整个SystemUI大部分的依赖实例都管理在Dependency.java这个类中,我们这篇文章主要是分析VolumeUI,那我们就看看VolumeUI是怎么初始化以及启动的。

    在1.2小节中,我们知道了Application先被实例化,然后会调用onCreate方法,但是在我们的AndroidManifest.xml中,Application的标签里面定义了一个属性:

    <application
        android:name=".SystemUIApplication"
        ...
        tools:replace="android:appComponentFactory"
        android:appComponentFactory=".SystemUIAppComponentFactory">
    </application>
    

    就是这个appComponentFactory的属性,如果我们指定了相关的类,那么我们appComponentFactory的实现类在创建Application的实例时会收到回调,这个时候我们可以去执行一些初始化的操作,可以在创建Application实例之前或者之后都可以。具体流程这里不去一步一步分析了,可以去跟一下源码流程,比较简单。

    2.2 Denpendency依赖实例注入

    SystemUIAppComponentFactory继承自AppComponentFactory,重写了instantiateApplicationCompat方法,当系统去创建Application的实例对象时就会回调这个方法。

    // frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
    public class SystemUIAppComponentFactory extends AppComponentFactory {
    
        private static final String TAG = "AppComponentFactory";
        @Inject
        public ContextComponentHelper mComponentHelper;
    
        public SystemUIAppComponentFactory() {
            super();
        }
    
        @NonNull
        @Override
        public Application instantiateApplicationCompat(
                @NonNull ClassLoader cl, @NonNull String className)
                throws InstantiationException, IllegalAccessException, ClassNotFoundException {
            Application app = super.instantiateApplicationCompat(cl, className);
            if (app instanceof ContextInitializer) {
                ((ContextInitializer) app).setContextAvailableCallback(
                        context -> {
                            SystemUIFactory.createFromConfig(context);
                            SystemUIFactory.getInstance().getRootComponent().inject(
                                    SystemUIAppComponentFactory.this);
                        }
                );
            }
            return app;
        }
        
        ...
     
        public interface ContextAvailableCallback {
            void onContextAvailable(Context context);
        }
    
        public interface ContextInitializer {
            void setContextAvailableCallback(ContextAvailableCallback callback);
        }
    }
    

    这里看到先调用了super方法拿到Application的实例化对象,然后判断Application是否实现了ContextInitializer这个接口,如果实现了,则去设置Context可用的监听,收到监听之后先创建SystemUIFactory的实例,然后初始化SystemUI的Dagger组件,然后向SystemUIAppComponentFactory注入依赖,这里主要的依赖就是ContextComponentHelper了。

    // frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
    public class SystemUIApplication extends Application implements
            SystemUIAppComponentFactory.ContextInitializer {
        ...
        @Override
        public void setContextAvailableCallback(
                SystemUIAppComponentFactory.ContextAvailableCallback callback) {
            mContextAvailableCallback = callback;
        }
    
        @Override
        public void onCreate() {
            ...
            mContextAvailableCallback.onContextAvailable(this);
            mRootComponent = SystemUIFactory.getInstance().getRootComponent();
            mComponentHelper = mRootComponent.getContextComponentHelper();
            ...
        }
    }
    

    这里可以看到在onCreate里面调用了onContextAvailable方法。

    接下来看看SystemUIFactory,这个类主要是用来提供自定义的SystemUI components,

    // frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
    public class SystemUIFactory {
        private static final String TAG = "SystemUIFactory";
    
        static SystemUIFactory mFactory;
        private SystemUIRootComponent mRootComponent;
    
        public static <T extends SystemUIFactory> T getInstance() {
            return (T) mFactory;
        }
    
        public static void createFromConfig(Context context) {
            if (mFactory != null) {
                return;
            }
    
            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();
                mFactory.init(context);
            } catch (Throwable t) {
                Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t);
                throw new RuntimeException(t);
            }
        }
        ...
        public SystemUIFactory() {}
    
        private void init(Context context) {
            mRootComponent = buildSystemUIRootComponent(context);
    
            // Every other part of our codebase currently relies on Dependency, so we
            // really need to ensure the Dependency gets initialized early on.
            Dependency dependency = new Dependency();
            mRootComponent.createDependency().createSystemUI(dependency);
            dependency.start();
        }
    }
    

    SystemUIFactory有两个子类:CarSystemUIFactory、TvSystemUIFactory

    先说一下SystemUIFactory的createFromConfig做了哪些事情

    1.先根据config_systemUIFactoryComponent拿到class name,然后去实例化。

    2.然后调用buildSystemUIRootComponent去获取SystemUI的组件实例

    3.创建Dependency实例并绑定到DependencyInjector子组件中,这一步就完成了Dependency里面依赖的所有的对象的注入。

    4.然后启动Dependency做一些初始化的操作。

    这几步是比较复杂的,接下来看看Dependency的代码。

    2.3 Dependency代码分析

    Dependency管理着整个SystemUI绝大多数的依赖关系,在Dependency里面通过Lazy来延迟实例的初始化。虽然是延迟初始化,但是预计所有的对象都是在sysui启动期间去创建的。

    // frameworks/base/packages/SystemUI/src/com/android/systemui/Dependency.java
    /**
     * Class to handle ugly dependencies throughout sysui until we determine the
     * long-term dependency injection solution.
     */
    public class Dependency {
        // 通过Class作为key,实例化对象作为value来当做一个缓存
        private final ArrayMap<Object, Object> mDependencies = new ArrayMap<>();
        // 通过Class作为key,LazyDependencyCreator接口当做value来延迟初始化对应的实例
        private final ArrayMap<Object, LazyDependencyCreator> mProviders = new ArrayMap<>();
    
        ...
        // 这里通过Lazy延迟对应的对象的实例化
        @Inject Lazy<ActivityStarter> mActivityStarter;
        @Inject Lazy<BroadcastDispatcher> mBroadcastDispatcher;
        ...
        
        protected void start() {
            ...
            // 当调用start方法后,会将Class和延迟创建实例化的接口对象存放进map
            mProviders.put(ActivityStarter.class, mActivityStarter::get);
            mProviders.put(BroadcastDispatcher.class, mBroadcastDispatcher::get);
            ...
        }
    
        // 在需要使用实例对象的时候就会调用这个方法
        // e.g. 
        // ActivityStarter starter = Dependency.get(ActivityStarter.class);
        @Deprecated
        public static <T> T get(Class<T> cls) {
            return sDependency.getDependency(cls);
        }
    
        protected final <T> T getDependency(Class<T> cls) {
            return getDependencyInner(cls);
        }
      
        // 通过mDependencies去拿缓存的实例,如果为空则会创建对应的实例
        // 并将获取到的实例存放在mDependencies里面当做缓存
        private synchronized <T> T getDependencyInner(Object key) {
            @SuppressWarnings("unchecked")
            T obj = (T) mDependencies.get(key);
            if (obj == null) {
                obj = createDependency(key);
                mDependencies.put(key, obj);
    
                // TODO: Get dependencies to register themselves instead
                if (autoRegisterModulesForDump() && obj instanceof Dumpable) {
                    mDumpManager.registerDumpable(obj.getClass().getName(), (Dumpable) obj);
                }
            }
            return obj;
        }
    
        // 通过懒加载回调获取注入的实例
        @VisibleForTesting
        protected <T> T createDependency(Object cls) {
            Preconditions.checkArgument(cls instanceof DependencyKey<?> || cls instanceof Class<?>);
    
            @SuppressWarnings("unchecked")
            LazyDependencyCreator<T> provider = mProviders.get(cls);
            if (provider == null) {
                throw new IllegalArgumentException("Unsupported dependency " + cls
                        + ". " + mProviders.size() + " providers known.");
            }
            return provider.createDependency();
        }
        ...
    }
    

    这里就是缓存着整个SystemUI大部分的依赖了,其实可以看到这个类的注释,Google也是说明了Dependency只是解决依赖的一种方法,目前并没有找到一个比较好的方法来处理依赖关系。

    2.4 启动Service

    // frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java
    public class SystemUIService extends Service {
        @Override
        public void onCreate() {
            super.onCreate();
    
            // Start all of SystemUI
            ((SystemUIApplication) getApplication()).startServicesIfNeeded();
        }
    }
    

    通过ContextComponentHelper解析预设的service类名得到实例并启动。

    // frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
    public class SystemUIApplication {
        
        public void startServicesIfNeeded() {
            String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());
            startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);
        }
        
        private void startServicesIfNeeded(String metricsPrefix, String[] services) {
            ...
            mServices = new SystemUI[services.length];
            final int N = services.length;
            for (int i = 0; i < N; i++) {
                String clsName = services[i];
                try {
                    SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
                    if (obj == null) {
                        Constructor constructor = Class.forName(clsName).getConstructor(Context.class);
                        obj = (SystemUI) constructor.newInstance(this);
                    }
                    mServices[i] = obj;
                }
                ...
                mServices[i].start();
       
                if (mBootCompleteCache.isBootComplete()) {
                    mServices[i].onBootCompleted();
                }
            }
            mServicesStarted = true;
        }
    }
    

    在2.2小节已经说明,mComponentHelper单例对象通过Dagger提供。

    // frameworks/base/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
    @Singleton
    @Component(modules = {...})
    public interface SystemUIRootComponent {
        ...
        @Singleton
        ContextComponentHelper getContextComponentHelper();
        ...
    }
    

    ContextComponentHelper是在SystemUIModule中提供的

    // frameworks/base/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
    public abstract class SystemUIModule {
    
        @Binds
        public abstract ContextComponentHelper bindComponentHelper(
                ContextComponentResolver componentHelper);
    
        ...
    }
    

    实际上提供的是ContextComponentResolver的实例。

    // frameworks/base/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java
    @Singleton
    public class ContextComponentResolver implements ContextComponentHelper {
        private final Map<Class<?>, Provider<Activity>> mActivityCreators;
        private final Map<Class<?>, Provider<Service>> mServiceCreators;
        private final Map<Class<?>, Provider<SystemUI>> mSystemUICreators;
        private final Map<Class<?>, Provider<RecentsImplementation>> mRecentsCreators;
        private final Map<Class<?>, Provider<BroadcastReceiver>> mBroadcastReceiverCreators;
    
        @Inject
        ContextComponentResolver(Map<Class<?>, Provider<Activity>> activityCreators,
                Map<Class<?>, Provider<Service>> serviceCreators,
                Map<Class<?>, Provider<SystemUI>> systemUICreators,
                Map<Class<?>, Provider<RecentsImplementation>> recentsCreators,
                Map<Class<?>, Provider<BroadcastReceiver>> broadcastReceiverCreators) {
            mActivityCreators = activityCreators;
            mServiceCreators = serviceCreators;
            mSystemUICreators = systemUICreators;
            mRecentsCreators = recentsCreators;
            mBroadcastReceiverCreators = broadcastReceiverCreators;
        }
        ...
        private <T> T resolve(String className, Map<Class<?>, Provider<T>> creators) {
            try {
                Class<?> clazz = Class.forName(className);
                Provider<T> provider = creators.get(clazz);
                return provider == null ? null : provider.get();
            } catch (ClassNotFoundException e) {
                return null;
            }
        }
    }
    

    ContextComponentResolver用于缓存各种Activity,Service,SystemUI等等的实例对象,通过class实例从Map查询得到的Provider里取得对应的Service实例。

    我们找一下提供VolumeUI实例的地方,是在SystemUIBinder里面提供的

    // frameworks/base/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
    @Module(includes = {...})
    public abstract class SystemUIBinder {
    
        @Binds
        @IntoMap
        @ClassKey(VolumeUI.class)
        public abstract SystemUI bindVolumeUI(VolumeUI sysui);
        ...
    }
    
    // frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
    @Singleton
    public class VolumeUI extends SystemUI {
    
        @Inject
        public VolumeUI(Context context, VolumeDialogComponent volumeDialogComponent) {
            super(context);
            mVolumeComponent = volumeDialogComponent;
        }
        ...
    }
    

    这里就是提供的VolumeUI的地方了,到这里我们的Dagger的大致流程到这里就结束了。

    2.5 Dagger小结

    Dagger的部分依赖处理我们就讲完了。里面涉及到了一些注解,都需要我们去了解实际的作用。

    1.Application标签有appComponentFactory属性,可以指定对应的子类,在实例化Application,Service,Activity时都会回调对应的方法,我们可以去做一些初始化的操作。

    2.SystemUI Dagger根组件是SystemUIRootComponent这个类,它引用了非常多的module。可以理解为,大部分的依赖都是由SystemUIRootComponent这个类提供的注入。

    3.在SystemUIRootComponent类中有Dependency.DependencyInjector createDependency();这个方法,Dependency.DependencyInjector是在Dependency里面,注解为@Subcomponent,为SystemUIRootComponent的子组件,这个注解的作用就是,即使SystemUIRootComponent没有将实例对象通过方法暴露出去,DependencyInjector也能获取到SystemUIRootComponent里面提供的所有实例的对象。最后将所有的依赖注入到Denpendency里面。

    4.在Dependency里面使用到了Lazy,延迟了依赖的对象的初始化,不是一开始就全部初始化,而是在使用的时候才会去初始化,再去通过map缓存起来。

    5.ContextComponentResolver这个类缓存了由Dagger提供的Activity、Service、SystemUI等等的实例化对象,在需要使用对应的实例时,会优先从这个类里面去拿到缓存的对象,如果为空才会去创建新的实例化对象。

    3.Volume初始化

    3.1 启动SystemUI

    话不多说,直接上代码

    public class SystemUIService extends Service {
    
        ...
        @Override
        public void onCreate() {
            super.onCreate();
            ((SystemUIApplication) getApplication()).startServicesIfNeeded();
        }
        ...
    }
    

    Application里面去启动所有的SystemUI,mComponentHelper通过class去拿对应的实例对象,如果为空则去通过Class实例化。

    public class SystemUIApplication {
        
        public void startServicesIfNeeded() {
            String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());
            startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);
        }
        
        private void startServicesIfNeeded(String metricsPrefix, String[] services) {
            ...
            mServices = new SystemUI[services.length];
            final int N = services.length;
            for (int i = 0; i < N; i++) {
                String clsName = services[i];
                try {
                    SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
                    if (obj == null) {
                        Constructor constructor = Class.forName(clsName).getConstructor(Context.class);
                        obj = (SystemUI) constructor.newInstance(this);
                    }
                    mServices[i] = obj;
                }
                ...
                mServices[i].start();
       
                if (mBootCompleteCache.isBootComplete()) {
                    mServices[i].onBootCompleted();
                }
            }
            mServicesStarted = true;
        }
    }
    

    需要启动的所有Serivce写在配置文件里面

        <!-- SystemUI Services: The classes of the stuff to start. -->
        <string-array name="config_systemUIServiceComponents" translatable="false">
            <item>com.android.systemui.util.NotificationChannels</item>
            <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
            <item>com.android.systemui.recents.Recents</item>
            <item>com.android.systemui.volume.VolumeUI</item>
            ...
        </string-array>
    

    我们需要注意的是,这里使用了模板模式。

    SystemUI是一个基类,其中定义了4个抽象或空方法,作为模板指定了子类的行为模式。资源文件中定义的众多子服务类都是SystemUI的子类,既然都继承自SystemUI类,那么这些子类就有一些共同的行为模式,在某些阶段应该有什么表现,只是具体如何表现因不同子类而异。与我们经常用到的AsyncTask是类似的。

    3.2 启动VolumeUI

    VolumeUI里面没有过多的操作,持有VolumeDialogComponent的引用,在 start的时候做一些初始化的操作。

    // frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
    @Singleton
    public class VolumeUI extends SystemUI {
    
        private VolumeDialogComponent mVolumeComponent;
    
        @Inject
        public VolumeUI(Context context, VolumeDialogComponent volumeDialogComponent) {
            super(context);
            mVolumeComponent = volumeDialogComponent;
        }
    
        @Override
        public void start() {
            boolean enableVolumeUi = mContext.getResources().getBoolean(R.bool.enable_volume_ui);
            boolean enableSafetyWarning =
                mContext.getResources().getBoolean(R.bool.enable_safety_warning);
            mEnabled = enableVolumeUi || enableSafetyWarning;
            if (!mEnabled) return;
    
            mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning);
            setDefaultVolumeController();
        }
        ...
        private void setDefaultVolumeController() {
            DndTile.setVisible(mContext, true);
            if (LOGD) Log.d(TAG, "Registering default volume controller");
            mVolumeComponent.register();
        }
    }
    

    两件事情:

    1.VolumeDialogComponent里面会去创建我们的音量条UI的实例对象,也就是VolumeDialogImpl

    2.setDefaultVolumeController方法会设置AudioService的回调接口

    3.3 创建VolumeDialogImpl

    先来看看VolumeDialogComponent里面的操作

    // frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
    @Singleton
    public class VolumeDialogComponent implements VolumeComponent, TunerService.Tunable,
            VolumeDialogControllerImpl.UserActivityListener{
    
        ...
        @Inject
        public VolumeDialogComponent(Context context, KeyguardViewMediator keyguardViewMediator,
                VolumeDialogControllerImpl volumeDialogController) {
            mContext = context;
            mKeyguardViewMediator = keyguardViewMediator;
            mController = volumeDialogController;
            mController.setUserActivityListener(this);
            // Allow plugins to reference the VolumeDialogController.
            Dependency.get(PluginDependencyProvider.class)
                    .allowPluginDependency(VolumeDialogController.class);
            Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
                    .withPlugin(VolumeDialog.class)
                    // 调用createDefault方法
                    .withDefault(this::createDefault)
                    // 回调处理
                    .withCallback(dialog -> {
                        if (mDialog != null) {
                            mDialog.destroy();
                        }
                        mDialog = dialog;
                        mDialog.init(LayoutParams.TYPE_VOLUME_OVERLAY, mVolumeDialogCallback);
                    }).build();
        }
    
        protected VolumeDialog createDefault() {
            VolumeDialogImpl impl = new VolumeDialogImpl(mContext);
            impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
            impl.setAutomute(true);
            impl.setSilentMode(false);
            return impl;
        }
        ...
             
    }
    

    可以看到VolumeDialogComponent持有VolumeDialogControllerImpl的引用

    在初始化的时候,会去创建我们的VolumeDialogImpl,并且会去调用其init方法

    // frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
    public class VolumeDialogImpl implements VolumeDialog,
            ConfigurationController.ConfigurationListener {
              
        private final H mHandler = new H();
        ...
        public void init(int windowType, Callback callback) {
            initDialog();
    
            mAccessibility.init();
    
            // 这里添加了VolumeDialogController里面的回调,
            mController.addCallback(mControllerCallbackH, mHandler);
            mController.getState();
    
            Dependency.get(ConfigurationController.class).addCallback(this);
        }
              
        private final VolumeDialogController.Callbacks mControllerCallbackH
                = new VolumeDialogController.Callbacks() {
    
            @Override
            public void onStateChanged(State state) {
                onStateChangedH(state);
            }
            ...
        };
              
      private final class H extends Handler {
            ...
            private static final int STATE_CHANGED = 7;
    
            public H() {
                super(Looper.getMainLooper());
            }
    
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    ...
                    case STATE_CHANGED: onStateChangedH(mState); break;
                }
            }
        }
        ...
    }
    

    这一段代码做了如下几件事情:

    1.初始化dialog,设置dialog的布局等等。

    2.添加VolumeDialogController的回调,当VolumeDialogController接收到AudioService的回调之后,通过Callback将事件继续通知给Dialog去做出响应的处理。这里的两个参数,一个是回调各个状态的接口,一个是在主线程初始化的Handler。

    // frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
    @Singleton
    public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable {
        
        protected C mCallbacks = new C();
    
        ...
        // 添加回调监听
        public void addCallback(Callbacks callback, Handler handler) {
            mCallbacks.add(callback, handler);
            callback.onAccessibilityModeChanged(mShowA11yStream);
        }
      
        class C implements Callbacks {
            // Callbacks作为key,Handler为value
            private final HashMap<Callbacks, Handler> mCallbackMap = new HashMap<>();
    
            public void add(Callbacks callback, Handler handler) {
                if (callback == null || handler == null) throw new IllegalArgumentException();
                mCallbackMap.put(callback, handler);
            }
    
            @Override
            public void onStateChanged(final State state) {
                final long time = System.currentTimeMillis();
                final State copy = state.copy();
                for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
                    entry.getValue().post(new Runnable() {
                        @Override
                        public void run() {
                            entry.getKey().onStateChanged(copy);
                        }
                    });
                }
                Events.writeState(time, copy);
            }
        }
      
        ...
    }
    

    这里C是Callbacks的实现类,并且在内部有一个Map,用来存放对应的Callbacks以及Handler

    在VolumeDialogControllerImpl收到来自AudioService的方法之后,就会调用mCallbacks的方法,由于调用的地方是在工作线程,所以在这里通过Handler转化为了UI线程去调用,在对应的实现地方就可以直接改变UI了。

    Callbacks代码如下:

    // frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
    @ProvidesInterface(version = VolumeDialogController.VERSION)
    @DependsOn(target = StreamState.class)
    @DependsOn(target = State.class)
    @DependsOn(target = Callbacks.class)
    public interface VolumeDialogController {
    
        @ProvidesInterface(version = Callbacks.VERSION)
        public interface Callbacks {
            int VERSION = 1;
    
            void onShowRequested(int reason);
            void onDismissRequested(int reason);
            void onStateChanged(State state);
            void onLayoutDirectionChanged(int layoutDirection);
            void onConfigurationChanged();
            void onShowVibrateHint();
            void onShowSilentHint();
            void onScreenOff();
            void onShowSafetyWarning(int flags);
            void onAccessibilityModeChanged(Boolean showA11yStream);
            void onCaptionComponentStateChanged(Boolean isComponentEnabled, Boolean fromTooltip);
        }
    }
    

    3.4 注册VolumeController

    接着来看setDefaultVolumeController,这个比较重要

    // frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
    @Singleton
    public class VolumeDialogComponent implements VolumeComponent, TunerService.Tunable,
            VolumeDialogControllerImpl.UserActivityListener{
        
        private final VolumeDialogControllerImpl mController;
        
        @Inject
        public VolumeDialogComponent(Context context, KeyguardViewMediator keyguardViewMediator,
                VolumeDialogControllerImpl volumeDialogController) {
            mController = volumeDialogController;
            ...
        }
              
        ...  
        @Override
        public void register() {
            mController.register();
        }
        ...
    }
    

    VolumeDialogComponent调用VolumeDialogControllerImpl的方法

    // frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
    @Singleton
    public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable {
        ...
        private AudioManager mAudio;
        protected final VC mVolumeController = new VC();
        ...
        public void register() {
            setVolumeController();
        }
      
        protected void setVolumeController() {
            try {
                mAudio.setVolumeController(mVolumeController);
            } catch (SecurityException e) {
                Log.w(TAG, "Unable to set the volume controller", e);
                return;
            }
        }
      
        private final class VC extends IVolumeController.Stub {
            @Override
            public void volumeChanged(int streamType, int flags) throws RemoteException {
                // 收到AudioService调用的方法
                mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();
            }
        }
        ...
    }
    

    这里调用AudioManager的setVolumeController方法去设置了音量控制的回调接口。

    // frameworks/base/services/core/java/com/android/server/audio/AudioService.java
    public class AudioService extends IAudioService.Stub
            implements AccessibilityManager.TouchExplorationStateChangeListener,
                AccessibilityManager.AccessibilityServicesStateChangeListener {
    
        private final VolumeController mVolumeController = new VolumeController();
                  
        @Override
        public void setVolumeController(final IVolumeController controller) {
            ...
            mVolumeController.setController(controller);
        }
        
        public static class VolumeController {
            private IVolumeController mController;
    
            public void setController(IVolumeController controller) {
                mController = controller;
                mVisible = false;
            }
    
            // 音量发生改变就会调用这个方法
            public void postVolumeChanged(int streamType, int flags) {
                if (mController == null)
                    return;
                try {
                    mController.volumeChanged(streamType, flags);
                } catch (RemoteException e) {
                    Log.w(TAG, "Error calling volumeChanged", e);
                }
            }
            ...
        }
                  
    }
    

    在AudioService里面定义了一个内部类VolumeController,持有IVolumeController的引用,当音量发生改变就会调用VolumeController的方法,然后调用IVolumeController的方法,最终回调到SystemUI的VolumeDialogControllerImpl的VC类中。

    // frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
    @Singleton
    public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable {
        ...
        private final class VC extends IVolumeController.Stub {
            @Override
            public void volumeChanged(int streamType, int flags) throws RemoteException {
                // 收到AudioService调用的方法
                mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();
            }
        }
      
        private final class W extends Handler {
            private static final int VOLUME_CHANGED = 1;
    
            W(Looper looper) {
                super(looper);
            }
    
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case VOLUME_CHANGED: onVolumeChangedW(msg.arg1, msg.arg2); break;
                    ...
                }
            }
        }
      
        boolean onVolumeChangedW(int stream, int flags) {
            final boolean showUI = shouldShowUI(flags);
            final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
            final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
            final boolean showSilentHint = (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0;
            boolean changed = false;
            if (showUI) {
                changed |= updateActiveStreamW(stream);
            }
            int lastAudibleStreamVolume = getAudioManagerStreamVolume(stream);
            changed |= updateStreamLevelW(stream, lastAudibleStreamVolume);
            changed |= checkRoutedToBluetoothW(showUI ? AudioManager.STREAM_MUSIC : stream);
            if (changed) {
                // 调用mCallbacks的onStateChanged方法
                mCallbacks.onStateChanged(mState);
            }
            ...
            return changed;
        }
        
        ...
    }
    

    这里的mWork是通过子线程的Looper去初始化的,所以onVolumeChangedW也是在子线程执行的,那么我们mCallbacks的方法也是在子线程执行的,这里的分析也是和3.3小节的分析对应上了。

    3.5 VolumeUI小结

    这里我们来分析一下VolumeUI整理流程

    1. VolumeUI持有VolumeDialogComponent的引用,在调用VolumeUI的start方法时,会判断音量条和安全音量提示是否打开,然后会去注册AudioService的监听
    2. VolumeDialogComponent的构造函数会去创建音量条实例-VolumeDialogImpl,同时VolumeDialogImpl会去执行一些初始化的操作,同时添加VolumeDialogControllerImpl的监听回调。
    3. 注册AudioService的监听是在VolumeDialogControllerImpl里面注册的,当AudioService进行了调整音量的操作后,VolumeDialogControllerImpl会收到通知,同时会将收到的消息回调给VolumeDialogImpl,做出相应的UI调整,这样就完成了一轮操作。

    整个SystemUI还是比较复杂的,这里只是片面的说明了VolumeUI的代码流程,并没有详细的分析VolumeDialogImpl里面的代码,里面大多都是UI相关的处理,本篇文章侧重去分析了代码流程,如有可以改进的地方还望大家指出,感激不尽~

    相关文章

      网友评论

          本文标题:Android音量调节(二)音量处理及UI刷新

          本文链接:https://www.haomeiwen.com/subject/sxijpltx.html