起因:

最近做的APP中有一个新功能:已知用户微信号,可点击直接跳转到当前用户微信聊天窗口页面。

当时第一想法是使用无障碍来做,并且觉得应该不难,只是逻辑有点复杂。没想到最终踩了好多坑,特地把踩过的坑记录下来。

实现逻辑:

在APP中点击按钮→跳转到微信界面→模拟点击微信搜索按钮→在微信搜索页面输入获取的微信号→模拟点击查询到的用户进入用户聊天界面。

效果图:

实现过程:

跳转微信按钮点击事件:

 1 jumpButton.setOnClickListener(new View.OnClickListener() {
 2         @Override
 3         public void onClick(View view) {
 4               Intent intent = new Intent(Intent.ACTION_MAIN);
 5               ComponentName cmp = new ComponentName("com.tencent.mm", "com.tencent.mm.ui.LauncherUI");
 6               intent.addCategory(Intent.CATEGORY_LAUNCHER);
 7               intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 8               intent.setComponent(cmp);
 9               startActivity(intent);
10         }
11    });

无障碍监听主要方法:

一些必要的参数:

 1     /**
 2      * 微信主页面的“搜索”按钮id
 3      */
 4     private final String SEARCH_ID = "com.tencent.mm:id/ij";
 5
 6     /**
 7      * 微信主页面bottom的“微信”按钮id
 8      */
 9     private final String WECHAT_ID = "com.tencent.mm:id/d3t";
10
11     /**
12      * 微信搜索页面的输入框id
13      */
14     private final String EDIT_TEXT_ID = "com.tencent.mm:id/ka";
15
16     /**
17      * 微信搜索页面活动id
18      */
19     private String SEARCH_ACTIVITY_NAME = "com.tencent.mm.plugin.fts.ui.FTSMainUI";
20
21     private String LIST_VIEW_NAME = "android.widget.ListView";

微信组件的id之前有博客说过如何获取,所以在此就不重复说明了。

监听主要方法:

 1     @Override
 2     public void onAccessibilityEvent(AccessibilityEvent event) {
 3         List<AccessibilityNodeInfo> searchNode = event.getSource().findAccessibilityNodeInfosByViewId(SEARCH_ID);
 4         List<AccessibilityNodeInfo> wechatNode = event.getSource().findAccessibilityNodeInfosByViewId(WECHAT_ID);
 5
 6         if (searchNode.size() > 1) {
 7             // 点击“搜索”按钮
 8             if (searchNode.get(0).getParent().isClickable()) {
 9                 searchNode.get(0).getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
10                 return;
11             }
12         } else if (searchNode.size() == 1) {
13             // 如果在“我”页面,则进入“微信”页面
14             for (AccessibilityNodeInfo info : wechatNode) {
15                 if (info.getText().toString().equals("微信") && !info.isChecked()) {
16
17                     if (info.getParent().isClickable()) {
18                         info.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
19                         return;
20                     }
21                     break;
22                 }
23             }
24         }
25
26         // 当前页面是搜索页面
27         if (SEARCH_ACTIVITY_NAME.equals(event.getClassName().toString())) {
28             List<AccessibilityNodeInfo> editTextNode = event.getSource().findAccessibilityNodeInfosByViewId(EDIT_TEXT_ID);
29
30             if (editTextNode.size() > 0) {
31                 // 输入框内输入查询的微信号
32                 Bundle arguments = new Bundle();
33                 arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, Constant.wechatId);
34                 editTextNode.get(0).performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
35             }
36         } else if (LIST_VIEW_NAME.equals(event.getClassName().toString())) {
37             // 如果监听到了ListView的内容改变,则找到查询到的人,并点击进入
38             List<AccessibilityNodeInfo> textNodeList = event.getSource().findAccessibilityNodeInfosByText("微信号: " + Constant.wechatId);
39             if (textNodeList.size() > 0) {
40                 textNodeList.get(0).getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
41             }
42         }
43
44     }

这是最原始的版本,具体逻辑已在注释中说明。

遇到的坑:

1. 搜索内容无法赋值给搜索框

最开始以为是赋值的方法有问题,但是在调试状态下能够赋值成功。因此猜测是因为UI加载太慢的缘故。

在搜索框还没完全加载完全的时候就进行了赋值,因此赋值不成功。

解决办法:

在赋值之前停顿300ms,在30行赋值前先停顿300ms。

1 try {
2     Thread.sleep(300);
3 } catch (InterruptedException e) {
4     e.printStackTrace();
5 }

2. 如何停止监听?

由于监听是一直会进行的,因此只要进入了微信页面就会执行无障碍方法。这是不合理的。理论上应该在点击按钮进入微信才开始监听,而查找到好友之后就停止监听。

解决办法:

可以设置全局的变量用来控制监听。需要在点击按钮设置变量值为监听,而查找到微信好友之后设置为不监听。

全局变量:

 1 public class Constant {
 2
 3     /**
 4      * 判断是否需要监听
 5      */
 6     public static int flag = 0;
 7
 8     /**
 9      * 微信号
10      */
11     public static String wechatId;
12 }

按钮点击修改flag值:

 1 jumpButton.setOnClickListener(new View.OnClickListener() {
 2     @Override
 3     public void onClick(View view) {
 4         Intent intent = new Intent(Intent.ACTION_MAIN);
 5         ComponentName cmp = new ComponentName("com.tencent.mm", "com.tencent.mm.ui.LauncherUI");
 6         intent.addCategory(Intent.CATEGORY_LAUNCHER);
 7         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 8         intent.setComponent(cmp);
 9         startActivity(intent);
10
11         Constant.flag = 1;
12         Constant.wechatId = editText.getText().toString();
13     }
14 });

根据flag判断是否需要监听:

在无障碍服务的监听方法中开始位置判断,

1 // 只有从app进入微信才进行监听
2 if (Constant.flag == 0) {
3     return;
4 }

查询到结果后修改flag值:

1 // 如果监听到了ListView的内容改变,则找到查询到的人,并点击进入
2 List<AccessibilityNodeInfo> textNodeList = event.getSource().findAccessibilityNodeInfosByText("微信号: " + Constant.wechatId);
3 if (textNodeList.size() > 0) {
4     textNodeList.get(0).getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
5
6     // 模拟点击之后将暂存值置空,类似于取消监听
7     Constant.flag = 0;
8     Constant.wechatId = null;
9 }

3. 没查询到结果如何停止监听?

想必大家都发现了,上面的处理方法还没有考虑到未查询到好友的情况。那么,未查询到好友如何停止监听呢?

最开始想的是找到未查询页面,只要知道了什么情况是未查询的,那就可以停止监听了。

但是未查询到好友的页面查找比较麻烦,因此想了一个取巧的办法。

解决办法:

写一个线程,两秒后执行,因为用户一般在未查询到结果页面会停留至少两秒,两秒误操作就停止监听。

线程实现(线程得是类持有的,而不应该是方法持有的):

1 Handler handler = new Handler();
2 Runnable runnable = new Runnable() {
3     @Override
4     public void run() {
5         Constant.flag = 0;
6         Constant.wechatId = null;
7     }
8 };

监听方法内进行线程的开启操作:

1 // 两秒后如果还没有任何的事件,则停止监听
2 handler.removeCallbacks(runnable);
3 handler.postDelayed(runnable, 2000);

由于无障碍的监听方法会反复执行,因此为了保证其正确性,需要保证在最后一次事件才开始计时。

4. 如果在微信其他页面怎么办?

最开始被这个问题难住了。后来产品给了我一个思路,其实很简单,如果判断当前页面并不是微信主页面的话,就执行全局返回按钮事件就行。

解决办法:

如果是页面改变事件,并且当前页面不是主页面也不是搜索页面(搜索页面就可以直接搜索了)的话,就执行全局返回键。

1 if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && !LAUNCHER_ACTIVITY_NAME.equals(event.getClassName().toString()) && !SEARCH_ACTIVITY_NAME.equals(event.getClassName().toString())) {
2     // 如果当前页面不是微信主页面也不是微信搜索页面,就模拟点击返回键
3     performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
4     return;
5 }

5. 页面改变UI加载太慢

在解决上述问题时,又遇到了之前遇到的问题,UI加载太慢的问题,因此需要在每次页面改变事件中都得加上300ms的延迟时间。

解决办法:

1 // 页面改变时需要延迟一段时间进行布局加载
2 if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
3     try {
4         Thread.sleep(300);
5     } catch (InterruptedException e) {
6         e.printStackTrace();
7     }
8 }

6. 聊天界面和主页面是同一个活动

解决了上述问题之后,又遇到了一个新的问题,经常性的返回到聊天页面就不返回了。

经过调试,发现聊天页面的活动和微信主页面的活动是同一个。

解决办法:

对聊天界面单独做处理,根据聊天界面左上角UI存在不存在来确定是否为聊天界面。

 1 if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && !LAUNCHER_ACTIVITY_NAME.equals(event.getClassName().toString()) && !SEARCH_ACTIVITY_NAME.equals(event.getClassName().toString())) {
 2     // 如果当前页面不是微信主页面也不是微信搜索页面,就模拟点击返回键
 3     performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
 4     return;
 5 } else if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && LAUNCHER_ACTIVITY_NAME.equals(event.getClassName().toString())) {
 6     List<AccessibilityNodeInfo> list = event.getSource().findAccessibilityNodeInfosByViewId(USERNAME_ID);
 7     if (list.size() > 0) {
 8         // 如果是微信主页面,但是是微信聊天页面,则模拟点击返回键
 9         performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
10         return;
11     }
12 }

其中USRENAME_ID为左上角备注部分的UIid。

7. 搜索不到结果时,发现他在搜索结果页面乱跳

经排查,发现搜索结果页面中的搜索布局提示布局id和首页面的搜索按钮id一致,因此就执行了点击搜索按钮的方法。

解决办法:

对于搜索按钮页面(主页面)也要进行单独判断,由于主页面一定有ViewPage布局,因此只要找到ViewPage那就证明是在主页面。

 1 List<AccessibilityNodeInfo> searchNode = event.getSource().findAccessibilityNodeInfosByViewId(SEARCH_ID);
 2 List<AccessibilityNodeInfo> wechatNode = event.getSource().findAccessibilityNodeInfosByViewId(WECHAT_ID);
 3 List<AccessibilityNodeInfo> viewPageNode = event.getSource().findAccessibilityNodeInfosByViewId(VIEW_PAGE_ID);
 4
 5 Log.e(TAG, "searchNode:" + searchNode.size());
 6 Log.e(TAG, "viewPageNode:" + viewPageNode.size());
 7
 8 // 由于搜索控件在多个页面都有,所以还得判断是否在主页面
 9 if (searchNode.size() > 1 && viewPageNode.size() > 0) {
10     // 点击“搜索”按钮
11     if (searchNode.get(0).getParent().isClickable()) {
12         searchNode.get(0).getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
13         return;
14     }
15 } else if (searchNode.size() == 1) {
16     // 如果在“我”页面,则进入“微信”页面
17     for (AccessibilityNodeInfo info : wechatNode) {
18         if (info.getText().toString().equals("微信") && !info.isChecked()) {
19
20             if (info.getParent().isClickable()) {
21                 info.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
22                 return;
23             }
24             break;
25         }
26     }
27 }

8. 在主页面偶尔找不到搜索按钮

这个问题很奇怪,排查了半天也没发现为什么。这个问题主要出现在进入微信比较深的地方一步步返回之后。我发现找不到搜索按钮主要是通过id找直接就没找到。

于是就换了一种查找控件的方式。

解决办法:

将event.getSource()换成getRootInActiveWindow()。

1 // 用getRootInActiveWindow是为了防止找不到搜索按钮的问题
2 List<AccessibilityNodeInfo> searchNode = getRootInActiveWindow().findAccessibilityNodeInfosByViewId(SEARCH_ID);
3 List<AccessibilityNodeInfo> wechatNode = getRootInActiveWindow().findAccessibilityNodeInfosByViewId(WECHAT_ID);
4 List<AccessibilityNodeInfo> viewPageNode = getRootInActiveWindow().findAccessibilityNodeInfosByViewId(VIEW_PAGE_ID);

9. 如果通过同一微信号进行查找,会发现在搜索结果页面就停止了

经排查,发现在搜索结果页面直接更改输入框的查询值,如果值一样的话,不会触发任何的事件。出现该问题的原因就在这。

解决办法:

先清空输入框,再输入需要查询的微信号。

 1 if (editTextNode.size() > 0) {
 2     try {
 3         Thread.sleep(300);
 4     } catch (InterruptedException e) {
 5         e.printStackTrace();
 6     }
 7
 8     // 输入框内清空
 9     Bundle clear = new Bundle();
10     clear.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, "");
11     editTextNode.get(0).performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, clear);
12
13     // 输入框内输入查询的微信号
14     Bundle arguments = new Bundle();
15     arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, Constant.wechatId);
16     editTextNode.get(0).performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
17 }

反思:

  • 任何一门技术都是说说容易,做做难。因为在实现过程中总会出现各种各样的问题;
  • 通过无障碍的方式来实现该功能效率低,并且不稳定,不知是否有更好的方法;
  • Android系统真的特别不安全!

GitHub地址:JumpToWeChat

大家如果有什么疑问或者建议可以通过评论或者邮件的方式联系我,欢迎大家的评论~

转载于:https://www.cnblogs.com/lanxingren/p/10299481.html

从APP跳转到微信指定联系人聊天页面功能的实现与采坑之旅相关推荐

  1. android studio聊天跳转_android app跳转到微信的示例

    今天写这片文章主要是记录下 app跳转到微信的实现方法,我的项目需求是跳转到微信公众号,由于微信官方关闭了这个直接可以跳到公众号的接口,只能 从app打开微信,让用户自己去搜索. 我的项目需求: 点击 ...

  2. php++仿网页版微信,vue+web端仿微信网页版聊天室功能

    一.项目介绍 基于Vue2.5.6+Vuex+vue-cli+vue-router+vue-gemini-scrollbar+swiper+elementUI等技术混合架构开发的仿微信web端聊天室- ...

  3. 个推透传消息设置通知栏展示 ios_企微功能十问 | 企业微信如何开启聊天侧边栏功能,有什么用?...

    企业微信(以下简称企微)更新的速度一次比一次快,现有功能已相对完善和强大,是市场上最适合企业运营私域流量的载体.企业微信的功能很多,今天给大家介绍的是一个非常实用,但绝大多数人都不知道的功能--聊天侧 ...

  4. 参数 携带 跳转_微信小程序:页面跳转及参数传递

    本文首发自个人自有博客:[FaxMiao个人博客],一个关注Web前端开发技术.关注用户体验.记录前端点滴,坚持更多原创,为大家提供高质量技术博文! 前言 小程序的跳转方法有很多种,有的人一直只用wx ...

  5. iOS 微信SDK1.8.6后需要UniversalLink解决方案及采坑记录

    项目最初因审核原因,一直使用iOS原生分享, 最近因项目需求要求, 接入微信分享, 以为和原来的没有区别, 但是接入时才发现改动的地方还是挺多的, 主要是需要配置UniversalLink和提包时的一 ...

  6. iOS应用跳转qq指定联系人聊天

    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"mqq://im/chat?chat_type=wpa&a ...

  7. 扫一扫跳转到微信非官方网页提醒页面解决方案

    测试时,本来打算通过微信扫一扫,打开一张图片,发现会跳转到如下一个页面: 妹的,我用的可是腾讯云cos的服务,自家的腾讯云地址都不认. 解决方案: 把图片域名绑定为一个公众号的业务域名,也就是白名单啦 ...

  8. APP自动化_操作微信小程序/H5页面实现自动化_多终端并行

    APP自动化_混合App自动化理论相关 原理:本质是混合app,应该用切入webview的方法去自动化web页面. 现状:目前由于技术原因无法切到webview中做自动化. 方案:采用原生app自动方 ...

  9. 通过链接跳转到微信公众号关注页面

    首先你查看要操作的公众号历史文章,将其在PC浏览器上打开,将其 参数值复制下来,然后替换这个链接地址中的参数值即可https://mp.weixin.qq.com/mp/profile_ext?act ...

最新文章

  1. eeglab中文教程系列(13)-导入cnt文件
  2. 芯片巨人也要搞医疗?
  3. [转]iis7.5+win2008 出现 HTTP Error 503. The service is unavailable.
  4. 自动秒收录导航php源码
  5. 输出呈三角形的杨辉三角形(C语言)
  6. Java 多线程 简单实例 (Thread)
  7. JavaScript—计算图片加载的张数。
  8. 中国人工智能学会通讯——KS-Studio:一个知识计算引擎 1.4 结束语
  9. [软件应用]深入验证Nero是否注册成功
  10. tftd32搭建DHCP服务器软件打开报错
  11. java计算机毕业设计ssm+vue工商学院办公用品管理信息系统
  12. Python 将中文大写数字转为阿拉伯数字
  13. 制定自己的工作目标时,应该学会SMART法则
  14. 分布式ID生成算法——雪花算法
  15. C语言中求和、计算平均值、方差和标准差
  16. PS把图片切成九宫格
  17. GSWiFi校园网路由器设置方法最新版
  18. 中式红木装修——优雅传统之美
  19. 【生信】第一二三代测序技术原理的理解
  20. java获取wps文档字数

热门文章

  1. Cadence OrCAD 原理图栅格设置方法
  2. 计算机型号win7,新买的计算机最近3个月发生了10次蓝屏,HP的型号HPE-035CN预装WIN7家庭高级版的。请大家看看是什么原 - Microsoft Community...
  3. 深入理解maven构建生命周期和各种plugin插件
  4. Flv视频编辑软件FlvEditor
  5. 第七次 java实验 紫金学院 异常
  6. 4岁估值45亿美金,商汤为何能入选AI国家队
  7. Git 与 GitHub 、GitLab 区别(学习笔记)
  8. 腾讯云学生服务器搭建wordpress个人网站
  9. 3ds max学习笔记(九)-- 实例操作(路径阵列)
  10. SVN A C D M G U R I的含义