Android中微信搶紅包插件原理解析和開發(fā)實(shí)現(xiàn)

一南誊、前言

自從去年中微信添加搶紅包的功能了罪,微信的電商之旅算是正式開始正式火爆起來飒赃。但是作為Android開發(fā)者來說利花,我們?cè)趽尲t包的同時(shí)意識(shí)到了很多問題,就是手動(dòng)去搶紅包的速度慢了载佳,當(dāng)然這些有很多原因?qū)е铝顺词隆;蛟S是網(wǎng)絡(luò)的原因蔫慧,而且這個(gè)也是最大的原因挠乳。但是其他的不可忽略的因素也是要考慮到進(jìn)去的,比如在手機(jī)充電鎖屏的時(shí)候,我們并不知道有人已經(jīng)開始發(fā)紅包了睡扬,那么這時(shí)候也是讓我們喪失了一大批紅包的原因盟蚣。那么關(guān)于網(wǎng)絡(luò)的問題,我們開發(fā)者可能用相關(guān)技術(shù)無法解決(當(dāng)然在Google和Facebook看來的話卖怜,他們的理想是能夠在任何地方都能連接互聯(lián)網(wǎng)屎开,當(dāng)然在偏遠(yuǎn)的農(nóng)村也是,不過我們期待他們有一天能夠普及開來马靠。到時(shí)候才是真正的互聯(lián)網(wǎng))奄抽。扯得有點(diǎn)遠(yuǎn)了。我們回歸到正題虑粥,今天我們來看看使用技術(shù)來解決其他非網(wǎng)絡(luò)問題如孝。在充電鎖屏的時(shí)候也可以自動(dòng)幫我們搶紅包。而且你要知道娩贷,機(jī)器搶紅包的準(zhǔn)確率是100%的第晰,這個(gè)也許就是人和機(jī)器的區(qū)別。那么保證搶得準(zhǔn)確率是100%的話彬祖,那就依賴于我們高效準(zhǔn)確的算法實(shí)現(xiàn)了茁瘦。下面就來看看原理實(shí)現(xiàn)。

當(dāng)去年我看到搶紅包那么火爆的時(shí)候储笑,當(dāng)時(shí)作為一個(gè)開發(fā)者心里是多么渴望開發(fā)一個(gè)插件出來甜熔,可是當(dāng)時(shí)我們能想到的就是使用:

adb shell monkey

命令去模擬點(diǎn)擊屏幕,但是那種方式有一個(gè)問題就是是無頭緒的盲目點(diǎn)擊突倍,所以幾乎會(huì)出現(xiàn)誤點(diǎn)腔稀,點(diǎn)擊成功率極其低下。所以當(dāng)時(shí)就沒有想到其他方法了羽历,因?yàn)樽罱隽擞嘘P(guān)輔助功能相關(guān)的工作的時(shí)候焊虏,那么就發(fā)現(xiàn)這個(gè)功能可以用于搶紅包。

其實(shí)現(xiàn)在我們可以去各大市場(chǎng)搜索一下看到秕磷,有很多搶紅包的插件了诵闭。當(dāng)然我們并不是用于商業(yè)化,這里只是來解析一下原理澎嚣。我們會(huì)發(fā)現(xiàn)那些插件都有一個(gè)共同的特點(diǎn)是:第一步都是引導(dǎo)用戶去開啟輔助功能疏尿。

二、原理解析

關(guān)于輔助功能(AccessibilityService)易桃,如果又不了解的同學(xué)可以去Google一下褥琐,這個(gè)功能其實(shí)很有用的,但是他的出現(xiàn)的出發(fā)點(diǎn)是給那些肢體上有障礙的人使用的晤郑,比如手指不健全的用戶踩衩,怎么才能滑動(dòng)屏幕嚼鹉,然后打開一個(gè)應(yīng)用呢?那么輔助功能就是干這些事驱富,他的功能其實(shí)就是可以概括兩句話:

第一、尋找到我們想要的View節(jié)點(diǎn)

第二匹舞、然后模擬點(diǎn)擊褐鸥,實(shí)現(xiàn)特定功能

我們知道Android中的View體系是一個(gè)樹形結(jié)構(gòu),那么每一個(gè)View就是一個(gè)節(jié)點(diǎn)赐稽。所以我們可以查找到指定的節(jié)點(diǎn)叫榕,那么我們?cè)撊绾尾檎业轿覀兿胍墓?jié)點(diǎn)呢?這里我們先看一下輔助功能(AccessibilityService)的用法

第一步姊舵、我們需要集成AccessibilityService類
我們需要自定一個(gè)Service然后繼承AccessibilityService晰绎,當(dāng)然還需要在AndroidManifest.xml中聲明這個(gè)服務(wù):


第二步、聲明權(quán)限和配置
這個(gè)服務(wù)需要注明一個(gè)權(quán)限:

android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"

當(dāng)然還要一個(gè)meta-data的聲明括丁,這個(gè)聲明是對(duì)這個(gè)AccessibilityService的配置荞下。我們看一下配置文件內(nèi)容:

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

這里我們看到有很多選項(xiàng),我們看一下常用的幾個(gè)屬性:

1、android:accessibilityEventTypes="typeAllMask"
看屬性名也差不多可以明白史飞,這個(gè)是用來設(shè)置響應(yīng)事件的類型尖昏,typeAllMask當(dāng)然就是響應(yīng)所有類型的事件了。當(dāng)然還有單擊构资、長(zhǎng)按抽诉、滑動(dòng)等。

2吐绵、android:accessibilityFeedbackType="feedbackSpoken"
設(shè)置回饋給用戶的方式迹淌,有語(yǔ)音播出和振動(dòng)〖旱ィ可以配置一些TTS引擎唉窃,讓它實(shí)現(xiàn)發(fā)音。

3荷鼠、android:notificationTimeout="100"
響應(yīng)時(shí)間的設(shè)置就不用多說了

4句携、android:packageNames="com.example.android.apis"
可以指定響應(yīng)某個(gè)應(yīng)用的事件,這里因?yàn)橐憫?yīng)所有應(yīng)用的事件允乐,所以不填矮嫉,默認(rèn)就是響應(yīng)所有應(yīng)用的事件。比如我們寫一個(gè)微信搶紅包的輔助程序牍疏,就可以在這里填寫微信的包名蠢笋,便可以監(jiān)聽微信產(chǎn)生的事件了。

注意:

1鳞陨、我們這些配置信息除了在xml中定義昨寞,同樣也可以在代碼中定義瞻惋,我們一般都是在onServiceConnected()方法里進(jìn)行

@Override
protected void onServiceConnected() {
    AccessibilityServiceInfo info = getServiceInfo();
    info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
    info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;  
    info.notificationTimeout = 100;  
    setServiceInfo(info); 
    info.packageNames = new String[]{"xxx.xxx.xxx", "yyy.yyy.yyy","...."};
    setServiceInfo(info);
    super.onServiceConnected();
}

2、這里我們一般都會(huì)在這里寫上我們需要監(jiān)聽的應(yīng)用的包名援岩,但是有時(shí)候我們需要監(jiān)聽多個(gè)應(yīng)用歼狼,那么這時(shí)候我們?cè)撛趺崔k呢?

這時(shí)候我們可以這么做:

第一種:我們?cè)诖a中注冊(cè)多個(gè)應(yīng)用的包名享怀,從而可以監(jiān)聽多個(gè)應(yīng)用

@Override
protected void onServiceConnected() {
    AccessibilityServiceInfo info = getServiceInfo();
    //這里可以設(shè)置多個(gè)包名羽峰,監(jiān)聽多個(gè)應(yīng)用
    info.packageNames = new String[]{"xxx.xxx.xxx", "yyy.yyy.yyy","...."};
    setServiceInfo(info);
    super.onServiceConnected();
}

第二種:我們?cè)趏nAccessibilityEvent事件監(jiān)聽的方法中做包名的過濾(這種方式最常用)

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
    String pkgName = event.getPackageName().toString();
    if("xxx.xxx.xxx".equals(pkgName)){
 
    }else if("yyy.yyy.yyy".equals(pkgName)){
 
    }else if("....".equals(pkgName)){
 
    }
}

第三步、在onAccessibilityEvent方法中監(jiān)聽指定的事件
比如我們需要監(jiān)聽有通知欄消息的事件:

@Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        int eventType = event.getEventType();
        switch (eventType) {
        case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
            //.......
        }
}

這個(gè)事件類型很多的添瓷,我們可以查看AccessibilityEvent類的源碼:

@Deprecated
public static final int MAX_TEXT_LENGTH = 500;
 
/**
 * Represents the event of clicking on a {@link android.view.View} like
 * {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc.
 */
public static final int TYPE_VIEW_CLICKED = 0x00000001;
 
/**
 * Represents the event of long clicking on a {@link android.view.View} like
 * {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc.
 */
public static final int TYPE_VIEW_LONG_CLICKED = 0x00000002;
 
/**
 * Represents the event of selecting an item usually in the context of an
 * {@link android.widget.AdapterView}.
 */
public static final int TYPE_VIEW_SELECTED = 0x00000004;
 
/**
 * Represents the event of setting input focus of a {@link android.view.View}.
 */
public static final int TYPE_VIEW_FOCUSED = 0x00000008;
 
/**
 * Represents the event of changing the text of an {@link android.widget.EditText}.
 */
public static final int TYPE_VIEW_TEXT_CHANGED = 0x00000010;
 
/**
 * Represents the event of opening a {@link android.widget.PopupWindow},
 * {@link android.view.Menu}, {@link android.app.Dialog}, etc.
 */
public static final int TYPE_WINDOW_STATE_CHANGED = 0x00000020;
 
/**
 * Represents the event showing a {@link android.app.Notification}.
 */
public static final int TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040;
 
/**
 * Represents the event of a hover enter over a {@link android.view.View}.
 */
public static final int TYPE_VIEW_HOVER_ENTER = 0x00000080;
 
/**
 * Represents the event of a hover exit over a {@link android.view.View}.
 */
public static final int TYPE_VIEW_HOVER_EXIT = 0x00000100;
 
/**
 * Represents the event of starting a touch exploration gesture.
 */
public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 0x00000200;
 
/**
 * Represents the event of ending a touch exploration gesture.
 */
public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 0x00000400;
 
/**
 * Represents the event of changing the content of a window and more
 * specifically the sub-tree rooted at the event's source.
 */
public static final int TYPE_WINDOW_CONTENT_CHANGED = 0x00000800;
 
/**
 * Represents the event of scrolling a view.
 */
public static final int TYPE_VIEW_SCROLLED = 0x00001000;
 
/**
 * Represents the event of changing the selection in an {@link android.widget.EditText}.
 */
public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 0x00002000;
 
/**
 * Represents the event of an application making an announcement.
 */
public static final int TYPE_ANNOUNCEMENT = 0x00004000;
 
/**
 * Represents the event of gaining accessibility focus.
 */
public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 0x00008000;
 
/**
 * Represents the event of clearing accessibility focus.
 */
public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 0x00010000;
 
/**
 * Represents the event of traversing the text of a view at a given movement granularity.
 */
public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 0x00020000;
 
/**
 * Represents the event of beginning gesture detection.
 */
public static final int TYPE_GESTURE_DETECTION_START = 0x00040000;
 
/**
 * Represents the event of ending gesture detection.
 */
public static final int TYPE_GESTURE_DETECTION_END = 0x00080000;
 
/**
 * Represents the event of the user starting to touch the screen.
 */
public static final int TYPE_TOUCH_INTERACTION_START = 0x00100000;
 
/**
 * Represents the event of the user ending to touch the screen.
 */
public static final int TYPE_TOUCH_INTERACTION_END = 0x00200000;
 
/**
 * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
 * The type of change is not defined.
 */
public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0x00000000;
 
/**
 * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
 * A node in the subtree rooted at the source node was added or removed.
 */
public static final int CONTENT_CHANGE_TYPE_SUBTREE = 0x00000001;
 
/**
 * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
 * The node's text changed.
 */
public static final int CONTENT_CHANGE_TYPE_TEXT = 0x00000002;
 
/**
 * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
 * The node's content description changed.
 */
public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000004;

這里有很多事件梅屉,這些事件我們通過名字就可以看出來有很多我們可能都知道,比如當(dāng)窗口發(fā)生變化的時(shí)候鳞贷,當(dāng)某個(gè)View被點(diǎn)擊了坯汤,被滾動(dòng)了等消息都是可以知道的。那么我們有了這些事件我們就可以做我們的事情了搀愧,因?yàn)槲覀冎朗录|發(fā)了惰聂。

第四步、查找到我們想要處理的節(jié)點(diǎn)View
這里系統(tǒng)提供了兩個(gè)方法讓我們來進(jìn)行查找想要的節(jié)點(diǎn)View

第一種是通過節(jié)點(diǎn)View的Text內(nèi)容來查找

findAccessibilityNodeInfosByText("查找內(nèi)容")

這種方式查找妈橄,就是像TextView,Button等View有文本內(nèi)容的庶近,可以使用這種方式快速的找到。

第二種是通過節(jié)點(diǎn)View在xml布局中的id名稱

findAccessibilityNodeInfosByViewId("@id/xxx")

這個(gè)一般很難知道眷蚓,但是我們?cè)诓檎蚁到y(tǒng)控件的時(shí)候還是可以做的鼻种,因?yàn)橄到y(tǒng)的控件的id是可以知道的,而且是統(tǒng)一的沙热。
(關(guān)于這兩個(gè)方法我們?cè)趯懢W(wǎng)頁(yè)爬蟲程序的時(shí)候可能知道叉钥,在html中通過tag/name/id等信息可以找到一個(gè)節(jié)點(diǎn),原理都類似)

第五步篙贸、模擬點(diǎn)擊指定事件
我們找到我們想要的View節(jié)點(diǎn)投队,調(diào)用方法模擬事件:

performAction(AccessibilityNodeInfo.ACTION_CLICK)

調(diào)用這個(gè)方法即可,當(dāng)然這里的參數(shù)就是指定事件的名稱爵川,這個(gè)和AccessibilityEvent中監(jiān)聽的那些事件是一一對(duì)應(yīng)的敷鸦,這里是模擬點(diǎn)擊事件,我們當(dāng)然可以模擬View的滾動(dòng)事件寝贡,長(zhǎng)按事件等扒披。

三、實(shí)戰(zhàn)案例:微信搶紅包插件

上面我們就介紹了一個(gè)輔助功能開發(fā)的具體步驟圃泡,那么下面就通過一個(gè)簡(jiǎn)單的例子碟案,來實(shí)戰(zhàn)一下

例子:微信自動(dòng)搶紅包插件

首先我們來看一下微信搶紅包的流程:

第一步、我們?cè)谕ㄖ獧跁?huì)接收到一個(gè)微信紅包的消息

我們監(jiān)聽通知欄事件:

AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED

然后查看通知欄的消息中是否有:[微信紅包] 的文本內(nèi)容

是的話颇蜡,就走進(jìn)入第二步

第二步价说、我們模擬打開通知欄

打開微信如下圖:



我們查找包含有:領(lǐng)取紅包 的文本內(nèi)容的節(jié)點(diǎn)View,然后模擬點(diǎn)擊辆亏,進(jìn)入第三步:

第三步、我們點(diǎn)擊領(lǐng)取紅包

如下圖:

這里我們?cè)诓檎野校翰鸺t包 的文本內(nèi)容的節(jié)點(diǎn)View,然后模擬點(diǎn)擊



下面我們來看一下代碼中的具體實(shí)現(xiàn):

package krelve.demo.rob;
 
import java.util.List;
 
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
 
public class RobMoney extends AccessibilityService {
 
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        int eventType = event.getEventType();
        switch (eventType) {
        //第一步:監(jiān)聽通知欄消息
        case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
            List<CharSequence> texts = event.getText();
            if (!texts.isEmpty()) {
                for (CharSequence text : texts) {
                    String content = text.toString();
                    Log.i("demo", "text:"+content);
                    if (content.contains("[微信紅包]")) {
                        //模擬打開通知欄消息
                        if (event.getParcelableData() != null
                                && 
                            event.getParcelableData() instanceof Notification) {
                            Notification notification = (Notification) event.getParcelableData();
                            PendingIntent pendingIntent = notification.contentIntent;
                            try {
                                pendingIntent.send();
                            } catch (CanceledException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
            break;
        //第二步:監(jiān)聽是否進(jìn)入微信紅包消息界面
        case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
            String className = event.getClassName().toString();
            if (className.equals("com.tencent.mm.ui.LauncherUI")) {
                //開始搶紅包
                getPacket();
            } else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI")) {
                //開始打開紅包
                openPacket();
            }
            break;
        }
    }
 
    /**
     * 查找到
     */
    @SuppressLint("NewApi")
    private void openPacket() {
        AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
        if (nodeInfo != null) {
            List<AccessibilityNodeInfo> list = nodeInfo
                    .findAccessibilityNodeInfosByText("搶紅包");
            for (AccessibilityNodeInfo n : list) {
                n.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }
        }
 
    }
 
    @SuppressLint("NewApi")
    private void getPacket() {
        AccessibilityNodeInfo rootNode = getRootInActiveWindow();
        recycle(rootNode);
    }
    
    /**
     * 打印一個(gè)節(jié)點(diǎn)的結(jié)構(gòu)
     * @param info
     */
    @SuppressLint("NewApi")
    public void recycle(AccessibilityNodeInfo info) {  
        if (info.getChildCount() == 0) { 
            if(info.getText() != null){
                if("領(lǐng)取紅包".equals(info.getText().toString())){
                    //這里有一個(gè)問題需要注意鳖目,就是需要找到一個(gè)可以點(diǎn)擊的View
                    Log.i("demo", "Click"+",isClick:"+info.isClickable());
                    info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                    AccessibilityNodeInfo parent = info.getParent();
                    while(parent != null){
                        Log.i("demo", "parent isClick:"+parent.isClickable());
                        if(parent.isClickable()){
                            parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                            break;
                        }
                        parent = parent.getParent();
                    }
                    
                }
            }
            
        } else {  
            for (int i = 0; i < info.getChildCount(); i++) {  
                if(info.getChild(i)!=null){  
                    recycle(info.getChild(i));  
                }  
            }  
        }  
    }  
 
    @Override
    public void onInterrupt() {
    }
 
    
}

代碼沒什么好說的了扮叨,按照我們之前說的三個(gè)步驟來就可以了,但是這里需要注意點(diǎn)細(xì)節(jié)上的問題:
1领迈、我們?cè)诒O(jiān)聽到通知欄的消息的時(shí)候甫匹,調(diào)用如下代碼來進(jìn)行通知欄的消息點(diǎn)擊

if (content.contains("[微信紅包]")) {
    //模擬打開通知欄消息
    if (event.getParcelableData() != null
            && 
            event.getParcelableData() instanceof Notification) {
        Notification notification = (Notification) event.getParcelableData();
        PendingIntent pendingIntent = notification.contentIntent;
        try {
            pendingIntent.send();
        } catch (CanceledException e) {
            e.printStackTrace();
        }
    }
}

2、我們?cè)谀M點(diǎn)擊通知欄消息之后惦费,還是需要監(jiān)聽:AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED 這個(gè)事件,這個(gè)事件我們以后會(huì)經(jīng)常用到抢韭,這個(gè)事件就是在窗口發(fā)生改變的時(shí)候發(fā)出來的事件薪贫,很常用的,比如我們可以通過這個(gè)事件來監(jiān)聽TopActivity刻恭,然后得到包名瞧省,這也是一個(gè)實(shí)現(xiàn)應(yīng)用鎖的一個(gè)原理。

3鳍贾、我們?cè)诓檎翌I(lǐng)取紅包的時(shí)候鞍匾,模擬點(diǎn)擊的時(shí)候做了一個(gè)工作,就是從“領(lǐng)取紅包”文本的控件View網(wǎng)上查找骑科,查找到一個(gè)可以點(diǎn)擊的View出來橡淑,然后模擬點(diǎn)擊

if(info.getText() != null){
    if("領(lǐng)取紅包".equals(info.getText().toString())){
        //這里有一個(gè)問題需要注意,就是需要找到一個(gè)可以點(diǎn)擊的View
        Log.i("demo", "Click"+",isClick:"+info.isClickable());
        info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
        AccessibilityNodeInfo parent = info.getParent();
        while(parent != null){
            Log.i("demo", "parent isClick:"+parent.isClickable());
            if(parent.isClickable()){
                parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                break;
            }
            parent = parent.getParent();
        }
 
    }
}

這里為什么這么做咆爽,其實(shí)原理很簡(jiǎn)單梁棠,因?yàn)槲覀儾恢牢⑿潘慕缑娌季郑膊恢浪麑?duì)哪個(gè)View進(jìn)行了setOnClickListener斗埂。我們可以寫一個(gè)例子符糊,performAction方法只對(duì)調(diào)用了setOnClickListener方法的View模擬點(diǎn)擊才有效,其實(shí)看View的源碼也是可以看出來的.這里就不多解釋了呛凶。所以我們就需要得到一個(gè)View節(jié)點(diǎn)之后男娄,從下往上找,直到找到一個(gè)可以click的View為止漾稀。

技術(shù)延展:

我們其實(shí)還可以使用DDMS工具里的Dump View Hierarchy For UI Automator 去分析微信UI結(jié)構(gòu)模闲,這個(gè)方法也是我后面才發(fā)現(xiàn)的,比上面的代碼更有效县好,如下圖:



這里我們可以看到View的詳細(xì)布局围橡,還有每個(gè)View的屬性,還有很重要的信息resource-id缕贡,這個(gè)就是我們?cè)趚ml中定義的id翁授,這個(gè)id我們也可以使用前面說到的findAccessibilityNodeInfosByViewId("@id/xxx")來查找控件了

這個(gè)也算是學(xué)習(xí)了拣播,學(xué)會(huì)使用DDMS來分析View結(jié)構(gòu)。

四收擦、延展

關(guān)于微信搶紅包的原理解析上面已經(jīng)做了分析了贮配,但是要想做到極致,這里還有很多問題的塞赂,比如我們還需要過濾一些已經(jīng)領(lǐng)取過的紅包泪勒,這樣的話效率也是很高的。這個(gè)都是算法精確的問題了宴猾,我想在這里說的是圆存,我們不僅可以用輔助功能來實(shí)現(xiàn)搶紅包,還可以實(shí)現(xiàn)很多功能仇哆,比如

1沦辙、靜默安裝

關(guān)于靜默安裝的實(shí)現(xiàn),之前的做法是讹剔,參見這篇文章:

http://blog.csdn.net/jiangwei0910410003/article/details/36427963

在這篇文章中我介紹了很多方法來實(shí)現(xiàn)靜默安裝油讯,但是都是有一個(gè)限制,那就是root,或者是獲取到systemId延欠。但是對(duì)于這兩個(gè)要求陌兑,我們或許很難得到,那么現(xiàn)在如果有了輔助功能由捎,我們就好做了:



我們可以監(jiān)聽系統(tǒng)的這個(gè)安裝界面兔综,然后得到安裝節(jié)點(diǎn)View,然后模擬點(diǎn)擊即可,卸載也是同樣的原理

2隅俘、強(qiáng)制停止應(yīng)用

我們知道Android中停止應(yīng)用有很多方法邻奠,kill進(jìn)程,stopService,但是這些方法为居,有一些應(yīng)用它們都是有對(duì)策的碌宴,那么我們之前用到的強(qiáng)制停止的方法是獲取root權(quán)限調(diào)用系統(tǒng)的forceStop的api來停止,但是前提還是有root蒙畴。那么現(xiàn)在如果我們有了輔助功能的話贰镣,我們可以這么做:



我們可以監(jiān)聽系統(tǒng)的應(yīng)用詳情頁(yè)面,然后找到:結(jié)束運(yùn)行的節(jié)點(diǎn)View膳凝,然后模擬點(diǎn)擊即可

當(dāng)然上面我就說了兩個(gè)簡(jiǎn)單的例子碑隆,還有很多輔助功能都是可以做的。他的好處就是不需要root權(quán)限蹬音。但是他也是需要用戶授權(quán)的:


如果用戶沒有授權(quán)的話上煤,那么所有的工作都沒辦法開始了,所以說這個(gè)方法也不是萬能的著淆。當(dāng)然說句題外話:有了輔助功能的話劫狠,他的危險(xiǎn)性比root之后的危險(xiǎn)性更大拴疤,比如我們上面的搶紅包插件,其實(shí)我們稍作修改独泞,就可以獲取微信通訊錄信息呐矾,微信支付的密碼。這些事都是可以做的懦砂,所以說蜒犯,我們?cè)谧鳛橛脩舻臅r(shí)候,進(jìn)行授權(quán)的時(shí)候還是需要三思而后行荞膘。

五罚随、總結(jié)

關(guān)于輔助功能,之前沒有太多的接觸羽资,是在一次工作中用到了這個(gè)功能毫炉,就去學(xué)習(xí)了一下,作為自己的興趣削罩,就延展了學(xué)習(xí)了如何寫一個(gè)微信搶紅包的插件,同時(shí)可以考慮了使用輔助功能能夠做我們之前需要root做的事情费奸。當(dāng)然輔助功能是google對(duì)于肢體上有障礙的人開發(fā)出來的一個(gè)功能弥激,我們開發(fā)者或許使用這個(gè)功能,可以做一下產(chǎn)品的拓展功能愿阐,當(dāng)然這些是google沒有想到的事情微服,但是這個(gè)至少是我們開發(fā)者在以后的開發(fā)道路上的一個(gè)解決問題的一個(gè)辦法和途徑,謹(jǐn)記此功能缨历!


視頻資料.jpg
高清大綱與資料.png

+qq群:853967238以蕴。獲取以上高清技術(shù)思維圖,以及相關(guān)技術(shù)的免費(fèi)視頻學(xué)習(xí)資料辛孵。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末丛肮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子魄缚,更是在濱河造成了極大的恐慌宝与,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冶匹,死亡現(xiàn)場(chǎng)離奇詭異习劫,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)嚼隘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門诽里,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人飞蛹,你說我怎么就攤上這事谤狡【难郏” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵豌汇,是天一觀的道長(zhǎng)幢炸。 經(jīng)常有香客問我,道長(zhǎng)拒贱,這世上最難降的妖魔是什么宛徊? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮逻澳,結(jié)果婚禮上闸天,老公的妹妹穿的比我還像新娘。我一直安慰自己斜做,他們只是感情好苞氮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瓤逼,像睡著了一般笼吟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上霸旗,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天贷帮,我揣著相機(jī)與錄音,去河邊找鬼诱告。 笑死撵枢,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的精居。 我是一名探鬼主播锄禽,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼靴姿!你這毒婦竟也來了沃但?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤佛吓,失蹤者是張志新(化名)和其女友劉穎绽慈,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辈毯,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坝疼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谆沃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钝凶。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出耕陷,到底是詐尸還是另有隱情掂名,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布哟沫,位于F島的核電站饺蔑,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嗜诀。R本人自食惡果不足惜猾警,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望隆敢。 院中可真熱鬧发皿,春花似錦、人聲如沸拂蝎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)温自。三九已至玄货,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悼泌,已是汗流浹背誉结。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留券躁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓掉盅,卻偏偏與公主長(zhǎng)得像也拜,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子趾痘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容