车机开发之语音中界面显示
前言
随着汽车的智能化,车内语音的重要性自然就提升了,这次先不说语音的工作原理,先讲一下语音工作中遇到的一个问题和解决方法;之前做App,基本没想过没有界面的app的工作原理,也只是了解过像推送这种确实不需要界面的功能;现在做了语音开发之后,对service和window有了进一步的认识;
背景
语音确实是不需要界面的,只是运行的一个或者多个service;最近做了一个功能,就是更新语音资源的一个功能,跟我们做app时候的版本更新是差不多的,下载,版本控制这些就不说了,主要是看看做app很少遇到的问题和解决方法;这里面有几个问题需要解决:1,启动的时候如果有更新,怎么展示界面;2,这个展示的界面怎么去启动;
接下来就把我的解决方案一一展示;
第一怎么展示界面:方案用windowmanager;第二怎么去启动这个UI:用service;
总的来说就是,启动一个service,然后service中启动一个windowmanager来展示ui;
接下来分开来讲一下:
service
大概讲一下可能遇到的问题和面试遇到的问题,其他相信的网上一搜一堆;
启动方式
第一种:startservice
当外部调用了stopService()或stopSelf()方法时,该Service才会停止运行并销毁
当系统资源不足时, 会回收一些不重要的service,service被系统回收也会停止运行并被销毁
onStartCommand()
如果多次执行了Context的startService()方法,那么Service的onStartCommand()方法也会相应的多次调用。
onBind()
Service中的onBind()方法是抽象方法,Service类本身就是抽象类,所以onBind()方法是必须重写的,即使我们用不到。
onDestory()
在销毁的时候会执行Service该方法。
第二种:bindservice
特点:
bindService启动的服务和调用者之间是典型的client-server模式。调用者是client,service则是server端。service只有一个,但绑定到service上面的client可以有一个或很多个。这里所提到的client指的是组件,比如某个Activity。
client可以通过IBinder接口获取Service实例,从而实现在client端直接调用Service中的方法以实现灵活交互,这在通过startService方法启动中是无法实现的。
bindService启动服务的生命周期与其绑定的client息息相关。当client销毁时,client会自动与Service解除绑定。当然,client也可以明确调用Context的unbindService()方法与Service解除绑定。当没有任何client与Service绑定时,Service会自行销毁
WindowManager
接下来我们讲一下用windowmanager来展示ui;
基本原理
Window表示一个窗口的概念,在日常开发中直接接触的机会不多,但是在某些特殊时候需要在桌面上显示一个界面,就要用到Window实现。它是一个抽象类,具体实现是PhoneWindow。创建一个Window很简单,通过WindowManager即可。WindowManager是外界访问Window的入口,WindowManager和WindowManagerService的交互是一个IPC过程。Android中所有的视图都是通过Window呈现的,不管是Activity、Dialog等,都是附加在Window上的,所以Window是View的直接管理者。View事件分发机制中单击事件也是由Window传给DecorView再传给我们的View,就连setContentView在底层也是通过Window完成。
用法
演示通过WindowManager添加Window的过程
WindowManager.LayoutParams mLpWindowMain = new WindowManager.LayoutParams();
mLpWindowMain.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;//LayoutParams.TYPE_PHONE;
mLpWindowMain.format = PixelFormat.RGBA_8888;
mLpWindowMain.gravity = Gravity.LEFT | Gravity.TOP;
mLpWindowMain.width = WindowManager.LayoutParams.WRAP_CONTENT;
mLpWindowMain.height = WindowManager.LayoutParams.WRAP_CONTENT;
mLpWindowMain.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
mLpWindowMain.flags =
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
// WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mLlParent = mView.findViewById(R.id.rl_parent);
tvTip = mView.findViewById(R.id.tvTip);
tvprogress = mView.findViewById(R.id.tvprogress);
tvUpdateTip = mView.findViewById(R.id.tvUpdateTip);
tvKnow = mView.findViewById(R.id.tvKnow);
pb = mView.findViewById(R.id.pb);
mWindowManager.addView(mLlParent, mLpWindowMain);
在 左上角加了一个自定义布局view;
注意
1.LayoutParams中的_type参数不能为0,是不合理的
type: 1-99 应用Window层
type: 1000=1999 子Window
type: 2000-2999 系统层 (最顶层)
2.Context对象不能使用ApplitionContext,不管是View还是WindowManager,都只能直接或间接地使用Context而不能使用ApplitionContext。
LayoutParams中的flags和type
flags表示Window的属性,它的选项可以控制Window的显示特性(有很多类型,可以去官方API文档去看,这里只介绍常用的
FLAG_NOT_FOCUSABLE
表示Window不需要获取焦点,也不需要接收各种输入事件,此标记会同时启用FLAG_NOT_TOUCH_MODAL,最终事件会直接传递给下层的具有焦点的Window
FLAG_NOT_TOUCH_MODAL
系统会将当前Window区域以外的单击事件传递给底层的Window,当前Window区域以内的单击事件则自己处理。一般来说都需要开启此标记,否则其他Window无法收到单击事件
FLAG_SHOW_WHEN_LOCKED
开启此模式可以让Window显示在锁屏的界面上。
TYPE
type参数表示Window的类型,Window有三种类型,分别是应用Window、子Window、系统Window。 应用类Window对应着一个Activity
子Window不能单独存在,它需要附属在特定的父Window之中,比如一些常见的Dialog就是一个子Window
系统Window是需要声明权限才能创建的Window,比如Toast和系统状态栏都是系统View
Window是分层的,每个Window都有对应的z-ordered,层级大的会覆盖在层级小的Window上面。在三类Window中
type: 1-99 应用Window层
type: 1000 -1999 子Window
type: 2000-2999 系统层 (最顶层)
系统层级有很多值,一般可以选用TYPE_SYSTEM_OVERLAY或者TYPE_SYSTEM_ERROR,如果采用ERROR只需要为type参数指定这个层级即可,同时声明权限
< uses-permission android:name=“android.permission.SYSTEM_ALERT_WINDOW”/>
因为系统类型的Window是需要检查权限的。
Window的内部机制
它是一个抽象概念,每一个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系,因此Window不是实际存在的,它以View的形式存在。View才是Window存在的实体,对它的访问必须通过WindowManager。
这里只简单说一下怎么用和一些基础概念,下次深入讲一下window内部机制;
感谢老铁阅读,对你有帮助,欢迎点赞收藏。接下来想讲一下车机端语音是怎么保活的。