妙用AccessibilityService黑科技实现微信自动加好友拉人进群聊

标签: 2018


引言

在上上周的周六和周日,我发了两篇利用itchat实现微信机器人的文章(Python):

  • 小猪的Python学习之旅 —— 18.Python微信转发小宇宙早报
  • 小猪的Python学习之旅 —— 19.Python微信自动好友验证,自动回复,发送群聊链接

通过把脚本挂到服务器上,自此告别手动挡,不用去手动转发小宇宙,
不用手动加好友,然后把别人一个个拉到我的Py交易群里。正当我
暗自窃喜的时候,微信并没有放过我这只小猫咪。

我还记得那天早上,我兴高采烈早早来到公司,更新了一波代码准备为
我的机器人添砖加瓦的时候,当我关闭了阿里云上的脚本,这时候意外来了,
我的机器人小号,再也无法通过微信网页端的接口登录了!!!
扫描完二维码,永远提示的都是下面这样一句话:

<error><ret>1203</ret><message>当前登录环境异常。为了你的帐号安全,暂时不能登录web微信。
你可以通过Windows微信、Mac微信或者手机客户端微信登录。</message></error>

是的,就是这样一句话,找不到申诉渠道,也不知道何时才可能会解封。(客户端任可正常使用)
而现在另外新申请的微信小号是无法登录微信网页端的,其实这是微信在慢慢关停网页版登录,
最主要的原因就是机器人泛滥!

没有了网页版微信,日子还是要过的,难道只能回归手动档么?几种解决方案:

  • 1.研究客户端协议(这个成本巨高,而且官方稍微改点东西,够你哭的)
  • 2.APP逆向,利用Xposed框架,hook相关的方法,也是有些研究成本的;
  • 3.利用类似与按键精灵的东西,编写脚本让他自动点点点,自动化测试
    工具或者本节讲的这个无障碍服务——AccessibilityService

AccessibilityService其实不是一个新的东西了,老久之前就有了,
官方原意:优化残障人士的使用体验的,而在我大天朝:

抢红包,自动安装,一键XXX等等,可谓欣欣向荣。

使用AccessibilityService也非常Easy,核心要点就是:

通过UI Automator找到节点,通过resource-id,text,content-desc等
唯一特征定位到具体的节点,接着执行各种模拟操作,点,滚动,填充,
用法比较简单的,大部分时间会花在试错和逻辑调整上

来一发通过AccessibilityService实现的自动加好友以及拉人进群聊的Gif体验下:

Gif加速了一点,不过完成加好友以及拉人总共也就耗时15s,是相当客观的啦。
下面就来介绍下AccessibilityService这个玩意怎么用吧~


AccessibilityService用法简介

1.自定义Service继承AccessibilityService

如题,自定义一个AccessibilityService类,重写两个主要方法:

onInterrupt( ):辅助功能中断的回调,基本不用理,核心还是
onAccessibilityEvent(AccessibilityEvent event) 上。

当界面发生了什么事情,比如顶部Notification,界面更新,内容变化等,
会触发这个方法,你可以根据不同的事件响应不同的操作,比如小猪这个
就是当顶部出现加好友的Notification的event时,跳转到加好友页。
点开AccessibilityEvent类可以看到一堆的事件类型~

事件类型 描述
TYPE_VIEW_CLICKED View被点击
TYPE_VIEW_LONG_CLICKED View被长按
TYPE_VIEW_SELECTED View被选中
TYPE_VIEW_FOCUSED View获得焦点
TYPE_VIEW_TEXT_CHANGED View文本变化
TYPE_WINDOW_STATE_CHANGED 打开了一个PopupWindow,Menu或Dialog
TYPE_NOTIFICATION_STATE_CHANGED Notification变化
TYPE_VIEW_HOVER_ENTER 一个View进入悬停
TYPE_VIEW_HOVER_EXIT 一个View退出悬停
TYPE_TOUCH_EXPLORATION_GESTURE_START 触摸浏览事件开始
TYPE_TOUCH_EXPLORATION_GESTURE_END 触摸浏览事件完成
TYPE_WINDOW_CONTENT_CHANGED 窗口的内容发生变化,或子树根布局发生变化
TYPE_VIEW_SCROLLED View滚动
TYPE_VIEW_TEXT_SELECTION_CHANGED Edittext文字选中发生改变事件
TYPE_ANNOUNCEMENT 应用产生一个通知事件
TYPE_VIEW_ACCESSIBILITY_FOCUSED 获得无障碍焦点事件
TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED 无障碍焦点事件清除
TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY 在给定的移动粒度下遍历视图文本的事件
TYPE_GESTURE_DETECTION_START 开始手势监测
TYPE_GESTURE_DETECTION_END 结束手势监测
TYPE_TOUCH_INTERACTION_START 触摸屏幕事件开始
TYPE_TOUCH_INTERACTION_END 触摸屏幕事件结束
TYPE_WINDOWS_CHANGED 屏幕上的窗口变化事件,需要API 21+
TYPE_VIEW_CONTEXT_CLICKED View中的上下文点击事件
TYPE_ASSIST_READING_CONTEXT 辅助用户读取当前屏幕事件

好吧,上面的表其实并没什么大用,我还是习惯直接把event.toString()给打印出来,
然后自行去判断~

如图就可以拿到event类型,以及产生对应事件的类名,核心是这两个,
除此之外还有Text和ContentDescription等。

比如我那个监听Notification跳转到添加好友页的:

这里就是对事件类型做了下判断,然后获取contentIntent,跳转而已。
简单点讲就是:

你在这个方法里,去判断一波事件类型和className,
然后再获取控件,做一些点击,滚动,填充文本等。


2.服务的配置

自定义完这个服务要想让他启用你还得执行下面的操作:

Step 1:在res文件夹下创建xml文件夹,新建一个配置的xml文件(名字自己定)

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged"android:accessibilityFeedbackType="feedbackGeneric"android:accessibilityFlags="flagDefault"android:canRetrieveWindowContent="true"android:notificationTimeout="100"android:packageNames="com.tencent.mm"android:settingsActivity="com.coderpig.wechathelper.MainActivity" />

属性简介如下

  • accessibilityEventTypes:设置监听的事件种类,用|隔开,监听所有可以用typeAllMask;
  • accessibilityFeedbackType:服务提供的反馈类型,feedbackGeneric通用反馈;
  • accessibilityFlags:辅助功能附加的标志,flagDefault默认的配置
  • canRetrieveWindowContent:辅助功能服务是否能够取回活动窗口内容的属性
  • notificationTimeout:响应时间
  • packageNames:监听的应用包名,不填,默认监听所有应用的事件
  • settingsActivity:允许用户修改辅助功能的activity类名

Step 2:接着AndroidManifest.xml文件中对该Service进行配置

先是添加一个权限:

android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"  

接着是Service的配置:

这里是你那个配置文件xml文件的文件名,其他照抄。

Step 3:安装到手机后,需要在手机设置的无障碍处开启服务

一般在设置的辅助功能处能找到:

如果Logcat那里能看到打印的LOG,说明服务正常运行,接下来要找控件节点


3.找控件

这里可以用到神器UI Automator来查看布局层次,打开Android Studio,
Ctrl + alt + A,输入 monitor

依次点击:选中设备 -> Dump View Hierarchy for UI Automator

稍等一会,右侧就会出现当前页面的布局层次图,如图随手选中一个邀请的节点:

右侧可以拿到对应的信息,一般比较常用的是这几个,有一点要注意!!!
resource-id不一定是唯一的

获得控件基本都会通过下述这个方法:

getRootInActiveWindow( ):获取当前整个活动窗口的根节点
返回的是一个AccessibilityNodeInfo类,代表View的状态信息,
提供了下述几个非常实用的方法:

  • getParent:获取父节点。
  • getChild:获取子节点。
  • performAction:在节点上执行一个动作。
  • findAccessibilityNodeInfosByText:通过字符串查找节点元素。
  • findAccessibilityNodeInfosByViewId:通过视图id查找节点元素。

后面的这两个方法会返回一个AccessibilityNodeInfo列表,一般操作是
遍历,然后筛选特定节点,比如我程序里的,获得底部Tab节点为”通讯录”,
然后点击,跳转后遍历,筛选”群聊”的节点,点击。

另外,UI Automator有时并不可靠(实时问题),我建议写多一个遍历节点
的方法,可以更清楚里面的控件情况:

拿到控件,接着就到触发事件了。


4.触发事件

通过调用performAction()传入一个时间类型即可触发相应时间,比如点击,长按等
事件就多了,自己点开AccessibilityNodeInfo类查看吧,这里介绍下最常用的几个事件:

//点击
performAction(AccessibilityNodeInfo.ACTION_CLICK);//长按
performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);//滚动
performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); //向下滚一下
performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); //向上滚一下//填充EditText(API版本需要>18可用方法1,API>21两种方法都可以使用)//方法1:
ClipboardManager clipboard = (ClipboardManager)this.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("text", "填充内容");
clipboard.setPrimaryClip(clip);
//获得焦点
info.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
粘贴进入内容
info.performAction(AccessibilityNodeInfo.ACTION_PASTE);  //方法2:
Bundle arguments = new Bundle();
arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, "填充内容");
info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);  

除了控件触发事件外,AccessibilityService提供了一个performGlobalAction(),用于执行
一些通用的事件

GLOBAL_ACTION_BACK    点击返回按钮
GLOBAL_ACTION_HOME    点击home
GLOBAL_ACTION_NOTIFICATIONS    打开通知
GLOBAL_ACTION_RECENTS    打开最近应用
GLOBAL_ACTION_QUICK_SETTINGS    打开快速设置
GLOBAL_ACTION_POWER_DIALOG    打开长按电源键的弹框

另外在实际开发中,直接调用这些全局方法又是并没有生效,
我在调GLOBAL_ACTION_BACK的时候就发现有时不会回退,
个人的解决方案是使用handler.postDelay()延时执行:

除了这样玩以外,我还利用时间差,串行去执行几个任务,比如:

上面的步骤是:

进入群聊聊天信息页后,列表滚动两次,接着依次:

  • 1.延时1s后,找到添加成员按钮并点击;
  • 2.延时2.3s后,把名字填充到EditText里
  • 3.延时3s后,点击确定按钮

就不用过于依赖onAccessibilityEvent方法,除了用handler.postDelay外,
还可以用Thread.sleep(休眠时长),用到的点大概就这么多,其余的自行探究吧。


小结

本节讲解一波如何通过AccessibilityService来实现自动加好友以及拉人进群,
之前是打算用xposed来写的,后面发现没我想像中简单,而且很多用安卓机的都
不会搞机(基),root也不会,后来还是选择了AccessibilityService,简单易用,
当然后面还是会研究一波xposed实现的,敬请期待~
对了,还有,之前那个网页端的机器人被封原因估计是信息秒回,如果有还用
itchat那个做机器人的,建议回复的时间可以稍微延长些;

关于AccessibilityService更多内容可见

  • Android辅助功能:https://blog.csdn.net/qq_24800377/article/details/78283662
  • Building Accessibility Services:https://developer.android.com/guide/topics/ui/accessibility/services.html
  • Developing an Accessibility Service:https://developer.android.com/training/accessibility/service.html

附:关键代码(都可以在:https://github.com/coder-pig/WechatHelper 找到):
代码有Bug的话正常,后续会优化下逻辑,感觉写得有点杂~

package com.coderpig.wechathelper;import android.accessibilityservice.AccessibilityService;
import android.app.Notification;
import android.app.PendingIntent;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;import java.util.List;/*** 描述:微信监控服务类** @author CoderPig on 2018/04/04 13:46.*/public class HelperService extends AccessibilityService {private static final String TAG = "HelperService";private Handler handler = new Handler();private String userName = "123";@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {int eventType = event.getEventType();CharSequence classNameChr = event.getClassName();String className = classNameChr.toString();Log.d(TAG, event.toString());switch (eventType) {case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {Notification notification = (Notification) event.getParcelableData();String content = notification.tickerText.toString();if (content.contains("请求添加你为朋友")) {PendingIntent pendingIntent = notification.contentIntent;try {pendingIntent.send();} catch (PendingIntent.CanceledException e) {e.printStackTrace();}}}break;case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:switch (className) {case "com.tencent.mm.plugin.subapp.ui.friend.FMessageConversationUI":addFriend();break;case "com.tencent.mm.plugin.profile.ui.SayHiWithSnsPermissionUI":verifyFriend();break;case "com.tencent.mm.plugin.profile.ui.ContactInfoUI":performBackClick();break;case "com.tencent.mm.ui.LauncherUI":if (!userName.equals("123")) {openGroup();}break;case "com.tencent.mm.ui.contact.ChatroomContactUI":if (!userName.equals("123")) {inviteGroup();}break;case "com.tencent.mm.ui.chatting.ChattingUI":if (!userName.equals("123")) {openGroupSetting();}break;case "com.tencent.mm.plugin.chatroom.ui.ChatroomInfoUI":if (userName.equals("123")) {performBackClick();} else {addToGroup();}break;case "com.tencent.mm.ui.base.i":dialogClick();break;}break;case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:}}private void addFriend() {AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();if (nodeInfo != null) {List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText("接受");if (list != null && list.size() > 0) {for (AccessibilityNodeInfo n : list) {n.performAction(AccessibilityNodeInfo.ACTION_CLICK);}} else {performBackClick();}}}private void verifyFriend() {AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();//获得用户名if (nodeInfo != null) {userName = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/d0n").get(0).getText().toString();AccessibilityNodeInfo finishNode = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/hd").get(0);finishNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);}}private void openGroup() {AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();if (nodeInfo != null) {List<AccessibilityNodeInfo> nodes = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/ca5");for (AccessibilityNodeInfo info : nodes) {if (info.getText().toString().equals("通讯录")) {info.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);handler.postDelayed(new Runnable() {@Overridepublic void run() {AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();if (nodeInfo != null) {List<AccessibilityNodeInfo> nodes = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/j5");for (AccessibilityNodeInfo info : nodes) {if (info.getText().toString().equals("群聊")) {info.getParent().getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);break;}}}}}, 500L);}}}}private void inviteGroup() {AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();if (nodeInfo != null) {List<AccessibilityNodeInfo> nodes = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/a9v");for (AccessibilityNodeInfo info : nodes) {if (info.getText().toString().equals("小猪的Python学习交流群")) {info.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);break;}}}}private void openGroupSetting() {AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();if (nodeInfo != null) {nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/he").get(0).performAction(AccessibilityNodeInfo.ACTION_CLICK);}}private void addToGroup() {AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();if (nodeInfo != null) {List<AccessibilityNodeInfo> listNodes = nodeInfo.findAccessibilityNodeInfosByViewId("android:id/list");if(listNodes != null && listNodes.size() > 0) {AccessibilityNodeInfo listNode = listNodes.get(0);listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);final AccessibilityNodeInfo scrollNodeInfo = getRootInActiveWindow();if (scrollNodeInfo != null) {handler.postDelayed(new Runnable() {@Overridepublic void run() {List<AccessibilityNodeInfo> nodes = scrollNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/d0b");for (AccessibilityNodeInfo info : nodes) {if (info.getContentDescription().toString().equals("添加成员")) {info.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);break;}}}},1000L);handler.postDelayed(new Runnable() {@Overridepublic void run() {List<AccessibilityNodeInfo> editNodes = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.tencent.mm:id/arz");if(editNodes != null && editNodes.size() > 0) {AccessibilityNodeInfo editNode = editNodes.get(0);Bundle arguments = new Bundle();arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, userName);editNode.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);}}}, 2300L);handler.postDelayed(new Runnable() {@Overridepublic void run() {List<AccessibilityNodeInfo> cbNodes = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.tencent.mm:id/kr");if(cbNodes != null) {AccessibilityNodeInfo cbNode = null;if(cbNodes.size() == 1) {cbNode = cbNodes.get(0);} else if(cbNodes.size() == 2) {cbNode = cbNodes.get(1);}if (cbNode != null) {cbNode.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);AccessibilityNodeInfo sureNode = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.tencent.mm:id/hd").get(0);sureNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);}}}}, 3000L);}}}}private void dialogClick() {AccessibilityNodeInfo inviteNode = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.tencent.mm:id/aln").get(0);inviteNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);userName = "123";handler.postDelayed(new Runnable() {@Overridepublic void run() {List<AccessibilityNodeInfo> sureNodes = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("com.tencent.mm:id/aln");if(sureNodes != null && sureNodes.size() > 0) {AccessibilityNodeInfo sureNode = sureNodes.get(0);sureNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);}}},1000L);}private void performBackClick() {handler.postDelayed(new Runnable() {@Overridepublic void run() {performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);}}, 300L);}//遍历控件的方法public void recycle(AccessibilityNodeInfo info) {if (info.getChildCount() == 0) {Log.i(TAG, "child widget----------------------------" + info.getClassName().toString());Log.i(TAG, "showDialog:" + info.canOpenPopup());Log.i(TAG, "Text:" + info.getText());Log.i(TAG, "windowId:" + info.getWindowId());Log.i(TAG, "desc:" + info.getContentDescription());} else {for (int i = 0; i < info.getChildCount(); i++) {if (info.getChild(i) != null) {recycle(info.getChild(i));}}}}@Overridepublic void onInterrupt() {}}

来啊,Py交易啊

想加群一起学习Py的可以加下,智障机器人小Pig,验证信息里包含:
PythonpythonpyPy加群交易屁眼 中的一个关键词即可通过;

验证通过后回复 加群 即可获得加群链接(不要把机器人玩坏了!!!)~~~
欢迎各种像我一样的Py初学者,Py大神加入,一起愉快地交流学♂习,van♂转py。


妙用AccessibilityService黑科技实现微信自动加好友拉人进群聊相关推荐

  1. 利用 Android AccessibilityService 实现自动加好友,拉人进群聊

    WechatHelper 项目地址:coder-pig/WechatHelper 简介: 利用 Android AccessibilityService 实现自动加好友,拉人进群聊 更多:作者   提 ...

  2. AccessibilityService黑科技(微信自动抢红包等)

    抢红包应该是AccessibilityService火起来的最大因素.网上借助AccessibilityService来实现的抢红包插件也是数不胜数,又是一个看上去很牛逼的功能. 『绿色守护』App的 ...

  3. 启善企业微信自动加好友助手教程

    一.打开企业微信,依次点击"通讯录"-"新的客户"-"添加" 二.打开<启善企业微信自动加好友助手>,"导入手机号&q ...

  4. Python+appium+GUI界面pyside2微信自动加好友(改问候语、备注、标签)

    目录 一.项目背景 二.实现功能 三.具体实现代码 四.支持与合作 一.项目背景 首先,这是我的第一篇文章,写作功底有待提高,后续有时间我会对代码细节的讲解进行完善. 闲暇中,帮助朋友的初创公司开发了 ...

  5. 5:实战2:微信自动加好友 JavaAppium

    如果您已经看过了前三篇文章,恭喜您已经初步了解了JavaAppium.本文会利用前三篇文章的知识点实现微信自动加好友跟着我们一起来作用例设计吧. 需求分析 要实现添加好友第一步:点击添加选择添加朋友 ...

  6. (小白)python如何微信自动加好友

    小白一枚在研究python,希望获得一点小成就感. 希望可以实现微信自动加好友的功能 希望大神解答,详细一点最好(您是大佬,我是小白!!!)

  7. 薅羊毛!企业微信自动加好友工具免费使用机会!

    从早到晚不停地发朋友圈,回复却寥寥无几?捧着手机不断刷新消息,却没有客户找你?明明已经很努力,可却总与客户失之交臂?谁都不想被动等待,可到底该如何跟进?如何营销? 很多营销团队都在思考客流来源和转化率 ...

  8. 小米微信自动加好友appium

    本人小菜,喜欢编程,初来乍到,请多多关照, 只可以加能搜索到的好友... from appium import webdriver from time import sleep class Weche ...

  9. [python,b站黑科技]ass字幕自动加载到b站弹幕

    https://github.com/opheliaKyouko/biliDmShooter

最新文章

  1. 计算的极限(零):逻辑与图灵机
  2. Android 之问题集锦
  3. python怎么输入代码-如何编写python代码
  4. LUOGU P2827 蚯蚓 (noip 2016)
  5. jzoj6288-旋转子段【优雅的暴力】
  6. Linux7/Redhat7/Centos7 安装Oracle 12C_监听配置及DBCA安装数据库_05
  7. Js操作Select大全(取值、设置选中)
  8. 【git】Git Submodule管理项目子模块
  9. 2016.05.07华为网盘-将会暂停服务的网站一览 - 做好数据迁移的准备哦
  10. python创建txt文件并写入-python创建txt文件
  11. iOS - UIButton连续点击事件控制
  12. Restorator软件使exe文件都不能打开,任务管理器不支持此接口
  13. 高并发访问数据库引发的故障
  14. 解决小米手机用Android studio安装不了app的问题
  15. win7+ubuntu16.04以及win10+ubuntu18.04双系统安装
  16. prototype.js详解
  17. 如何逆序输出一个整数
  18. Docker-搭建私有仓浅谈
  19. 使用Nexus搭建Maven私服、私服下载与上传
  20. 计算机应用基础考试excel操作题,计算机应用基础统考题库 2016年4月网络统考电子表格...

热门文章

  1. 软件测试团队口号及队名,团队队名口号(精选50句)
  2. 戴尔笔记本电脑怎下载c语言,戴尔笔记本电脑如何下载驱动
  3. python图片修复软件下载_python实现的jpg格式图片修复代码
  4. kubernetes dev client-go 介绍
  5. SkyForm CMP同Mirantis OpenStack兼容测试
  6. 总结:MUSTer中的keypoint matching以及利用RANSAC去除outliers
  7. 会议及作用篇--项目管理(十六)
  8. 华为在HDC2021发布全新HMS Core 6 宣布跨OS能力开放
  9. Ubuntu安装ESPNET(搞了一个多月)
  10. iOS app添加桌面快捷方式