在前面的视频、文章中我们介绍完了整个车载Android应用开发所需要的基础知识:
本期内容,我们介绍原生Android Automotive中车载应用的实现方式和它的原理。首先要介绍的就是车载应用开发中非常重要的一个系统应用,Android系统的UI - SystemUI
。
由于原生Android系统的SystemUI
代码量很大、内容也非常庞杂,这里我会挑选出对车载SystemUI
开发具有参考意义的模块进行介绍,大约会有4-5期的内容,主要分为以下几个模块:
SystemUI的源代码可能是所有Android原生应用中最复杂的一个,当我们需要定制SystemUI时,庞大的源码量会对的定制化开发带来巨大的潜在风险。所以目前车载SystemUI常见的做法就是,从原生SystemUI中移植少量必须的源码,然后从头定制一个源码、功能完全可控的SystemUI。
重新开发一个SystemUI就是唯一的选项吗?当然不是!Google官方早就注意到了这个问题,所以SystemUI中提供插件化的开发方式 - SystemUI Plugin。
本文源码地址:/frameworks/base/+/refs/heads/main/packages/SystemUI/plugin/ExamplePlugin/
本文源码环境基于Android 13
SystemUI plugin机制是一种让SystemUI的功能可以被动态替换或修改的方法,它可以让开发者快速创建和迭代SystemUI的原型,而尽可能少的修改SystemUI的主框架。
注意:使用Plugin并不能保证我们完全不需要修改SystemUI的主框架,毕竟需求永远是多变的。
Plugin hooks是一些预定义的插件接口,它们可以让应用实现一些特定的功能,并通过Intent和注解来注册和声明插件的类型和版本。Plugin hooks有多种类型,例如OverlayPlugin, QSFactory, VolumeDialog等,每种类型都有一个对应的action和expected interface,用于标识插件的功能和要求。
Android 13中Plugin hooks预定义接口主要有以下几种:
创建一个AndroidStudio的SystemUI plugin项目,可以参考以下的步骤:
1)编译SystemUIPluginLib.jar
使用Plugin之前我们需要编译出SystemUIPluginLib.jar
,在AOSP源码根目录执行下面的指令。
make SystemUIPluginLib
然后就可以在下面的目录中得到SystemUIPluginLib.jar
out/target/product/emulator_x86/obj/JAVA_LIBRARIES/SystemUIPluginLib_intermediates/javalib.jar
在AOSP的文档中建议使用 frameworks/base/packages/SystemUI/plugin/update_plugin_lib.sh 脚本编译 SystemUIPluginLib.jar,不过我编译时出现了环境配置问题。
2)配置系统签名
在build.gradle中配置系统签名。
android {
...
signingConfigs {
sign {
storeFile file('system.keystore')
storePassword '123456'
keyAlias 'cardemo'
keyPassword '123456'
}
}
buildTypes {
release {
minifyEnabled false
signingConfig signingConfigs.sign
}
debug {
minifyEnabled false
signingConfig signingConfigs.sign
}
}
}
关于如何制作系统签名,请参考:车载Android应用开发与分析 - 开发系统应用 - 掘金
3)创建一个Plugin
在plugin项目中定义一个类,实现自Plugin中已经提供的各种插件,并使用Requires注解声明target和version字段,这些字段用于标识插件的类型和版本。
@Requires(target = OverlayPlugin.class, version = OverlayPlugin.VERSION)
public class SampleOverlayPlugin implements OverlayPlugin {
private static final String TAG = "SampleOverlayPlugin";
private Context mPluginContext;
private View mStatusBarView;
private View mNavBarView;
@Override
public void onCreate(Context sysuiContext, Context pluginContext) {
Log.d(TAG, "onCreate");
mPluginContext = pluginContext;
}
@Override
public void onDestroy() {
if (mInputSetup) {
mStatusBarView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
onComputeInternalInsetsListener);
}
Log.d(TAG, "onDestroy");
if (mStatusBarView != null) {
mStatusBarView.post(() -> ((ViewGroup) mStatusBarView.getParent()).removeView(mStatusBarView));
}
if (mNavBarView != null) {
mNavBarView.post(() -> ((ViewGroup) mNavBarView.getParent()).removeView(mNavBarView));
}
}
@Override
public void setup(View notificationShadeWindowView, View navBar) {
Log.d(TAG, "Setup");
if (notificationShadeWindowView instanceof ViewGroup) {
mStatusBarView = LayoutInflater.from(mPluginContext)
.inflate(R.layout.colored_overlay, (ViewGroup) notificationShadeWindowView, false);
((ViewGroup) notificationShadeWindowView).removeAllViews();
((ViewGroup) notificationShadeWindowView).addView(mStatusBarView);
}
if (navBar instanceof ViewGroup) {
mNavBarView = LayoutInflater.from(mPluginContext)
.inflate(R.layout.colored_overlay, (ViewGroup) navBar, false);
((ViewGroup) navBar).removeAllViews();
((ViewGroup) navBar).addView(mNavBarView);
}
}
}
注意:Android不同版本中SystemUI的代码存在不小的差异,例如:Android13中setup(View statusBar, View navBar)中返回的statusBar实际上是NotificationShadeWindowView。
4)注册Plugin
在plugin项目的AndroidManifest中注册一个service,使用action和permission属性指定插件的接口和权限,这样SystemUI就可以通过Intent找到插件。
的name可以在我们实现的plugin接口中找到。
SystemUI为了保证系统安全,对于plugin的加载,构筑了两道防线:
第一道防线是Build.IS_DEBUGGABLE检查。SysUI 在扫描或加载设备上的任何插件之前,会检查Build.IS_DEBUGGABLE,以确保构建是可调试的。
第二道防线是就是签名权限。所有插件都必须被系统签名且持有com.android.systemui.permission.PLUGIN
权限才能加载其任何代码,否则将记录违规行为,并忽略插件。
5)运行Plugin
将plugin.apk push 到Android 13 模拟器的/system/priv-app/ 目录下,重启。可以看到如下的效果:
本文初试了SystemUI插件机制,在编写本文时发现Plugin相关的资料少的可怜,即使是官方资料有的也过时了。所以就像标题那样,本文只是简单尝试了Plugin,如何使用Plugin来详细定制一个完全符合我们需求的SystemUI呢?这个我们放到以后再写,因为接下来需要先来分析SystemUI Plugin的原理,在资料如此稀少的情况下,不了解原理几乎无法写出符合需求的Plugin。在分析的原理的过程中,我们会逐步补完、理解一些Plugin的概念。
以上就是本文的所有内容,感谢你的阅读,希望对你所有帮助。
参考资料
Sysui plugin
SystemUI Plugin 简介及使用
/SystemUI/docs/plugins.md