├── README.md └── 功能函数.js /README.md: -------------------------------------------------------------------------------- 1 | # Auto.js-skill 2 | # Auto.js的一些常用功能函数 详见js文件 3 | 4 | Autojs的崩溃: 5 | 6 | ## 一、无障碍常见崩溃: 7 | 8 | 1.无法click(屏幕长 / 2, 屏幕宽 /2) 9 | 10 | 原因:无法获取屏幕长款 11 | 12 | 解决:重启手机 13 | 14 | 2.开启后,明明事先打开了无障碍,却依然疯狂跳设置 15 | 16 | 原因:其他软件比如ATX的堵塞,或者就是单纯抽风了 17 | 18 | 解决:全部关掉,或者重启 19 | 20 | 21 | 22 | 二、无障碍特殊崩溃 23 | 24 | 1.[特殊崩溃]运行脚本后瞬间报错,只有launch和log等命令仍然在执行,错误内有线程字样 25 | 26 | 触发条件:魅族系统默认是智能后台,导致它杀了后台,但又没完全杀掉。 所以息屏后,再次点亮,就会触发这个情况 27 | 28 | 解决:关掉相应软件,无障碍重开,设置允许后台 29 | 30 | 2.[特殊崩溃]运行脚本到click步骤,直接退出 ,没有报错 ,更换脚本 ,依旧如此 31 | 32 | 解决:这个需要一步步排查才能发现,暂时原因不明,触发条件是体验版的魅族系统+不正确的app版本,解决就是重启手机 33 | 34 | ## 二、打包apk 35 | 36 | 使用auto();去代替 auto.waitFor()并做进按钮中, 以避免不必要的疯狂弹窗 37 | 38 | ## 三、特殊的语法功能 39 | 40 | ### 1.点击按钮的方法: 41 | 42 | #### 1)控件点击 43 | 44 | 首先推荐的必然是控件点击,它的逻辑是只要能被侦测,就能被点击,无论它是躲在某个悬浮窗后面,还是躲在看不见的下滑栏的底部,即使是app在弹广告,依然可以无视掉。前提是找到控件的特殊属性 45 | 46 | #### 2)层级结构 47 | 48 | 其次仍然是控件点击的延申,层级点击,分为两种点击,通常用于app内的网页展示,表现可以具体参照jd,它的结构极其杂乱 49 | 1. 写10多个child,找到它的那个拥有独一无二参数的上家,此时需要pc端控件查看,才能理清思路 50 | 2. 另外一种 就是使用indexInParent().depth()去查找,如果怕重复,就可以换为find(),然后再一个个检查 51 | 下面是一个我在饿了么里面用到的一段代码: 52 | 53 | sleep(3000); 54 | depth('15').indexInParent('3').waitFor(); 55 | log('页面加载完成\n等待5s'); 56 | sleep(5000); 57 | 58 | let exb = depth('16').indexInParent('0').find(); 59 | for(i = 0; i < exb.length; i++){ 60 | if(exb[i].bounds().centerX() > dev_width/2) continue; 61 | if(exb[i].bounds().centerY() > dev_hight/2) break; 62 | } 63 | exb[i].click();sleep(2000); 64 | 65 | 66 | #### 3)识图/色点击 67 | 即便层级结构复杂,我也是不推荐识图点击,有个例外,就是1对1定制,只在有限的几个设备上用,那识图确实是个省力的活,但是如果面向很多用户,并不推荐, 68 | 理由1 不同人的手机屏幕分辨率和尺寸不同,结果就是ppi不同,缩放不同,识图基本上是像素点对比的,所谓的容错也只是颜色的范围,这种问题同样出现在Windows中的pywinauto上 69 | 理由2 除了ppi不同导致图案大小不同,还会导致颜色色彩不同,是的,色彩,rgb值不一样。 70 | 修正方案:hamibot作者给的建议,只截取屏幕的一长条区域,进行颜色判定。 71 | 72 | #### 4)ocr点击 73 | 具体写在功能函数里了,借用外部的ocr识别,拿到字的坐标 74 | 75 | #### 5)坐标点击 76 | 坐标点击我放在最后,只是因为它即使用了比例坐标,依旧会无法点击,我感觉是aj4.1的bug,当然,1对1定制,可以用 77 | 78 | #### 6)轮廓识别 79 | 80 | 图色的高级版,https://www.yuque.com/yashujs/bfug6u/rdd292 81 | 82 | #### 2.Matches 正则 83 | 84 | 在功能函数里有说它的用法,它的原则是每个控件的text,classname 等等文本内容都不可拆分,所以直接textMatches('某某'),而并没有写全,是无法找到的,所以需要.+ 即可,再加上|就可以实现多个 85 | 86 | 字符的轮流查找 87 | 88 | 所以textMatches('.+A.+'|'.+B.+')一句话的功能就相当于 89 | 90 | textContains(A).find(); 91 | textContains(B).find(); 92 | 93 | 两句话的作用,更何况后者还是两个数组,(我不知道在js里面应该叫什么),同样它支持包名 类名的匹配,所以,是个优化代码的好方法 94 | 95 | 96 | -------------------------------------------------------------------------------- /功能函数.js: -------------------------------------------------------------------------------- 1 | //1.初始化 2 | auto.waitFor(); 3 | 4 | //2.使用屏幕长宽数据的初始化 5 | if(!device.width) {toastLog('BUG啦,请重启手机');exit();}; 6 | 7 | //3.截图初始化 8 | if(!requestScreenCapture()){ 9 | toast("请求截图失败"); 10 | exit();} 11 | 12 | //4.点击控件 找不到报错 13 | function sureclick(x) {if(x) return x.click();else toastLog('未找到按钮');} 14 | 15 | //5.点击不可点击的控件 16 | function position_click(x){if(x) click(x.bounds().centerX(), x.bounds().centerY()) 17 | else toastLog('找不到此控件');} 18 | 19 | //6.子线程 音量键关闭 20 | threads.start(function(){ 21 | events.observeKey(); 22 | events.onKeyDown("volume_up", function(event){toastLog("\n音量+被按下,即将结束脚本!");sleep(2000);console.hide();exit();}); 23 | }); 24 | 25 | //7.不断查找某控件父元素,并点击(解决模拟器和手机不查找元素不一致问题) 26 | function up_click(x) { 27 | if (x && x.clickable()) return x.click(); 28 | for (let ii = 0; ii < 6; ii++) { 29 | if (!x) break 30 | x = x.parent(); 31 | if (x && x.clickable()) return x.click(); 32 | let list_x = x.children(); 33 | for (let i = 0; i < list_x.length; i++) 34 | {if (list_x[i] && list_x[i].clickable()) return list_x[i].click()};} 35 | return false} 36 | 37 | //8.找图点击 38 | function png_click(num, pngbase64){ 39 | while(num--){ 40 | let img = captureScreen(); 41 | let temp1 = images.fromBase64(pngbase64); 42 | let pos = findImage(img, temp1); 43 | if(pos){return click(pos.x, pos.y);} else sleep(1000);} 44 | return false;} 45 | 46 | //9.找色点击 47 | function cs_click(rgb, xr, yr, wr, hr) { 48 | let img = captureScreen() 49 | let point = findColor(img, rgb, { region: [img.getWidth() * xr, img.getHeight() * yr, img.getWidth() * wr, img.getHeight() * hr], threshold: 8 }) 50 | if (point) { 51 | point.x = img.getWidth() - point.x; point.y = img.getHeight() - point.y 52 | return click(point.x, point.y); 53 | } 54 | } 55 | 56 | //10.多词匹配 同样适用于descMatch 等等 57 | let sth = textMatches('.+关键词.+|.+关键词.+|.+关键词.+'); 58 | 59 | //11.意图启动格式 60 | app.startActivity({ 61 | packageName:'com.taobao.live', 62 | action:'VIEW', 63 | className:'com.taobao.live.TaoLiveVideoActivity' 64 | }); 65 | 66 | //12.自动校准归位 67 | threads.start(function(){ 68 | while(true){ 69 | var current_page = currentActivity(); 70 | sleep(15000); 71 | var current_page2 = currentActivity(); 72 | if(check_flag){ 73 | if(current_page == current_page2) { 74 | log('已堵塞,重启中...'); 75 | thread_main.interrupt(); 76 | comeback();//回到原点 77 | main_thread();//重新执行 78 | continue; 79 | } 80 | } 81 | } 82 | }) 83 | 84 | //保持运行activity 85 | function keep_running(say, avtivity, package, avti_data){ 86 | toastLog(say);home();sleep(1000); 87 | toastLog('启动中...'); 88 | app.launch(package);sleep(1000); 89 | app.startActivity({ 90 | packageName: package, 91 | data: avti_data 92 | }); 93 | sleep(5000); 94 | while(currentActivity() != avtivity){ 95 | toastLog('启动中...'); 96 | app.launch(package);sleep(1000); 97 | app.startActivity({ 98 | packageName: package, 99 | data: avti_data 100 | }); 101 | sleep(12000); 102 | } 103 | toastLog('已启动'); 104 | } 105 | 106 | //举例: 107 | keep_running('环节1:吃货豆浏览', 'me.ele.component.webcontainer.view2.AppUCWeb2Activity', 'me.ele', 'eleme://web?&url=https://h5.ele.me/svip/task-list'); 108 | 109 | //百度OCR 三连发 110 | //ocr1 返回识图结果 111 | function Baidu_ocr(imgFile){ 112 | log("识图..."); 113 | var imag64 = images.toBase64(imgFile, "png", 100); 114 | var API_Key="自己的AK"; 115 | var Secret_Key="自己的SK"; 116 | var getTokenUrl="https://aip.baidubce.com/oauth/2.0/token";//选择网络图片识别 117 | var token_Res = http.post(getTokenUrl, { 118 | grant_type: "client_credentials", 119 | client_id: API_Key, 120 | client_secret: Secret_Key, 121 | }); 122 | var access_token=token_Res.body.json().access_token; 123 | var ocrUrl = "https://aip.baidubce.com/rest/2.0/ocr/v1/webimage_loc"; 124 | var ocr_Res = http.post(ocrUrl, { 125 | headers: { 126 | "Content - Type": "application/x-www-form-urlencoded" 127 | }, 128 | access_token: access_token, 129 | image: imag64, 130 | language_type:"CHN_ENG"//可添加额外参数 131 | }); 132 | sleep(1000); 133 | var json = ocr_Res.body.json(); 134 | return json.words_result; 135 | } 136 | //OCR2 处理返回结果 并点击 137 | function ocr_click(target_words) { 138 | var imgScreen = captureScreen(); 139 | var logOcr= Baidu_ocr(imgScreen); 140 | var target_nums = 0; 141 | 142 | for (i = 0; i < logOcr.length; i++){ 143 | if(target_words == logOcr[i].words) {console.log('找到:'+target_words);target_nums = i;break;} 144 | } 145 | if(!target_nums) {log('未找到指定文字');return false;} 146 | let postion = new Array(); 147 | 148 | postion[1] = logOcr[target_nums].location.top; 149 | postion[0] = logOcr[target_nums].location.left; 150 | sleep(500); 151 | 152 | click(postion[0], postion[1]); 153 | return postion; 154 | } 155 | //ocr3.记录所有相同字的位置,目的是为了尽可能的少发送截图请求 156 | function list_ocr(target_words) { 157 | sleep(2000); 158 | let img = captureScreen(); 159 | let imgScreen = images.clip(img, 0, dev_hight/2, dev_width, dev_hight/2); 160 | var logOcr= Baidu_ocr(imgScreen); 161 | let nums_list = new Array(); 162 | let times = 0; 163 | 164 | for (i = 0; i < logOcr.length; i++){ 165 | if(target_words == logOcr[i].words) nums_list[times++] = i; 166 | } 167 | if(!times) {log('未找到指定文字');return false;} 168 | else log('找到'+times+'个'); 169 | 170 | var postion = new Array(); 171 | for(k = 0; k < times; k++){ 172 | postion[k]=new Array(); 173 | } 174 | for(i = 0; i < times; i++){ 175 | postion[i][0] = logOcr[nums_list[i]].location.left; 176 | postion[i][1] = logOcr[nums_list[i]].location.top; 177 | } 178 | return postion; 179 | } 180 | --------------------------------------------------------------------------------