赞
踩
在前面的视频、文章中我们介绍完了整个车载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找到插件。
<uses-permission android:name="com.android.systemui.permission.PLUGIN" />
<application>
<service
android:name=".SampleOverlayPlugin"
android:exported="false"
android:label="@string/plugin_label"
tools:ignore="Instantiatable">
<intent-filter>
<action android:name="com.android.systemui.action.PLUGIN_OVERLAY" />
</intent-filter>
</service>
</application>
的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的概念。
以上就是本文的所有内容,感谢你的阅读,希望对你所有帮助。
参考资料
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。