wechat

背景

要实现微信自动化,大致有这么几种办法

基于Web微信API的框架 如 #

基于xposed 插件开发 ,通过广播方式和sdk 交互,只要发送广播就可以自动发送朋友圈,但是实现难度相对较高,需要逆向知识,还有被封号的风险,不可取。

基于AccessibilityService实现,有一个自动抢红包的功能就是通过他实现的,原本该功能是对那些由于视力、听力或其它身体原因导致不能方便使用Android智能手机的用户,Android提供了Accessibility功能和服务帮助这些用户更加简单地操作设备,包括文字转语音(不支持中文)、触觉反馈、手势操作、轨迹球和手柄操作等,到Android 4.1版本以后,系统辅助服务增加了与窗口元素的双向交互,此时可以通过辅助功能服务操作窗口元素,比如点击按钮、输入文本信息等功能,越来越方便。

这些方法各有利弊,综合看来,第一种实现总会有一天微信说要关闭web服务,那岂不是很惨,第二种风险在于被封号,只有第三种目前看来最合适,想要自动发送朋友圈,AccessibilityService是最完美的实现。知其然知其所以然,我们要想用好AccessibilityService,就要明白其原理,这样能更好的理解每一步操作的含义,少走弯路,避免考虑不周导致成功率不足。

自动化视频效果展示:

在做的过程中,也遇到很多问题,例如经常拿不到

AccessibilityNodeInfo实例,如果拿不到就无法操作当前界面的元素,等于是无法再执行下去了,这里有几个关键点需要注意的,只要注意这几个就可以完美拿到。卖个关子,下面会提到,请往下看。

自动分享小程序给好友(doing)

自动拉好友进群(doing)

自动踢人(doing)

AccessibilityService原理

先大致了解下原理,对你的使用更是事半功倍。

类图源于 here

AccessibilityService类图

分析下这个类图。

AccessibilityService:最主要的onBind()、onAccessibilityEvent(event: AccessibilityEvent)、onInterrupt()三个函数,后面两个需要子类实现,onBind已经实现了,看下这个函数源码 直接return了一个静态内部类IAccessibilityServiceClientWrapper

public final IBinder onBind(Intent intent) { return

new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {

}

}

IAccessibilityServiceClientWrapper : 用于和system_server通信的匿名Binder服务,

public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub

implements HandlerCaller.Callback {

该类又继承了IAccessibilityServiceClient.Stub,并实现了HandlerCaller.Callback接口。看到这分析出,这是一个跨进程通信Service,在IAccessibilityServiceClientWrapper构造函数中看到Callbacks 回调接口

public interface Callbacks {

void onAccessibilityEvent(AccessibilityEvent event);

void onInterrupt();

void onServiceConnected();

void init(int connectionId, IBinder windowToken);

boolean onGesture(int gestureId);

boolean onKeyEvent(KeyEvent event);

void onMagnificationChanged(@NonNull Region region,

float scale, float centerX, float centerY);

void onSoftKeyboardShowModeChanged(int showMode);

void onPerformGestureResult(int sequence, boolean completedSuccessfully);

void onFingerprintCapturingGesturesChanged(boolean active);

void onFingerprintGesture(int gesture);

void onAccessibilityButtonClicked();

void onAccessibilityButtonAvailabilityChanged(boolean available);

}

看到这里再回头看看onBind函数的具体实现如图

看到了吧,AccessibilityService的AccessibilityEvent事件

来源于IAccessibilityServiceClientWrapper,我们再看看IAccessibilityServiceClientWrapper是如何收到这个Event,再往下跟踪代码

IAccessibilityServiceClientWrapper中executeMessage(Message message)函数调用mCallback.onAccessibilityEvent(event)传递给AccessibilityService,executeMessage函数是HandlerCaller.Callback接口的实现,那谁发送的这个Message呢,

IAccessibilityServiceClientWrapper中同样的onAccessibilityEvent()函数如图

而这个函数又是谁调的呢,这里就到了进程间通信的逻辑,看一下外部逻辑,上面是倒推逻辑,下面正推一下。

AccessibilityService跟一个监控一样,界面的所有的事件都可以收到,那它的源头肯定在View上,肯定在View的事件处理上,跟着这个逻辑去找一下

在performClick函数中发现有一个AccessibilityEvent事件传递,再往里面跟踪发现了这个有用的信息

这里面可以清晰的看到,你收到的AccessibilityEvent事件所有的字段赋值逻辑就在这里。那它是如何发出去交给AccessibilityService呢,肯定是通过AIDL,进一步查找源码

发现在sendAccessibilityEventUncheckedInternal函数中,调用了

getParent().requestSendAccessibilityEvent(this, event),接着看看这个getParent()干了什么,找了一圈找到具体实现在哪,最终在ViewRootImpl中找到这个方法实现

@Override

public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {

if (mView == null || mStopped || mPausedForTransition) {

return false;

}

// Immediately flush pending content changed event (if any) to preserve event order

if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED

&& mSendWindowContentChangedAccessibilityEvent != null

&& mSendWindowContentChangedAccessibilityEvent.mSource != null) {

mSendWindowContentChangedAccessibilityEvent.removeCallbacksAndRun();

}

// Intercept accessibility focus events fired by virtual nodes to keep

// track of accessibility focus position in such nodes.

final int eventType = event.getEventType();

switch (eventType) {

case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {

final long sourceNodeId = event.getSourceNodeId();

final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(

sourceNodeId);

View source = mView.findViewByAccessibilityId(accessibilityViewId);

if (source != null) {

AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();

if (provider != null) {

final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(

sourceNodeId);

final AccessibilityNodeInfo node;

node = provider.createAccessibilityNodeInfo(virtualNodeId);

setAccessibilityFocus(source, node);

}

}

} break;

case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {

final long sourceNodeId = event.getSourceNodeId();

final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(

sourceNodeId);

View source = mView.findViewByAccessibilityId(accessibilityViewId);

if (source != null) {

AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();

if (provider != null) {

setAccessibilityFocus(null, null);

}

}

} break;

case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {

handleWindowContentChangedEvent(event);

} break;

}

mAccessibilityManager.sendAccessibilityEvent(event);

return true;

}

可以看到是mAccessibilityManager.sendAccessibilityEvent 发出了事件,再看下这个函数的实现逻辑

public void sendAccessibilityEvent(AccessibilityEvent event) {

final IAccessibilityManager service;

final int userId;

synchronized (mLock) {

service = getServiceLocked();

if (service == null) {

return;

}

if (!mIsEnabled) {

Looper myLooper = Looper.myLooper();

if (myLooper == Looper.getMainLooper()) {

throw new IllegalStateException(

"Accessibility off. Did you forget to check that?");

} else {

// If we're not running on the thread with the main looper, it's possible for

// the state of accessibility to change between checking isEnabled and

// calling this method. So just log the error rather than throwing the

// exception.

Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");

return;

}

}

if ((event.getEventType() & mRelevantEventTypes) == 0) {

if (DEBUG) {

Log.i(LOG_TAG, "Not dispatching irrelevant event: " + event

+ " that is not among "

+ AccessibilityEvent.eventTypeToString(mRelevantEventTypes));

}

return;

}

userId = mUserId;

}

try {

event.setEventTime(SystemClock.uptimeMillis());

// it is possible that this manager is in the same process as the service but

// client using it is called through Binder from another process. Example: MMS

// app adds a SMS notification and the NotificationManagerService calls this method

long identityToken = Binder.clearCallingIdentity();

service.sendAccessibilityEvent(event, userId);

Binder.restoreCallingIdentity(identityToken);

if (DEBUG) {

Log.i(LOG_TAG, event + " sent");

}

} catch (RemoteException re) {

Log.e(LOG_TAG, "Error during sending " + event + " ", re);

} finally {

event.recycle();

}

}

IAccessibilityManager 是个aidl接口,最终通过他发送给了服务

看到这是不是明白了其中的原理。在AccessibilityService你还可以拿到Activity的一些信息,同样的道理,你在源码中肯定能找到那个实现,你可以试着自己去搜一下。

AccessibilityService使用心得

上面卖的关子,现在可以圆满了,在使用中遇到过很多种情况拿不到RootInActiveWindow 也就是AccessibilityNodeInfo(表示窗口内容的节点),当窗口能拿到这个节点时,你才能通过他去findView,所以你知道它的重要性了,但为什么很多时候拿不到呢

第一个case,如图

如果在TYPE_WINDOWS_CHANGED中就会拿不到

第二个case,如图

当你不在当前页面(com.tencent.mm.ui.LauncherUI是微信的主页)时同样也有可能获取不到,有可能是在其他页面。

第三个case,没图,这种情况就很奇怪,在上面两个都避免了之后,还有拿不到的情况,怎么办了,偶然间我切回桌面,又回来,发现又有了,具体什么原理,目前没找到答案,好吧,总算有个解决办法,当拿不到这个界面的节点时,我切到任务管理状态,再点物理返回键,达到切换的效果,这时候还真的又拿到了,也许这是目前最有价值的一个case,希望你也能用它解决问题。

总结

本项目源码完善中,功能上会加入

自动分享小程序给好友

自动拉好友进群

自动踢人

代码会提交到这里 I校长

欢迎Star

android 自动加微信群,如何完美实现微信自动发朋友圈自动添加好友等等相关推荐

  1. 调用个人微信API协议接口收发消息,发朋友圈

    调用个人微信API协议接口收发消息,发朋友圈 java调用个人微信的API接口收发消息 /** * 接受微信好友发来聊天消息 * @author wechatno:tangjinjinwx * @pa ...

  2. 微信小助手2.9.0:专为mac微信3.1.2发行!支持发朋友圈!

    WeChatExtension是一款适用于Mac版的WeChat拓展功能插件,中文名为微信小助手,这款插件想必很多人都用过,其功能非常多,包括自动回复.消息防撤回.远程控制.微信多开.登录免认证.会话 ...

  3. 微信群拉人很累?学会这 6 种自动化方案后,轻松乐无忧!

    公众号关注 「运维之美」 设为「星标」,每天带你了解圈内新鲜事! 微信是世界上体量最大的超级 APP 之一,拥有数以十亿计的用户,每天都有许许多多的人在微信上进行聊天.沟通.推广.卖货--覆盖了社交辐 ...

  4. 企业微信群机器人是什么?企微机器人如何自动发消息?

    经常有很多用户会问我们,企业微信群机器人是什么,有什么用,有什么限制?企业微信群机器人提供的是一个webhook消息,如何可以通过这个来自动发消息呢?之前写过一些关于企业微信群机器人如何发消息的教程, ...

  5. android 微信高仿,Android 高仿微信发朋友圈浏览图片效果(转)

    最近一直在高仿微信.高仿微信,今天小编再给大家分享一个仿微信发朋友圈浏览图片的效果.... 好了,先看一下效果吧: 这里写图片描述 下面就来说一下具体怎么实现的: 实现思路 1.首先我们要获取数据源, ...

  6. android 仿微信选取相册_Android--选择图片(仿微信发朋友圈)第一篇

    这篇博客主要写仿微信朋友圈选择图片发朋友圈.整个功能包括加载图片,显示图片.相册文件夹.预览图片,九宫格显示已经选择好的图片等等,大概会有三篇博客. 效果图 看上面的图,加载图片肯定是异步加载,耗时任 ...

  7. 微信自动发朋友圈源码

    目前微信营销行业主要有两大神器,云控和群控,对于群控大家可能多少有些了解.总所周知,群控就是一种通过系统,实现一台PC控制百台手机微信的新媒体的一种营销软件,而云控系统核心功能与群控大致一样,主要区别 ...

  8. android朋友圈动态视频教程,Android--选择图片(仿微信发朋友圈)第一篇

    这篇博客主要写仿微信朋友圈选择图片发朋友圈.整个功能包括加载图片,显示图片.相册文件夹.预览图片,九宫格显示已经选择好的图片等等,大概会有三篇博客. 效果图 看上面的图,加载图片肯定是异步加载,耗时任 ...

  9. Android 高仿微信发朋友圈浏览图片效果

    最近一直在高仿微信.高仿微信,今天小编再给大家分享一个仿微信发朋友圈浏览图片的效果.... 好了,先看一下效果吧: 下面就来说一下具体怎么实现的: 实现思路 1.首先我们要获取数据源,数据源就是我们的 ...

最新文章

  1. easyexcel怎么设置表头宽度_easyexcel 自动设置列宽
  2. 为什么java需要静态类_为什么Java主要方法是静态的?
  3. Linux Shell 文本处理工具集锦
  4. 朗锐智科发布PCIe-3504PoE 千兆以太网图像采集卡
  5. 一种内核到用户空间的高效数据传输技术
  6. 欢迎访问我的Github
  7. day19-URL+视图+模板+ORM
  8. h5实现一键复制到粘贴板 兼容iOS
  9. Spring MVC请求-响应流
  10. c++调用matlab
  11. POJ - 1459 Power Network(最大流)(模板)
  12. Arduino操作记录---雨滴传感器的使用
  13. 算法题:Find the closest common ancestor
  14. 用GNS3制作路由交换网络拓扑图
  15. 调查 ESXi/ESX 上的虚拟机文件锁定 (10051)
  16. 1047: 字符图形3-平行四边形
  17. canva五角星空html,使用canvas绘制一个五角星
  18. 推荐7款非常棒的将代码片段转换成图片的工具
  19. Linux下查看CPU核数
  20. 软连接和硬连接(Linux创建软连接一定要用绝对路径)

热门文章

  1. jdk8 中英文版文档
  2. ieee 802.3学习笔记-MII
  3. 51单片机学习篇-- --基于51单片机的串口通信协议
  4. 如何梳理陌生的代码模块
  5. Redis报错Java.net.UnknownHostException的解决办法
  6. WSN连通性模拟、WSN覆盖率模拟、WSN分簇模拟、WSN能量损耗模拟
  7. Python实现门禁管理系统(源码)
  8. 51nod大鱼吃小鱼问题
  9. PMP备考图表汇总详解
  10. 「雕爷学编程」Arduino动手做(17)---人体感应模块