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

前言

第一篇: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相关的处理,本篇文章侧重去分析了代码流程,如有可以改进的地方还望大家指出,感激不尽~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容