接上一章,既然要搞插件,那就可以不止一个。参考了一下Nevolution的sdk,可以在manifest加个service把插件类暴露给宿主。
首先是在AndroidManifest.xml
上注册一个service:
1 2 3 4 5 6 7 8
| <service android:name=".Plugin" android:exported="true" tools:ignore="ExportedService"> <intent-filter> <action android:name="soko.ekibun.bangumi.plugins"/> </intent-filter> </service>
|
Plugin
类继承Service
,但并不用服务的功能,和原来一样,留一个setupPlugin
给宿主调用。
宿主中通过PackageManager.queryIntentServices
来获取插件列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| fun createPluginInstance(context: Context): Map<Context, Any> { return context.packageManager.queryIntentServices( Intent("soko.ekibun.bangumi.plugins"), 0 ).distinctBy { it.serviceInfo.packageName }.mapNotNull { try { val pluginContext = context.createPackageContext( it.serviceInfo.packageName, Context.CONTEXT_IGNORE_SECURITY or Context.CONTEXT_INCLUDE_CODE ) val pluginClass = pluginContext.classLoader.loadClass(it.serviceInfo.name) pluginContext to pluginClass.getDeclaredConstructor().let { it.isAccessible = true it.newInstance() } } catch (e: Exception) { e.printStackTrace() null } }.toMap() }
|
在PreferenceFragmentCompat.onBindPreferences
里显示插件列表:
1 2 3 4 5 6 7 8 9 10
| override fun onBindPreferences() { super.onBindPreferences() if (this.preferenceScreen.key == "pref_plugin") { val cat = findPreference<PreferenceCategory>("pref_plugin_list") ?: return cat.removeAll() App.get(context!!).pluginInstance.forEach { plugin -> cat.addPreference(PluginPreference(context, plugin)) } } }
|
PluginPreference
继承SwitchPreference
加上了一个设置按钮:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class PluginPreference(context: Context?, private val plugin: Map.Entry<Context, Any>) : SwitchPreference(context) {
init { val appInfo = plugin.key.applicationInfo key = "use_plugin_${plugin.key.packageName}" title = plugin.key.getString(appInfo.labelRes) icon = plugin.key.applicationInfo.loadIcon(plugin.key.packageManager) summary = plugin.key.packageName setDefaultValue(true) widgetLayoutResource = R.layout.pref_plugin_widget }
override fun onBindViewHolder(holder: PreferenceViewHolder) { super.onBindViewHolder(holder) holder.itemView.item_settings.setOnClickListener { showPreference() } }
private fun showPreference() { context.startActivity( Intent.createChooser( Intent("soko.ekibun.bangumi.plugins.setting").setPackage(plugin.key.packageName), plugin.key.packageName ) ) } }
|
要注意的是插件的Activity
如果不把category
设置成DEFAULT
,宿主没办法隐式调用:
1 2 3 4 5 6 7 8 9
| <activity android:name=".ui.setting.SettingsActivity" android:label="@string/settings" android:exported="true"> <intent-filter> <action android:name="soko.ekibun.bangumi.plugins.setting"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity>
|