Android進(jìn)階 - 監(jiān)視活動(dòng)窗口

摘要

在Android程序的開(kāi)發(fā)維護(hù)過(guò)程中,我們可能經(jīng)常需要知道自己所看到的界面處于哪一個(gè)Activity中,相信大部分程序員的做法是“在基類(lèi)里打Log”,很傳統(tǒng)沒(méi)毛病o(╯□╰)o....

前幾天,在應(yīng)用寶的應(yīng)用商店內(nèi)發(fā)現(xiàn)了一個(gè)很有意思的APP - 當(dāng)前Activity,可以顯示出當(dāng)前界面上顯示的 應(yīng)用包名Activity類(lèi)名。然后,一直很好奇是如何實(shí)現(xiàn)的,最近抽時(shí)間研究了一下。

下面是研究成果:

image_example.png

開(kāi)源項(xiàng)目CurrentActivity地址:

https://github.com/sinawangnan7/CurrentActivity

歡迎Star...(??????)??

正文

本文主要講述兩部分內(nèi)容:

1、CurrentActivity的使用場(chǎng)景

2、CurrentActivity的實(shí)現(xiàn)思路

CurrentActivity的使用場(chǎng)景

1、定位自己APP的頁(yè)面,獲取類(lèi)名稱(chēng)

這應(yīng)該是我們經(jīng)常碰到的問(wèn)題,測(cè)試人員經(jīng)常拿著出Bug的頁(yè)面詢(xún)問(wèn)“開(kāi)發(fā)人員”,如果這個(gè)頁(yè)面還不是“此開(kāi)發(fā)人員”寫(xiě)的,估計(jì)查起來(lái)就費(fèi)勁了。此工具能幫助開(kāi)發(fā)人員提高查找效率。

2、分析其他APP命名及功能

可以查看其他APP的頁(yè)面路徑和命名,給自己開(kāi)發(fā)做個(gè)參考(下面以微信支付寶為例)。

(1) 微信

wx.png

觀察上圖,可以看到 微信 的Activity在命名時(shí)是以 "UI" 為后綴進(jìn)行標(biāo)識(shí)的,類(lèi)在分包時(shí)也放在了ui包目錄下。另外,還有一個(gè)比較有意思的發(fā)現(xiàn),微信的“主界面”和“聊天界面”用的是同一個(gè)Activity

(2) 支付寶

alipay.png

筆者公司開(kāi)發(fā)的APP和支付寶APP類(lèi)似,也是做 互聯(lián)網(wǎng)金融 的。所以,產(chǎn)品經(jīng)理在設(shè)計(jì)功能和流程時(shí)經(jīng)常會(huì)“參考”支付寶是如何實(shí)現(xiàn)的,比如需要確定 哪些頁(yè)面用原生做,哪些需要用H5來(lái)做。但是,支付寶的原生和H5在使用體驗(yàn)上很難感知出來(lái)(這里支付寶做得很贊),這時(shí)這個(gè)工具就可以派上用場(chǎng)了,支付寶展示H5的頁(yè)面用的是H5Activity

最近,筆者需要做一個(gè)類(lèi)似支付寶的支付鍵盤(pán),一開(kāi)始以為是純Dialog實(shí)現(xiàn)的,最后發(fā)現(xiàn)實(shí)際上用Activity包了一層。這其實(shí)也并不意外,第三方APP在吊起“支付寶支付”時(shí),用戶(hù)如果安裝了支付寶,吊起的也是這個(gè)FlyBirdWindowActivity。

CurrentActivity的實(shí)現(xiàn)思路

這個(gè)監(jiān)視活動(dòng)的窗口是如何實(shí)現(xiàn)的?

本文只講述 思路和核心代碼,完整代碼請(qǐng)參看GitHub開(kāi)源項(xiàng)目:
CurrentActivity

實(shí)現(xiàn)這個(gè)監(jiān)視窗口需要解決兩個(gè)問(wèn)題:

  • 1、如何向界面頂部添加一個(gè)顯示窗口?

  • 2、如何監(jiān)聽(tīng)?wèi)?yīng)用切換、Activity切換?


第一個(gè)問(wèn)題很簡(jiǎn)單,通過(guò)WindowManager添加一個(gè)TextView即可, 但是需要注意下懸浮窗權(quán)限問(wèn)題,這個(gè)問(wèn)題很多博客都給出了解決方案,筆者簡(jiǎn)單說(shuō)下,就不在贅述了。

筆者做了一個(gè)簡(jiǎn)單的窗口視圖管理器:

完整代碼:WindowViewContainer.java

創(chuàng)建窗口的核心代碼如下:

/**
 * 添加窗口視圖
 */
private void addView() {
    // 創(chuàng)建布局參數(shù)
    mParams = new WindowManager.LayoutParams();
    // 獲取窗口管理器
    mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
    // 設(shè)置類(lèi)型
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        // Android O 以上,使用TYPE_APPLICATION_OVERLAY彈窗類(lèi)型
        mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
    } else {
        // Android O 以下,使用TYPE_SYSTEM_ALERT彈窗類(lèi)型
        mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
    }
    // 設(shè)置標(biāo)簽(FLAG_NOT_FOCUSABLE表示窗口不會(huì)獲取焦點(diǎn);FLAG_NOT_TOUCHABLE表示窗口不會(huì)接收Touch事件,即將Touch事件向下層分發(fā))
    mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
    // 設(shè)置位圖模式 (PixelFormat.RGBA_8888可以使背景透明。不設(shè)置默認(rèn)PixelFormat.OPAQUE,即不透明)
    mParams.format = PixelFormat.RGBA_8888;
    // 設(shè)置分布位置(距左對(duì)齊 + 距頂對(duì)齊)
    mParams.gravity = Gravity.LEFT | Gravity.TOP;
    // 設(shè)置布局寬/高為自適應(yīng)
    mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
    mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
    // 添加TextView
    mWindowManager.addView(mTextView, mParams);
    // 記錄視圖已被添加、顯示
    isAdded = true;
    isShow = true;
}

提醒:
1.注意懸浮窗權(quán)限問(wèn)題,可參考 AndroidManifest.xml 中的權(quán)限聲明。
2.適配Android 8.0,請(qǐng)使用TYPE_APPLICATION_OVERLAY彈窗類(lèi)型。


第二個(gè)問(wèn)題:如何監(jiān)聽(tīng)?wèi)?yīng)用切換、Activity切換?筆者查了很長(zhǎng)時(shí)間,最后發(fā)現(xiàn)了一個(gè)叫 AccessibilityService(輔助服務(wù))的東西,了解完 AccessibilityService 感覺(jué)好像發(fā)現(xiàn)了新大陸,其實(shí)這個(gè)“輔助服務(wù)”使用最為出名的是“微信自動(dòng)搶紅包插件”。但是,今天筆者只是簡(jiǎn)單介紹下AccessibilityService(輔助服務(wù))的入門(mén)使用 - 監(jiān)聽(tīng)窗口改變


輔助服務(wù)創(chuàng)建步驟及使用流程

1.創(chuàng)建自定義AccessibilityService,核心代碼如下:

/**
 * @ClassName: MAccessibilityService
 * @Description: 輔助服務(wù)
 * @Author wangnan7
 * @Date: 2018/4/1
 */

public class MAccessibilityService extends AccessibilityService {

    ......

    /**
     * 服務(wù)連接完成
     */
    @Override
    protected void onServiceConnected() {
        // 添加窗口
        mWindowViewContainer = WindowViewContainer.getInstance(this);
        mWindowViewContainer.addWindowView();
        ......
    }

    ......

    /**
     * 接收輔助服務(wù)事件
     */
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        if (event == null) {
            return;
        }
        switch (event.getEventType()) {
            case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: // 窗口狀態(tài)改變
                if (event.getPackageName() != null && event.getClassName() != null) {
                    // 更新窗口視圖
                    mWindowViewContainer.updateWindowView(event.getPackageName() + "\n" + event.getClassName());
                }
                break;
            default:
                break;
        }
    }

    /**
     * 服務(wù)中斷
     */
    @Override
    public void onInterrupt() {
    }


    /**
     * 服務(wù)退出
     */
    @Override
    public void onDestroy() {
        // 移除窗口,銷(xiāo)毀視圖容器
        mWindowViewContainer.destory();
        ......
    }
}

完整代碼:MAccessibilityService.java

整個(gè)功能的核心都在onAccessibilityEvent(AccessibilityEvent event)這個(gè)方法中,如果開(kāi)啟輔助服務(wù),只要服務(wù)不死,進(jìn)程不掛,當(dāng)前窗口的每次改變我們都能監(jiān)聽(tīng)到。(如果你查看了完整代碼,會(huì)看到筆者添加了一個(gè)Notification,把輔助服務(wù)提升為了前臺(tái)服務(wù),這么做只是簡(jiǎn)單的做下服務(wù)保活)


2.在AndroidManifest.xml文件中聲明該服務(wù), 核心代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.wangnan.currentactivity">

    ......

    <application
        
        ......

        <!-- 輔助服務(wù) -->
        <service
            android:name=".service.MAccessibilityService"
            android:label="當(dāng)前Activity(輔助工具)"
            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>
        ......
    </application>

</manifest>

完整代碼:AndroidManifest.xml

詳細(xì)說(shuō)下輔助服務(wù)的配置聲明:

1.android:name 用于指定服務(wù)名稱(chēng),前面加“.”表示書(shū)寫(xiě)時(shí)省略掉了包名。

2.android:lable 用于指定外部顯示的服務(wù)名稱(chēng)。

3.android:permission 表明服務(wù)向系統(tǒng)請(qǐng)求那些權(quán)限

4.intent-filteraction 表明傳遞給該服務(wù)的哪些Intent需要過(guò)濾出來(lái)進(jìn)行處理

可能會(huì)有人疑問(wèn)為什么要配置3和4,其實(shí)這是源碼文檔要求的,是必須配置的,如下圖所示:

source_code.png

5.meta-data 元數(shù)據(jù),用于配置輔助服務(wù)的詳情信息

其實(shí)這一項(xiàng)在manifest文件中是可配可不配的,只是文檔支持在manifest文件里進(jìn)行配置,如果不在這配置就需要到輔助服務(wù)的Java代碼里去配置了,源碼解釋如下:

source_code2.png

接下來(lái),看下筆者的元數(shù)據(jù)配置文件(android:resource="@xml/accessibility")

完整代碼:accessibility.xml

元數(shù)據(jù)配置文件說(shuō)明,如下圖所示:

source_code3.png

3.開(kāi)啟輔助服務(wù)

開(kāi)啟輔助服務(wù)不能用startService()這種方式打開(kāi),因?yàn)槭褂幂o助服務(wù)是有一定風(fēng)險(xiǎn)的,需要用戶(hù)主動(dòng)授權(quán)(同意開(kāi)啟)

輔助服務(wù)打開(kāi)流程:

安裝APP(含有輔助服務(wù)) -> 輔助功能 -> 服務(wù)(點(diǎn)擊需要開(kāi)啟的服務(wù)) -> 開(kāi)啟

以筆者的手機(jī)(360N5)和 CurrentActivity 為例,打開(kāi)流程如下圖所示:

image_open.png

4.關(guān)閉輔助服務(wù)

輔助功能 -> 服務(wù)(點(diǎn)擊需要關(guān)閉的服務(wù)) -> 關(guān)閉

以筆者的手機(jī)(360N5)和 CurrentActivity 為例,關(guān)閉流程如下圖所示:

image_close.png

CurrentActivity的詳細(xì)使用方法和源碼請(qǐng)參看Github:

https://github.com/sinawangnan7/CurrentActivity

其他

1.AccessibilityService位于在系統(tǒng)設(shè)置里,有些手機(jī)翻譯成“輔助服務(wù)”,有些手機(jī)翻譯成“無(wú)障礙”

2.AccessibilityService被用戶(hù)授權(quán)開(kāi)啟后并不是一直保活的,也有可能被系統(tǒng)銷(xiāo)毀。

題外話

輔助服務(wù)其實(shí)還有很多使用場(chǎng)景。比如,從上圖筆者手機(jī)應(yīng)用列表里看到的云服務(wù)360手機(jī)助手(下載自動(dòng)安裝)、京東金融(手環(huán)社交軟件提醒)。另外,還有我們經(jīng)常聽(tīng)說(shuō)的微信自動(dòng)搶紅包微信自動(dòng)回復(fù)...各位如果有興趣可以研究下。Good Luck~

最后的福利(2018年4月16號(hào)添加):
寫(xiě)完這篇博客后,筆者又研究了下微信搶紅包原理,寫(xiě)了一個(gè)【微信自動(dòng)搶紅包】插件,原理也是使用輔助服務(wù),開(kāi)源項(xiàng)目地址:
https://github.com/sinawangnan7/WXGiftMoney
歡迎Star...(??????)??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,732評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,214評(píng)論 3 426
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 177,781評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,588評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,315評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,699評(píng)論 1 327
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,698評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,882評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,441評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,189評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,388評(píng)論 1 372
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,933評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,613評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,023評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,310評(píng)論 1 293
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,112評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,334評(píng)論 2 377

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