當(dāng)Activity跳轉(zhuǎn)偶遇單身多年的老漢

本文章已授權(quán)鴻洋微信公眾號(hào)轉(zhuǎn)載:當(dāng)Activity跳轉(zhuǎn)偶遇單身多年的老漢

問題介紹

在項(xiàng)目中搂根,Activity多重跳轉(zhuǎn)一直是開發(fā)中最常見的問題渣淤,網(wǎng)上的解決方案很多,但是要怎么解決才是最佳的往往才是頭疼的問題古劲,我現(xiàn)在要講的是如何真正的解決這個(gè)問題而不留一絲Bug竹揍,先介紹幾種已有的方案以及優(yōu)缺點(diǎn)

AOP 面向切面

這里不講 AOP 的集成恼五,如需了解請(qǐng)左拐百度镰矿,這里只講優(yōu)勢(shì)和劣勢(shì)

textView.setOnClickListener(new OnClickListener() {

    @EnableFastOnClick
    @Override
    public void onClick(View v) {
        
    }
});
  • 優(yōu)點(diǎn):對(duì) View 點(diǎn)擊事件的方法進(jìn)行注解,看起來比較簡(jiǎn)潔

  • 缺點(diǎn):每一處 View 點(diǎn)擊事件都要進(jìn)行注解罢屈,開發(fā)成本較高嘀韧,容易出現(xiàn)遺漏

Activity 啟動(dòng)模式

<activity
    android:name=".ui.activity.XXXActivity"
    android:launchMode="singleTop" />

為 Activity 文件中設(shè)置 singleTop,這里復(fù)習(xí)一下 singleTop 啟動(dòng)模式

singleTop:?jiǎn)我豁敳磕J讲疲绻蝿?wù)棧的棧頂存在這個(gè)要開啟的 Activity锄贷,不會(huì)重新的創(chuàng)建 Activity,而是復(fù)用已經(jīng)存在的 Activity曼月。保證棧頂如果存在谊却,不會(huì)重復(fù)創(chuàng)建

  • 優(yōu)點(diǎn):直接在清單文件中設(shè)置 Activity 的啟動(dòng)模式,簡(jiǎn)單粗暴

  • 缺點(diǎn):每新增 Activity 都要設(shè)置啟動(dòng)模式哑芹,并且只能指定singleTop炎辨,開發(fā)成本較高,容易出現(xiàn)遺漏

startActivity 攔截

首先聪姿,我們需要先造一個(gè)雙擊判斷工具類

public final class DoubleClickHelper {

    private static final long[] TIME_ARRAY = new long[2]; // 數(shù)組的長(zhǎng)度為2代表只記錄雙擊操作

    /**
     * 是否在短時(shí)間內(nèi)進(jìn)行了雙擊操作
     */
    public static boolean isOnDoubleClick() {
        // 默認(rèn)間隔時(shí)長(zhǎng)
        return isOnDoubleClick(1500);
    }

    /**
     * 是否在短時(shí)間內(nèi)進(jìn)行了雙擊操作
     */
    public static boolean isOnDoubleClick(int time) {
        System.arraycopy(TIME_ARRAY, 1, TIME_ARRAY, 0, TIME_ARRAY.length - 1);
        TIME_ARRAY[TIME_ARRAY.length - 1] = SystemClock.uptimeMillis();
        return TIME_ARRAY[0] >= (SystemClock.uptimeMillis() - time);
    }
}

重寫 Activity 的 startActivity 方法

public abstract class BaseActivity extends AppCompatActivity {

    @Override
    public void startActivity(Intent intent) {
        if (DoubleClickHelper.isOnDoubleClick(500)) {
            return;
        }
        super.startActivity(intent);
    }
}

這樣寫其實(shí)存在一個(gè)漏洞碴萧,讓我們看 Activity 的跳轉(zhuǎn)方法

我想大家的第一眼感覺是和我一樣的乙嘀,這是神馬?我難道要重寫那么多個(gè)破喻?

遇到這種問題乒躺,一般菜鳥抱大腿的流程:

  • 菜鳥:遇到不會(huì)的問題怎么辦?

  • 老鳥:不會(huì)百度暗退酢!百度不會(huì)嗎曹货?

  • 菜鳥:百度不行怎么辦咆繁?

  • 老鳥:百度不行就換谷歌啊顶籽!

  • 菜鳥:谷歌也不行怎么辦玩般?

  • 老鳥:源碼是最好的老師!

這里只是講個(gè)段子礼饱,接下來讓我們通過查看源碼來解決這個(gè)問題坏为,先看 startActivity 的源碼

這里調(diào)用了同名不同參的方法,再看

原來 startActivity 最終還是要回調(diào) startActivityForResult


從這里看到 startActivityForResult 兩個(gè)方法镊绪,參數(shù)短的方法還是調(diào)用了參數(shù)長(zhǎng)的方法匀伏,這里我們只需要重寫那個(gè)參數(shù)長(zhǎng)的方法即可,那我們不能用剛剛那種方式了蝴韭,把 startActivity 換成 startActivityForResult

public abstract class BaseActivity extends AppCompatActivity {

    @Override
    public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
        if (DoubleClickHelper.isOnDoubleClick(500)) {
            return;
        }
        super.startActivityForResult(intent, requestCode, options);
    }
}

其實(shí)這樣還存在一個(gè)問題够颠,如果這個(gè)界面需要多重跳轉(zhuǎn)怎么辦呢?這樣直接寫死 BaseActivity 是不是不利于擴(kuò)展榄鉴?

這個(gè)問題解決也很簡(jiǎn)單履磨,在 BaseActivity 預(yù)留一個(gè)方法,子類可以重寫這個(gè)方法來決定是否要檢查和判斷 Activity 多重跳轉(zhuǎn)的問題

public abstract class BaseActivity extends AppCompatActivity {

    @Override
    public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
        if (isCheckActivityJump() && DoubleClickHelper.isOnDoubleClick(500)) {
            return;
        }

        // 查看源碼得知 startActivity 最終也會(huì)調(diào)用 startActivityForResult
        super.startActivityForResult(intent, requestCode, options);
    }

    /**
     * 是否檢查 Activity 跳轉(zhuǎn)頻率庆尘,避免重復(fù)跳轉(zhuǎn)
     */
    protected boolean isCheckActivityJump() {
        // 默認(rèn)需要檢查和判斷
        return true;
    }
}

其實(shí)就這兩句代碼剃诅,非常簡(jiǎn)單,接下來總結(jié)一下

  • 優(yōu)點(diǎn):基類處理驶忌,一勞永逸矛辕,開發(fā)成本極低

  • 缺點(diǎn):不能精準(zhǔn)的判斷跳轉(zhuǎn)的 Activity 是否是重復(fù)的,也就是說如果同時(shí)跳轉(zhuǎn)兩個(gè)不同的 Activity位岔,結(jié)果只有第一個(gè)成功跳轉(zhuǎn)如筛,而第二個(gè)卻沒有跳轉(zhuǎn)

startActivityForResult 攔截優(yōu)化

上一個(gè)解決方案還殘留著Bug,追求完美的我們?cè)跄苋菰S這種事情的發(fā)生抒抬,接下來讓我們來給這個(gè)問題畫上圓滿的句號(hào)

首先要想知道重復(fù)跳轉(zhuǎn)的 Activity 是不是同一個(gè)杨刨,我們可以通過 Intent 這個(gè)對(duì)象來進(jìn)行判斷,不過在此之前我們要先復(fù)習(xí)一下 Activity 的啟動(dòng)方式

  • 顯式意圖啟動(dòng)

    • 構(gòu)造方法:new Intent(Context packageContext, Class<?> cls)

    • 對(duì)象方法:intent.setClass(Context packageContext, Class<?> cls)

  • 隱式意圖啟動(dòng)

    • 構(gòu)造方法:new Intent(String action)

    • 對(duì)象方法:intent.setAction(String action)

這里已經(jīng)列出這兩種啟動(dòng)方式的使用了擦剑,我們可以利用顯式意圖和隱式意圖來分別創(chuàng)建一個(gè) Tag 標(biāo)記妖胀,用于判斷跳轉(zhuǎn)的 Activity 是否是重復(fù)的

// 標(biāo)記對(duì)象
String tag;
if (intent.getComponent() != null) { // 顯式跳轉(zhuǎn)
    tag = intent.getComponent().getClassName();
}else if (intent.getAction() != null) { // 隱式跳轉(zhuǎn)
    tag = intent.getAction();
}

除了判斷是否重復(fù)了之外芥颈,還需要再判斷跳轉(zhuǎn)時(shí)間間隔

if (tag.equals(mActivityJumpTag) && mActivityJumpTime >= SystemClock.uptimeMillis() - 500) {
    // 檢查不通過
    result = false;
}

完整代碼如下

public abstract class BaseActivity extends AppCompatActivity {

    @Override
    public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
        if (startActivitySelfCheck(intent)) {
            // 查看源碼得知 startActivity 最終也會(huì)調(diào)用 startActivityForResult
            super.startActivityForResult(intent, requestCode, options);
        }
    }

    private String mActivityJumpTag;
    private long mActivityJumpTime;

    /**
     * 檢查當(dāng)前 Activity 是否重復(fù)跳轉(zhuǎn)了,不需要檢查則重寫此方法并返回 true 即可
     *
     * @param intent          用于跳轉(zhuǎn)的 Intent 對(duì)象
     * @return                檢查通過返回true, 檢查不通過返回false
     */
    protected boolean startActivitySelfCheck(Intent intent) {
        // 默認(rèn)檢查通過
        boolean result = true;
        // 標(biāo)記對(duì)象
        String tag;
        if (intent.getComponent() != null) { // 顯式跳轉(zhuǎn)
            tag = intent.getComponent().getClassName();
        }else if (intent.getAction() != null) { // 隱式跳轉(zhuǎn)
            tag = intent.getAction();
        }else {
            return result;
        }

        if (tag.equals(mActivityJumpTag) && mActivityJumpTime >= SystemClock.uptimeMillis() - 500) {
            // 檢查不通過
            result = false;
        }

        // 記錄啟動(dòng)標(biāo)記和時(shí)間
        mActivityJumpTag = tag;
        mActivityJumpTime = SystemClock.uptimeMillis();
        return result;
    }
}

此解決方案已經(jīng)加入啃得香豪華套餐:AndroidProject赚抡,歡迎各位提 issue爬坑,歡迎 star

Android 技術(shù)討論 Q 群:10047167

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市涂臣,隨后出現(xiàn)的幾起案子盾计,更是在濱河造成了極大的恐慌变勇,老刑警劉巖摘悴,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件圆到,死亡現(xiàn)場(chǎng)離奇詭異勿侯,居然都是意外死亡纲仍,警方通過查閱死者的電腦和手機(jī)阱飘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門驹吮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來依疼,“玉大人剖煌,你說我怎么就攤上這事材鹦。” “怎么了耕姊?”我有些...
    開封第一講書人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵桶唐,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我箩做,道長(zhǎng)莽红,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任邦邦,我火速辦了婚禮安吁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘燃辖。我一直安慰自己鬼店,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開白布黔龟。 她就那樣靜靜地躺著妇智,像睡著了一般。 火紅的嫁衣襯著肌膚如雪氏身。 梳的紋絲不亂的頭發(fā)上巍棱,一...
    開封第一講書人閱讀 51,274評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音蛋欣,去河邊找鬼航徙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛陷虎,可吹牛的內(nèi)容都是我干的到踏。 我是一名探鬼主播杠袱,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼窝稿!你這毒婦竟也來了楣富?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤伴榔,失蹤者是張志新(化名)和其女友劉穎纹蝴,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體踪少,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡骗灶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秉馏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡脱羡,死狀恐怖萝究,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情锉罐,我是刑警寧澤帆竹,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站脓规,受9級(jí)特大地震影響栽连,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜侨舆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一秒紧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧挨下,春花似錦熔恢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至愁铺,卻和暖如春鹰霍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背茵乱。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來泰國打工茂洒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人似将。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓获黔,卻偏偏與公主長(zhǎng)得像蚀苛,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子玷氏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

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