摘要
在Android程序的开发维护过程中,我们可能经常需要知道自己所看到的界面处于哪一个Activity中,相信大部分程序员的做法是“在基类里打Log”,很传统没毛病o(╯□╰)o....
前几天,在应用宝的应用商店内发现了一个很有意思的APP - 当前Activity,可以显示出当前界面上显示的 应用包名 和 Activity类名。然后,一直很好奇是如何实现的,最近抽时间研究了一下。
下面是研究成果:
image_example.png开源项目CurrentActivity地址:
https://github.com/sinawangnan7/CurrentActivity
欢迎Star...(๑•̀ㅂ•́)و✧
正文
本文主要讲述两部分内容:
1、CurrentActivity的使用场景
2、CurrentActivity的实现思路
CurrentActivity的使用场景
1、定位自己APP的页面,获取类名称
这应该是我们经常碰到的问题,测试人员经常拿着出Bug的页面询问“开发人员”,如果这个页面还不是“此开发人员”写的,估计查起来就费劲了。此工具能帮助开发人员提高查找效率。
2、分析其他APP命名及功能
可以查看其他APP的页面路径和命名,给自己开发做个参考(下面以微信和支付宝为例)。
(1) 微信
wx.png观察上图,可以看到 微信 的Activity在命名时是以 "UI" 为后缀进行标识的,类在分包时也放在了ui包目录下。另外,还有一个比较有意思的发现,微信的“主界面”和“聊天界面”用的是同一个Activity。
(2) 支付宝
alipay.png笔者公司开发的APP和支付宝APP类似,也是做 互联网金融 的。所以,产品经理在设计功能和流程时经常会“参考”支付宝是如何实现的,比如需要确定 哪些页面用原生做,哪些需要用H5来做。但是,支付宝的原生和H5在使用体验上很难感知出来(这里支付宝做得很赞),这时这个工具就可以派上用场了,支付宝展示H5的页面用的是H5Activity。
最近,笔者需要做一个类似支付宝的支付键盘,一开始以为是纯Dialog实现的,最后发现实际上用Activity包了一层。这其实也并不意外,第三方APP在吊起“支付宝支付”时,用户如果安装了支付宝,吊起的也是这个FlyBirdWindowActivity。
CurrentActivity的实现思路
这个监视活动的窗口是如何实现的?
本文只讲述 思路和核心代码,完整代码请参看GitHub开源项目:
CurrentActivity
实现这个监视窗口需要解决两个问题:
-
1、如何向界面顶部添加一个显示窗口?
-
2、如何监听应用切换、Activity切换?
第一个问题很简单,通过WindowManager添加一个TextView即可, 但是需要注意下悬浮窗权限问题,这个问题很多博客都给出了解决方案,笔者简单说下,就不在赘述了。
笔者做了一个简单的窗口视图管理器:
创建窗口的核心代码如下:
/**
* 添加窗口视图
*/
private void addView() {
// 创建布局参数
mParams = new WindowManager.LayoutParams();
// 获取窗口管理器
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
// 设置类型
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Android O 以上,使用TYPE_APPLICATION_OVERLAY弹窗类型
mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
// Android O 以下,使用TYPE_SYSTEM_ALERT弹窗类型
mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
}
// 设置标签(FLAG_NOT_FOCUSABLE表示窗口不会获取焦点;FLAG_NOT_TOUCHABLE表示窗口不会接收Touch事件,即将Touch事件向下层分发)
mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
// 设置位图模式 (PixelFormat.RGBA_8888可以使背景透明。不设置默认PixelFormat.OPAQUE,即不透明)
mParams.format = PixelFormat.RGBA_8888;
// 设置分布位置(距左对齐 + 距顶对齐)
mParams.gravity = Gravity.LEFT | Gravity.TOP;
// 设置布局宽/高为自适应
mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
// 添加TextView
mWindowManager.addView(mTextView, mParams);
// 记录视图已被添加、显示
isAdded = true;
isShow = true;
}
提醒:
1.注意悬浮窗权限问题,可参考 AndroidManifest.xml 中的权限声明。
2.适配Android 8.0,请使用TYPE_APPLICATION_OVERLAY弹窗类型。
第二个问题:如何监听应用切换、Activity切换?笔者查了很长时间,最后发现了一个叫 AccessibilityService(辅助服务)的东西,了解完 AccessibilityService 感觉好像发现了新大陆,其实这个“辅助服务”使用最为出名的是“微信自动抢红包插件”。但是,今天笔者只是简单介绍下AccessibilityService(辅助服务)的入门使用 - 监听窗口改变。
辅助服务创建步骤及使用流程
1.创建自定义AccessibilityService,核心代码如下:
/**
* @ClassName: MAccessibilityService
* @Description: 辅助服务
* @Author wangnan7
* @Date: 2018/4/1
*/
public class MAccessibilityService extends AccessibilityService {
......
/**
* 服务连接完成
*/
@Override
protected void onServiceConnected() {
// 添加窗口
mWindowViewContainer = WindowViewContainer.getInstance(this);
mWindowViewContainer.addWindowView();
......
}
......
/**
* 接收辅助服务事件
*/
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (event == null) {
return;
}
switch (event.getEventType()) {
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: // 窗口状态改变
if (event.getPackageName() != null && event.getClassName() != null) {
// 更新窗口视图
mWindowViewContainer.updateWindowView(event.getPackageName() + "\n" + event.getClassName());
}
break;
default:
break;
}
}
/**
* 服务中断
*/
@Override
public void onInterrupt() {
}
/**
* 服务退出
*/
@Override
public void onDestroy() {
// 移除窗口,销毁视图容器
mWindowViewContainer.destory();
......
}
}
整个功能的核心都在onAccessibilityEvent(AccessibilityEvent event)
这个方法中,如果开启辅助服务,只要服务不死,进程不挂,当前窗口的每次改变我们都能监听到。(如果你查看了完整代码,会看到笔者添加了一个Notification,把辅助服务提升为了前台服务,这么做只是简单的做下服务保活)
2.在AndroidManifest.xml文件中声明该服务, 核心代码如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.wangnan.currentactivity">
......
<application
......
<!-- 辅助服务 -->
<service
android:name=".service.MAccessibilityService"
android:label="当前Activity(辅助工具)"
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>
......
</application>
</manifest>
完整代码:AndroidManifest.xml
详细说下辅助服务的配置声明:
1.android:name 用于指定服务名称,前面加“.”表示书写时省略掉了包名。
2.android:lable 用于指定外部显示的服务名称。
3.android:permission 表明服务向系统请求那些权限
4.intent-filter 的 action 表明传递给该服务的哪些Intent需要过滤出来进行处理
可能会有人疑问为什么要配置3和4,其实这是源码文档要求的,是必须配置的,如下图所示:
source_code.png
5.meta-data 元数据,用于配置辅助服务的详情信息
其实这一项在manifest文件中是可配可不配的,只是文档支持在manifest文件里进行配置,如果不在这配置就需要到辅助服务的Java代码里去配置了,源码解释如下:
source_code2.png
接下来,看下笔者的元数据配置文件(android:resource="@xml/accessibility")
完整代码:accessibility.xml
元数据配置文件说明,如下图所示:
source_code3.png3.开启辅助服务
开启辅助服务不能用startService()
这种方式打开,因为使用辅助服务是有一定风险的,需要用户主动授权(同意开启)。
辅助服务打开流程:
安装APP(含有辅助服务) -> 辅助功能 -> 服务(点击需要开启的服务) -> 开启
以笔者的手机(360N5)和 CurrentActivity 为例,打开流程如下图所示:
image_open.png4.关闭辅助服务
辅助功能 -> 服务(点击需要关闭的服务) -> 关闭
以笔者的手机(360N5)和 CurrentActivity 为例,关闭流程如下图所示:
image_close.pngCurrentActivity的详细使用方法和源码请参看Github:
其他
1.AccessibilityService位于在系统设置里,有些手机翻译成“辅助服务”,有些手机翻译成“无障碍”。
2.AccessibilityService被用户授权开启后并不是一直保活的,也有可能被系统销毁。
题外话
辅助服务其实还有很多使用场景。比如,从上图笔者手机应用列表里看到的云服务、360手机助手(下载自动安装)、京东金融(手环社交软件提醒)。另外,还有我们经常听说的微信自动抢红包、微信自动回复...各位如果有兴趣可以研究下。Good Luck~
最后的福利(2018年4月16号添加):
写完这篇博客后,笔者又研究了下微信抢红包原理,写了一个【微信自动抢红包】插件,原理也是使用辅助服务,开源项目地址:
https://github.com/sinawangnan7/WXGiftMoney
欢迎Star...(๑•̀ㅂ•́)و✧
网友评论