安卓

SystemUI插件化机制Plugin

2018-06-28  本文已影响0人  scarecrowtb

1.SystemUI的启动
在SystemServer中startOtherServices中调用startSystemUi方法启动SystemUIService。
SystemUIService在onCreate中

((SystemUIApplication) getApplication()).startServicesIfNeeded();

进一步调用

startServicesIfNeeded(SERVICES);

其中SERVICES是一个服务列表,会依次调用

mServices[i].start();

来启动SystemUI的各种服务,也就是各种SystemUI的子类。

2.加载插件模块

Dependency.get(PluginManager.class).addPluginListener(
         new PluginListener<OverlayPlugin>() {
              private ArraySet<OverlayPlugin> mOverlays;

              @Override
              public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {
                  StatusBar statusBar = getComponent(StatusBar.class);
                  if (statusBar != null) {
                      plugin.setup(statusBar.getStatusBarWindow(),
                              statusBar.getNavigationBarView());
                  }
                 // Lazy init.
                 if (mOverlays == null) mOverlays = new ArraySet<>();
                 if (plugin.holdStatusBarOpen()) {
                     mOverlays.add(plugin);
                     Dependency.get(StatusBarWindowManager.class).setStateListener(b ->
                              mOverlays.forEach(o -> o.setCollapseDesired(b)));
                     Dependency.get(StatusBarWindowManager.class).setForcePluginOpen(
                             mOverlays.size() != 0);

                 }
            }

           @Override
            public void onPluginDisconnected(OverlayPlugin plugin) {
                mOverlays.remove(plugin);
                Dependency.get(StatusBarWindowManager.class).setForcePluginOpen(
                         mOverlays.size() != 0);
           }
        }, OverlayPlugin.class, true /* Allow multiple plugins */);
 mServicesStarted = true;

Dependency.get方法:

    public static <T> T get(Class<T> cls) {
        return sDependency.getDependency(cls);
    }
    protected final <T> T getDependency(Class<T> cls) {
        return getDependencyInner(cls);
    }
    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);
        }
        return obj;
    }

先从mDependencies里去查找,找不到再创建一个Dependency出来,看一下createDependency

    protected <T> T createDependency(Object cls) {
        Preconditions.checkArgument(cls instanceof DependencyKey<?> || cls instanceof Class<?>);

        @SuppressWarnings("unchecked")
        DependencyProvider<T> provider = mProviders.get(cls);
        if (provider == null) {
            throw new IllegalArgumentException("Unsupported dependency " + cls);
        }
        return provider.createDependency();
    }

这里从mProviders里面去找,存在就创建,不存在就抛出异常。这个mProviders是在什么地方初始化的呢?前面讲到mServices[i].start();其中Dependency.class也是在这个mServices中,SystemUI启动的时候会调用其start()@Dependency.java方法

@Override
    public void start() {
mProviders.put(TIME_TICK_HANDLER, () -> {
            HandlerThread thread = new HandlerThread("TimeTick");
            thread.start();
            return new Handler(thread.getLooper());
        });
        mProviders.put(BG_LOOPER, () -> {
            HandlerThread thread = new HandlerThread("SysUiBg",
                    Process.THREAD_PRIORITY_BACKGROUND);
            thread.start();
            return thread.getLooper();
        });
        mProviders.put(MAIN_HANDLER, () -> new Handler(Looper.getMainLooper()));
        mProviders.put(ActivityStarter.class, () -> new ActivityStarterDelegate());
        ......
        // 这里添加了很多class到mProviders中
        ......
      // Put all dependencies above here so the factory can override them if it wants.
      SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);

mProviders 是一个ArrayMap<Object, DependencyProvider>

private final ArrayMap<Object, DependencyProvider> mProviders = new ArrayMap<>();

所以上面的startServicesIfNeeded方法中Dependency.get(PluginManager.class)得到的是PluginManagerImpl对象,调用了void addPluginListener(PluginListener<T> listener, Class<?> cls,
boolean allowMultiple)方法,实际调用到addPluginListener(String action, PluginListener<T> listener,
Class cls, boolean allowMultiple)方法

    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
            Class cls, boolean allowMultiple) {
        if (!isDebuggable) {
            // Never ever ever allow these on production builds, they are only for prototyping.
            return;
        }
        mPluginPrefs.addAction(action);
        PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
                allowMultiple, mLooper, cls, this);
        p.loadAll();
        mPluginMap.put(listener, p);
        startListening();
    }

这个action是调用PluginManager.getAction(cls)得到的
getAction@PluginManager

static <P> String getAction(Class<P> cls) {
        ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
        if (info == null) {
            throw new RuntimeException(cls + " doesn't provide an interface");
        }
        if (TextUtils.isEmpty(info.action())) {
            throw new RuntimeException(cls + " doesn't provide an action");
        }
        return info.action();
    }

实际上是获取类的注解
PluginManagerImpl将listener,PluginInstanceManager 以键值对保存起来,存到mPluginMap中。
看一下startListening()@PluginManagerImpl

    private void startListening() {
        if (mListening) return;
        mListening = true;
        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        filter.addAction(PLUGIN_CHANGED);
        filter.addAction(DISABLE_PLUGIN);
        filter.addDataScheme("package");
        mContext.registerReceiver(this, filter);
        filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
        mContext.registerReceiver(this, filter);
    }

注册了广播监听器,PluginManagerImpl本身就是一个BroadcastReceiver,看看其onReceive方法

@Override
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
            for (PluginInstanceManager manager : mPluginMap.values()) {
                // mPluginMap保存的是listener,PluginInstanceManage键值对
                manager.loadAll();
            }
        } else if (DISABLE_PLUGIN.equals(intent.getAction())) {
            Uri uri = intent.getData();
            ComponentName component = ComponentName.unflattenFromString(
                    uri.toString().substring(10));
            mContext.getPackageManager().setComponentEnabledSetting(component,
                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                    PackageManager.DONT_KILL_APP);
            mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
                    SystemMessage.NOTE_PLUGIN);
        } else {
            Uri data = intent.getData();
            String pkg = data.getEncodedSchemeSpecificPart();
            if (mOneShotPackages.contains(pkg)) {
                int icon = mContext.getResources().getIdentifier("tuner", "drawable",
                        mContext.getPackageName());
                int color = Resources.getSystem().getIdentifier(
                        "system_notification_accent_color", "color", "android");
                String label = pkg;
                try {
                    PackageManager pm = mContext.getPackageManager();
                    label = pm.getApplicationInfo(pkg, 0).loadLabel(pm).toString();
                } catch (NameNotFoundException e) {
                }
                // Localization not required as this will never ever appear in a user build.
                final Notification.Builder nb =
                        new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
                                .setSmallIcon(icon)
                                .setWhen(0)
                                .setShowWhen(false)
                                .setPriority(Notification.PRIORITY_MAX)
                                .setVisibility(Notification.VISIBILITY_PUBLIC)
                                .setColor(mContext.getColor(color))
                                .setContentTitle("Plugin \"" + label + "\" has updated")
                                .setContentText("Restart SysUI for changes to take effect.");
                Intent i = new Intent("com.android.systemui.action.RESTART").setData(
                            Uri.parse("package://" + pkg));
                PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, 0);
                nb.addAction(new Action.Builder(null, "Restart SysUI", pi).build());
                mContext.getSystemService(NotificationManager.class).notifyAsUser(pkg,
                        SystemMessage.NOTE_PLUGIN, nb.build(), UserHandle.ALL);
            }
            if (clearClassLoader(pkg)) {
                Toast.makeText(mContext, "Reloading " + pkg, Toast.LENGTH_LONG).show();
            }
            if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
                for (PluginInstanceManager manager : mPluginMap.values()) {
                    // plugin发生变化
                    manager.onPackageChange(pkg);
                }
            } else {
                for (PluginInstanceManager manager : mPluginMap.values()) {
                    // plugin被移除
                    manager.onPackageRemoved(pkg);
                }
            }
        }
    }

看一下manager.loadAll()

    public void loadAll() {
        if (DEBUG) Log.d(TAG, "startListening");
        mPluginHandler.sendEmptyMessage(PluginHandler.QUERY_ALL);
    }
public void handleMessage(Message msg) {
            switch (msg.what) {
                case QUERY_ALL:
                    if (DEBUG) Log.d(TAG, "queryAll " + mAction);
                    for (int i = mPlugins.size() - 1; i >= 0; i--) {
                        PluginInfo<T> plugin = mPlugins.get(i);
                        mListener.onPluginDisconnected(plugin.mPlugin);
                        if (!(plugin.mPlugin instanceof PluginFragment)) {
                            // Only call onDestroy for plugins that aren't fragments, as fragments
                            // will get the onDestroy as part of the fragment lifecycle.
                            plugin.mPlugin.onDestroy();
                        }
                    }
                    mPlugins.clear();
                    handleQueryPlugins(null);
                    break;
private void handleQueryPlugins(String pkgName) {
            // This isn't actually a service and shouldn't ever be started, but is
            // a convenient PM based way to manage our plugins.
            Intent intent = new Intent(mAction);
            if (pkgName != null) {
                intent.setPackage(pkgName);
            }
            // plugin在AndroidManifest中必须声明为<service>,packageManager读取到所有的service信息
            List<ResolveInfo> result =
                    mPm.queryIntentServices(intent, 0);
            if (DEBUG) Log.d(TAG, "Found " + result.size() + " plugins");
            if (result.size() > 1 && !mAllowMultiple) {
                // TODO: Show warning.
                Log.w(TAG, "Multiple plugins found for " + mAction);
                return;
            }
            for (ResolveInfo info : result) {
                ComponentName name = new ComponentName(info.serviceInfo.packageName,
                        info.serviceInfo.name);
                // 通过ComponentName创建出插件
                PluginInfo<T> t = handleLoadPlugin(name);
                if (t == null) continue;
                mMainHandler.obtainMessage(mMainHandler.PLUGIN_CONNECTED, t).sendToTarget();
                mPlugins.add(t);
            }
        }
 public void handleMessage(Message msg) {
            switch (msg.what) {
                case PLUGIN_CONNECTED:
                    if (DEBUG) Log.d(TAG, "onPluginConnected");
                    PluginPrefs.setHasPlugins(mContext);
                    PluginInfo<T> info = (PluginInfo<T>) msg.obj;
                    mManager.handleWtfs();
                    if (!(msg.obj instanceof PluginFragment)) {
                        // Only call onDestroy for plugins that aren't fragments, as fragments
                        // will get the onCreate as part of the fragment lifecycle.
                        info.mPlugin.onCreate(mContext, info.mPluginContext);
                    }
                    mListener.onPluginConnected(info.mPlugin, info.mPluginContext);
                    break;

终于看到了onPluginConnected的回调。
这里有个关键的方法handleLoadPlugin,这个方法创建出插件并且返回PluginInfo

protected PluginInfo<T> handleLoadPlugin(ComponentName component) {
            // This was already checked, but do it again here to make extra extra sure, we don't
            // use these on production builds.
            if (!isDebuggable) {
                // Never ever ever allow these on production builds, they are only for prototyping.
                Log.d(TAG, "Somehow hit second debuggable check");
                return null;
            }
            String pkg = component.getPackageName();
            String cls = component.getClassName();
            try {
                ApplicationInfo info = mPm.getApplicationInfo(pkg, 0);
                // TODO: This probably isn't needed given that we don't have IGNORE_SECURITY on
                if (mPm.checkPermission(PLUGIN_PERMISSION, pkg)
                        != PackageManager.PERMISSION_GRANTED) {
                    Log.d(TAG, "Plugin doesn't have permission: " + pkg);
                    return null;
                }
                // Create our own ClassLoader so we can use our own code as the parent.
                // 构造类加载器和Context,类加载器加载插件的代码,Context加载插件的资源。
                ClassLoader classLoader = mManager.getClassLoader(info.sourceDir, info.packageName);
                Context pluginContext = new PluginContextWrapper(
                        mContext.createApplicationContext(info, 0), classLoader);
                Class<?> pluginClass = Class.forName(cls, true, classLoader);
                // TODO: Only create the plugin before version check if we need it for
                // legacy version check.
                T plugin = (T) pluginClass.newInstance();
                try {
                    VersionInfo version = checkVersion(pluginClass, plugin, mVersion);
                    if (DEBUG) Log.d(TAG, "createPlugin");
                    return new PluginInfo(pkg, cls, plugin, pluginContext, version);
                } catch (InvalidVersionException e) {
                    final int icon = mContext.getResources().getIdentifier("tuner", "drawable",
                            mContext.getPackageName());
                    final int color = Resources.getSystem().getIdentifier(
                            "system_notification_accent_color", "color", "android");
                    final Notification.Builder nb = new Notification.Builder(mContext,
                            PluginManager.NOTIFICATION_CHANNEL_ID)
                                    .setStyle(new Notification.BigTextStyle())
                                    .setSmallIcon(icon)
                                    .setWhen(0)
                                    .setShowWhen(false)
                                    .setVisibility(Notification.VISIBILITY_PUBLIC)
                                    .setColor(mContext.getColor(color));
                    String label = cls;
                    try {
                        label = mPm.getServiceInfo(component, 0).loadLabel(mPm).toString();
                    } catch (NameNotFoundException e2) {
                    }
                    if (!e.isTooNew()) {
                        // Localization not required as this will never ever appear in a user build.
                        nb.setContentTitle("Plugin \"" + label + "\" is too old")
                                .setContentText("Contact plugin developer to get an updated"
                                        + " version.\n" + e.getMessage());
                    } else {
                        // Localization not required as this will never ever appear in a user build.
                        nb.setContentTitle("Plugin \"" + label + "\" is too new")
                                .setContentText("Check to see if an OTA is available.\n"
                                        + e.getMessage());
                    }
                    Intent i = new Intent(PluginManagerImpl.DISABLE_PLUGIN).setData(
                            Uri.parse("package://" + component.flattenToString()));
                    PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, 0);
                    nb.addAction(new Action.Builder(null, "Disable plugin", pi).build());
                    mContext.getSystemService(NotificationManager.class)
                            .notifyAsUser(cls, SystemMessage.NOTE_PLUGIN, nb.build(),
                                    UserHandle.ALL);
                    // TODO: Warn user.
                    Log.w(TAG, "Plugin has invalid interface version " + plugin.getVersion()
                            + ", expected " + mVersion);
                    return null;
                }
            } catch (Throwable e) {
                Log.w(TAG, "Couldn't load plugin: " + pkg, e);
                return null;
            }
        }

比较重要的两行

 // 构造类加载器和Context,类加载器加载插件的代码,Context加载插件的资源。
ClassLoader classLoader = mManager.getClassLoader(info.sourceDir, info.packageName);
Context pluginContext = new PluginContextWrapper(
                        mContext.createApplicationContext(info, 0), classLoader);

这里的mManager就是PluginManagerImpl,看看它的getClassLoader

    public ClassLoader getClassLoader(String sourceDir, String pkg) {
        if (mClassLoaders.containsKey(pkg)) {
            return mClassLoaders.get(pkg);
        }
        ClassLoader classLoader = new PathClassLoader(sourceDir, getParentClassLoader());
        mClassLoaders.put(pkg, classLoader);
        return classLoader;
    }

默认采用的是PathClassLoader来加载插件的代码

    @Override
    public Context createApplicationContext(ApplicationInfo application, int flags)
            throws NameNotFoundException {
        LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
                flags | CONTEXT_REGISTER_PACKAGE);
        if (pi != null) {
            ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken,
                    new UserHandle(UserHandle.getUserId(application.uid)), flags, null);

            final int displayId = mDisplay != null
                    ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;

            c.setResources(createResources(mActivityToken, pi, null, displayId, null,
                    getDisplayAdjustments(displayId).getCompatibilityInfo()));
            if (c.mResources != null) {
                return c;
            }
        }

        throw new PackageManager.NameNotFoundException(
                "Application package " + application.packageName + " not found");
    }

c.setResources,可以看到Context中包含了插件的资源信息。最后封装成一个PluginContextWrapper,构造出pluginContext,然后再和plugin一起封装成PluginInfo返回。

上一篇下一篇

猜你喜欢

热点阅读