android_easytouch(下)

上一次讲到如何将我们的 easy touch 显示到桌面上,但是还存在一些细节没有以及无障碍服务没有说清楚,所以这一篇将剩下的都说完。

Context

在上一篇中,我们介绍了获取WindowManager服务所用的语句

windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);

但是有一点需要注意,这里的mContext不是某个Activity的Context,而是整个应用的Context,也就是ApplicationContext。那么首先介绍Context:

它是与应用程序环境的全局信息的接口。这是一个抽象类,其实现是由Android系统提供的。它允许访问特定应用程序的资源和类,以及对应用程序级别操作(如启动活动、广播和接收意图等)的调用。

老实说这个解释我没怎么看明白,但是结合某些事情,我们就可以明白。他的间接子类中有Application, Activity,Service,我们经常在activity中通过传this构造一些view,dialog等UI控件,其实这些控件所需要的Context就是activity.this,因为从面向对象的概念来说,activity就是一个Context。

ApplicationContext

说到这个ApplicationContext,其实并没有这个类,但是因为Application也是一个context,所以我们这么称呼这个context,它和Activity的区别我们通过context来讨论。

生命周期

关于activity的生命周期,想必大家应该很熟悉,我们在onCreate的时候就可以初始化一些view,这时context就已经存在了,直到最后,onDestroy被调用的时候,context就消失了,这里有一点需要注意:
我们不能在activity不出现在前台的时候使用它的context去操纵一些view,经常出现的错误就是当activity不在前台的时候调用dialog的show,这时候就会报错。当我们在activity点击back的时候activity就会被销毁,当activity再次onCreate的时候,这里的context就会是一个新的context。

区别于activity,application的生命周期就是对于整个应用来说的,在这个程序中我们就使用了这个特性,只要这个应用的进程还在执行,ApplicationContext就还存在,也就是说,我们的easy touch作为view,并不依赖于activity,而是依赖于ApplicationContext,虽然ApplicationContext是通过Context.getApplication()来获取到的,但是我们只需要获取一次就行,并且一直使用这个ApplicationContext对easy touch进行管理。activity的作为context可能会因为生命周期的结束和重新开始而发生变化,但是ApplicationContext就不会,其实,通过activity这个context多次获取ApplicationContext也没关系,但是没有意义。


介绍完这两个,我聊一下我因为没有弄清楚context概念的时候所范的错误。

java.lang.IllegalArgumentException: View not attached to window manager

view没有附加到window manager上,我当时一头雾水,这什么玩意。百度里有个解释是当activity onCreate的时候先销毁view,再重新创建view,我发现可以运行,但是还是不懂啊。。。我决定把这个问题搞清楚。
为什么会出现这个问题?我的view怎么就没有附加到window上了。我把关注点放在了context的变化上,然后发现,activity因为生命周期的原因,context不断变化,但是applicatioContext是一直在的,当我将这两个搞清楚之后,发现这个问题依然存在,然后发现我多次new了view,所以出现了这样的问题,这个问题算是低级错误了,这里不继续说了。

问题解决了,下面我们开始继续了解关于无障碍服务的问题。

AccessibilityService

Accessibility services should only be used to assist users with disabilities in using Android devices and apps.

用于帮助残疾的用户使用android设备和apps,其实ios上的小白点设计初衷也是为了帮助残疾用户更好的使用手机,所以我现在做的应用也是蛮符合初衷的。我们使用这个easy touch完成哪些功能呢,我举个例子,目前android主流手机和版本都有home,back,recents这三个按键,要么是物理按键,要么是虚拟按键,我们就是将这三个按键功能复制到小白点上(另外的许多系统级别的功能也可以做,例如手电筒,wifi,浏览器,相机等等,这里并未提及),如果用户的物理按键损坏,也可以不用打开虚拟按键正常使用手机。

好了,我们如何使用这个服务呢,一步一步来:

  1. 创建服务类

    我们需要创建一个服务类,这个类需要继承自AccessibilityService,并且实现几个覆盖几个函数 onAccessibilityEvent(AccessibilityEvent event)及onInterruput()。

    onInterruput

    Callback for interrupting the accessibility feedback.
    用于中断accessibility 反馈的回调

    其实就是当accessibility被中断时会这个函数会被回调。

    onAccessibilityEvent(AccessibilityEvent event)

    Callback for AccessibilityEvents.

    AccessibilityEvent: The new event. This event is owned by the caller and cannot be used after this method returns. Services wishing to use the event after this method returns should make a copy.

    这里的event指的是例如window点击事件,通知时间,特定手势等等。我们可以在这里event来进行一些动作。
    除了这两个,还有一个需要使用的函数得说一下。

    onServiceConnected

    This method is a part of the AccessibilityService lifecycle and is called after the system has successfully bound to the service. If is convenient to use this method for setting the AccessibilityServiceInfo.

    当系统服务成功绑定我们的服务时,这个方法就会回调。在这个应用中,我使用它作为是否可以使用back键的依据。另外,在这个方法中,我们还可以对AccessibilityServiceInfo进行设置。

  2. 配置Androidmanifest.xml文件

    官方配置

    <service android:name=".MyAccessibilityService"
         android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
     <intent-filter>
         <action android:name="android.accessibilityservice.AccessibilityService" />
     intent-filter>
    service>

    下面的是我的配置

    <service
      android:name=".service.MyService"
      android:enabled="true"
      android:exported="true"
      android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
      <intent-filter>
          <action android:name="android.accessibilityservice.AccessibilityService"/>
      intent-filter>
      <meta-data
          android:name="android.accessibilityservice"
          android:resource="@xml/accessibility_service_config" />
    service>

    我们是为服务添加权限的,并且action的值是固定的,另外需要提及的是meta-data,其实官方配置中也有,但是这个不是强制需要的,可以通过setServiceInfo(AccessibilityServiceInfo)进行同样的配置,这里不多提。关于meta-data的配置文件配置哪些内容,这里也不多说。

  3. 服务的开启

    这个服务的开启是需要引导用户进入设置界面进行手动授权的,通过这样一句代码进行引导

    context.startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));

    当用户点击打开服务按钮的时候,onServiceConnected就会被调用,这时就正式启动了。

    关于检测无障碍服务是否启动的问题,我在网上找到了这样的方法,仅作参考(并不能用,笑)
    我们可以使用AccessibilityManager来检查当前已安装到系统的服务列表,或者检查当前已经启动的服务列表

    AccessibilityManager am = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);
    List serviceInfos = am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC);
    List installedAccessibilityServiceList = am.getInstalledAccessibilityServiceList();
    for (AccessibilityServiceInfo info : installedAccessibilityServiceList) {
      Log.d(TAG, info.getId());
    }

    看这段代码,我们可以看到两个list,第一个list是所有正在运行的AccessibilityServiceInfo,看样子是可以通过getId()找到本应用的服务是否启用,但是这个独独就是不能检测app本身开启的服务(很坑吧),第二个list可以找到系统当前已安装的服务,不管有没有开启,所以这个也不行。对于这个问题,我的解决方法就是通过服务的onServiceConnected和onUnbind来做标记,通过在这两个方法中改变某个静态变量的值,接着就可以通过这个变量的值来判断服务是否运行。

好了,介绍完了AccessibilityService,我们说一说如何完成功能。

我了解到,在AccessibilityService中,有一个方法

boolean performGlobalAction (int action)

action :
  GLOBAL_ACTION_BACK
  GLOBAL_ACTION_HOME
  GLOBAL_ACTION_NOTIFICATIONS
  GLOBAL_ACTION_RECENTS

通过这个方法,我们就可以模拟点击效果了,我们通过Eventbus将view中发生的事件传递到服务中来处理,完成这个功能。

在了解其他app可以不开启无障碍服务就使用home键的功能,我得研究研究了。

(未完待续)

你可能感兴趣的:(android学习笔记)