android拆轮子系列之一步一步教你写微信抢红包插件
上一章《AccessibilityService講解》我們對(duì)AccessibilityService做了基本的講解,本章我們就利用AccessibilityService做一個(gè)搶紅包插件。
1.前言
本章編寫的代碼僅供個(gè)人學(xué)習(xí)使用,禁止用于其他非法行為。技術(shù)是把雙刃劍希望大家正確對(duì)待!
在文章的開頭奉送上代碼,方便大家對(duì)照著學(xué)習(xí)。
2.代碼編寫
2.1搶包流程:
做搶紅包的插件我們得一步一步來,首先我們應(yīng)該了解搶紅包的流程是怎么樣的,回顧一下?lián)尲t包的流程:
1. 狀態(tài)欄出現(xiàn)”[微信紅包]”的消息提示,點(diǎn)擊進(jìn)入聊天界面 2. 點(diǎn)擊相應(yīng)的紅包信息,彈出搶紅包界面 3. 在搶紅包界面點(diǎn)擊”開”,打開紅包 4. 在紅包詳情頁面,查看詳情,點(diǎn)擊返回按鈕返回微信聊天界面.上面就是搶紅包的整個(gè)流程。雖然這是整個(gè)流程,但是當(dāng)前微信處于的界面可能出現(xiàn)以下幾種:
1.微信在后臺(tái)運(yùn)行,而當(dāng)前手機(jī)在瀏覽其他APP(需要執(zhí)行1->2->3->4) 2.當(dāng)前處于微信的首頁(需要執(zhí)行1->2->3->4) 3.處于和某個(gè)人聊天的頁面(2->3->4)2.1代碼編寫
理解了搶紅包的流程,我們開始編寫代碼。上一節(jié)講了,創(chuàng)建一個(gè)AccessibilityService的流程很簡(jiǎn)單如下:
1.繼承AccessibilityService類,重寫里面的方法
2.在AndroidMainifest中注冊(cè)
3.添加配置文件
1.下面我們來看一下AccessibilityService的編寫
package com.czh.service; import java.util.ArrayList; import java.util.List; import android.accessibilityservice.AccessibilityService; import android.app.Notification; import android.app.PendingIntent; import android.content.Intent; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.Toast;/*** @author 作者 YYD* @version 創(chuàng)建時(shí)間:2016年12月14日 下午2:46:49* @function 未添加*/ public class RobService extends AccessibilityService {/*** 此方法用來接收我的需要的各種事件 在accessibility.xml中我們監(jiān)聽了以下事件:* typeNotificationStateChanged typeWindowStateChanged* typeWindowContentChanged* http://www.jianshu.com/p/ba298b8d5a6e 看看這個(gè)是否被拆了判斷*/@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {int eventType = event.getEventType();switch (eventType) {// 當(dāng)通知欄發(fā)生改變的時(shí)候case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:List<CharSequence> texts = event.getText();if (!texts.isEmpty()) {for (CharSequence text : texts) {String content = text.toString();if (content.contains("[微信紅包]")) {if ((event.getParcelableData() != null)&& (event.getParcelableData() instanceof Notification)) {Notification notification = (Notification) event.getParcelableData();PendingIntent pendingIntent = notification.contentIntent;try {pendingIntent.send();} catch (Exception e) {}}}}}break;// 當(dāng)窗口內(nèi)容發(fā)生改變的時(shí)候case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:break;// 當(dāng)窗口狀態(tài)發(fā)生改變的時(shí)候case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:String className = event.getClassName().toString();if (className.equals("com.tencent.mm.ui.LauncherUI")) { //聊天頁 // getLastPacket();//注釋的原因我在方法中寫明了,如果放開這個(gè)方法就會(huì)出現(xiàn)死循環(huán),大家可以嘗試一下。 // inputClick("com.tencent.mm:id/fz");//這條語句是聊天頁面返回鍵的id,放不放開都沒用。} else if (className //拆紅包頁,點(diǎn)擊“開”,搶紅包.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI")) {inputClick("com.tencent.mm:id/bg7");} else if (className //紅包詳情頁,點(diǎn)擊返回鍵.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI")) {inputClick("com.tencent.mm:id/gd");}break;}}/*** 根據(jù)id,獲取AccessibilityNodeInfo,并點(diǎn)擊。*/private void inputClick(String clickId) {AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();if (nodeInfo != null) {List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(clickId);for (AccessibilityNodeInfo item : list) {item.performAction(AccessibilityNodeInfo.ACTION_CLICK);}}} /*** 這個(gè)方法有缺陷。要是能加 過濾已拆紅包就好了。我實(shí)在想不出好的辦法所以注釋掉了。* 如果大家有好的過濾方案,請(qǐng)告訴我一聲,不勝感激!這是我的一個(gè)遺憾* 因?yàn)檫@一行注釋掉,就不是我們想要的全自動(dòng)的搶紅包了。*/private void getLastPacket() {AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();recycle(nodeInfo);if (parents.size() > 0) {parents.get(parents.size() - 1).performAction(AccessibilityNodeInfo.ACTION_CLICK);}}@Overridepublic void onInterrupt() {}@Overrideprotected void onServiceConnected() {super.onServiceConnected();Toast.makeText(RobService.this, "成功與微信綁定,開始監(jiān)聽", Toast.LENGTH_SHORT).show();}ArrayList<AccessibilityNodeInfo> parents = new ArrayList<AccessibilityNodeInfo>(); /*** 這個(gè)方法是用遞歸的方式,遍歷節(jié)點(diǎn)樹。* 如果找到“領(lǐng)取紅包”和“查看紅包”所在葉子節(jié)點(diǎn),就用while不斷的找自己父節(jié)點(diǎn),這個(gè)父節(jié)點(diǎn)要求可以被點(diǎn)擊。(也是是說找最近一個(gè)可以點(diǎn)擊的父節(jié)點(diǎn))*/private void recycle(AccessibilityNodeInfo info) {if (info.getChildCount() == 0) {if (info.getText() != null) {if ("領(lǐng)取紅包".equals(info.getText().toString())||"查看紅包".equals(info.getText().toString())) { // if (info.isClickable()) { // info.performAction(AccessibilityNodeInfo.ACTION_CLICK); // }AccessibilityNodeInfo parent = info.getParent();while (parent != null) {if (parent.isClickable()) {parents.add(parent);//找到了添加到列表并推出循環(huán),否則繼續(xù)往上找父節(jié)點(diǎn)。break;}parent = parent.getParent();}}}} else {for (int i = 0; i < info.getChildCount(); i++) {if (info.getChild(i) != null) {recycle(info.getChild(i));}}}}@Overridepublic boolean onUnbind(Intent intent) {Toast.makeText(this, "斷開與微信綁定,停止監(jiān)聽", Toast.LENGTH_SHORT).show();return super.onUnbind(intent);} }這里我們對(duì)一些語句做一些解釋:
1.getRootInActiveWindow(); //獲取當(dāng)前窗口的根節(jié)點(diǎn)。 2.findAccessibilityNodeInfosByViewId//根據(jù)id獲取節(jié)點(diǎn),獲取的是一個(gè)列表 3.AccessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);//模擬點(diǎn)擊事件 4.AccessibilityNodeInfo.getParent()獲取父節(jié)點(diǎn)2.MainActivity類的編寫
public class MainActivity extends Activity implements OnClickListener{private Button settingBtn;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);findView();}private void findView(){settingBtn = (Button) findViewById(R.id.settingBtn);settingBtn.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch(v.getId()){case R.id.settingBtn://跳轉(zhuǎn)到輔助設(shè)置頁Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);startActivity(intent);break;}} }3.看一下menifest中的注冊(cè)
<service android:name="com.czh.service.RobService"android:enabled="true"android:exported="true"android:label="@string/servicelable"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>這里對(duì)menifest講解一下:
android:label="@string/servicelable"是服務(wù)描述效果如下:
這一塊是固定的寫法,沒什么好說的。注意權(quán)限是必須要加上!
<meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility" />meta-data是配置文件,配置信息在accessibility.xml中
3.看一下accessibility.xml文件
在res文件夾下面新建一個(gè)文件夾xml –> 在xml文件夾中新建一個(gè)accessibility.xml文件。
對(duì)這個(gè)accessibility.xml講解一下:
1.android:accessibilityEventTypes 表示監(jiān)聽的事件類型,這本章中我們監(jiān)聽通知,屏幕狀態(tài)變化,屏幕內(nèi)容變化。typeAllMask表示監(jiān)聽所有類型 2.android:accessibilityFeedbackType 表示手機(jī)用什么方式將信息反饋給用戶,語音震動(dòng)等等,這里默認(rèn); 3.android:canRetrieveWindowContent="true",允許檢索窗口內(nèi)容,這一條必須要加上。 4.android:description 描述 5.android:packageNames="com.tencent.mm"這里我們監(jiān)聽微信的包名,如果你想監(jiān)聽多個(gè)App的話可以這樣寫: android:packageNames="com.tencent.mm|com.tencent.qq"在配置文件中android:description添加后的效果圖是這樣的:
3.看一下strings.xml文件
<?xml version="1.0" encoding="utf-8"?> <resources><string name="app_name">微信搶紅包</string><string name="servicelable">微信自動(dòng)搶紅包</string><string name="description">本插件僅供娛樂,切勿用于不當(dāng)用途,出現(xiàn)問題后果自負(fù)!</string> </resources>3.補(bǔ)充
大家可能在想,findAccessibilityNodeInfosByViewId()方法中的ID是如何知道的呢?下面我們就來解釋一下:
在devices中有這么一個(gè)按鈕,點(diǎn)擊進(jìn)入,效果圖如下:
進(jìn)入之后你會(huì)看到下面界面:
通過圖你可以看出,左面是界面,又面是布局和控件的詳細(xì)信息(我們可以通過這個(gè)來學(xué)習(xí)人家的布局規(guī)律),當(dāng)你點(diǎn)擊某個(gè)控件的時(shí)候,右面就會(huì)顯示該控件的信息,圖中我們標(biāo)記出來了。
4.不足之處
我在寫這個(gè)東西上時(shí)候有一個(gè)bug,一直不知道如何解決:新版微信無法區(qū)分未拆紅包和已拆紅包。就是因?yàn)檫@個(gè)原因程序會(huì)把已拆紅包當(dāng)成未拆紅包來拆,導(dǎo)致死循環(huán),如果有知道解決方案的朋友請(qǐng)告知我,不勝感激!
5.結(jié)尾
在文章的結(jié)尾奉送上代碼,方便大家對(duì)照著學(xué)習(xí)。
在技術(shù)上我依舊是個(gè)小渣渣,加油勉勵(lì)自己。
總結(jié)
以上是生活随笔為你收集整理的android拆轮子系列之一步一步教你写微信抢红包插件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python安装环境傻瓜式安装_前后端分
- 下一篇: python | 查看pip支持的文件名