Android activity exported屬性理解

背景

這么久了升筏,我自己看來(lái)對(duì)此屬性的理解有點(diǎn)小偏差,當(dāng)然不是表面上的理解誤差煤率,而是涉及到具體實(shí)現(xiàn)的細(xì)節(jié)。這里先貼下官方關(guān)于此屬性的解釋:

android:exported
This element sets whether the activity can be launched by components of other applications — "true" if it can be, and "false" if not. If "false", the activity can be launched only by components of the same application or applications with the same user ID.
If you are using intent filters, you should not set this element "false". If you do so, and an app tries to call the activity, system throws an ActivityNotFoundException. Instead, you should prevent other apps from calling the activity by not setting intent filters for it.

If you do not have intent filters, the default value for this element is "false". If you set the element "true", the activity is accessible to any app that knows its exact class name, but does not resolve when the system tries to match an implicit intent.

This attribute is not the only way to limit an activity's exposure to other applications. You can also use a permission to limit the external entities that can invoke the activity (see the permission attribute).

這段文字說(shuō)明乏冀,值得多讀幾遍5础!辆沦!

由于我們團(tuán)隊(duì)的關(guān)系昼捍,我們開(kāi)發(fā)的模塊經(jīng)常需要集成到多個(gè)app中,而我們不想為某個(gè)app單獨(dú)維護(hù)一份代碼肢扯,即我們的開(kāi)發(fā)中妒茬,所有的宿主app用的都是同一套代碼。比如就存在類(lèi)似這樣的代碼:

<activity
            android:name=".SubActivity"
            android:configChanges="orientation|keyboardHidden"
            android:exported="false" // 注意這行代碼N党俊Uё辍!
            android:screenOrientation="portrait"
            android:windowSoftInputMode="stateHidden">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="mlpf" />
                <data android:host="sub" />
            </intent-filter>
        </activity>

這樣在宿主app里铭腕,通過(guò)打開(kāi)mlpf://sub這樣的短鏈就能輕松地來(lái)到我們模塊的SubActivity银择。注意這里android:exported=false的設(shè)置,因?yàn)槿绻辉O(shè)置的話累舷,根據(jù)Android的規(guī)則只要有intent-filter存在浩考,那么exported就是true,即對(duì)外暴露的被盈;而這里我們顯然不希望是對(duì)外暴露的析孽,因?yàn)槿绻@樣的話搭伤,當(dāng)安裝了多個(gè)集成了我們模塊的App時(shí),當(dāng)要打開(kāi)這樣的短鏈請(qǐng)求時(shí)系統(tǒng)就會(huì)彈出選擇框讓用戶選擇在哪個(gè)app里打開(kāi)袜瞬,這當(dāng)然不是我們期望的怜俐。

當(dāng)同一個(gè)設(shè)備上裝了我們的多個(gè)app的時(shí)候,在8.0之前都是ok的吞滞,即A app里的SubActivity和B app里的SubActivity互相是沒(méi)任何關(guān)系的佑菩,也是互相看不到對(duì)方的,這是我們對(duì)exported=false的認(rèn)識(shí)裁赠;直到上周某天晚上快要下班了殿漠,QA同學(xué)拿著升級(jí)到8.0的Nexus 6P跟我說(shuō),你看你們這個(gè)頁(yè)面跳不過(guò)去了佩捞,還彈出了個(gè)討厭的沒(méi)有應(yīng)用可執(zhí)行此操作的提示绞幌,我當(dāng)時(shí)也是一臉懵逼啊,但心里已經(jīng)有種不祥的預(yù)感一忱,看起來(lái)像是google改出來(lái)的bug莲蜘。

我接過(guò)設(shè)備,點(diǎn)擊了幾下帘营,確保能復(fù)現(xiàn)票渠,然后連著電腦,看了下adb logcat關(guān)于ActivityManager相關(guān)的輸出芬迄,果然我們這個(gè)intent沒(méi)有找到對(duì)應(yīng)的cmp(component)问顷,而是到了系統(tǒng)的ResolverActivity,ResolverAct大家都知道禀梳,當(dāng)系統(tǒng)找到了多個(gè)目標(biāo)或者沒(méi)目標(biāo)時(shí)會(huì)彈出它提醒用戶杜窄。這就有點(diǎn)奇怪了,同樣的case在7.x的設(shè)備上就是好的算途,雖然行為上也是到了ResolverAct塞耕,但ResolverAct內(nèi)部最終還是導(dǎo)到了本app內(nèi)部的SubActivity,最終正確調(diào)起了嘴瓤。

解惑

有一點(diǎn)我們需要知道扫外,即當(dāng)我們通過(guò)Intent打開(kāi)act的時(shí)候,系統(tǒng)內(nèi)部會(huì)調(diào)用
Intent.resolveActivity(pm)廓脆,其內(nèi)部又會(huì)接著調(diào)用PackageManager#resolveActivity畏浆。另外你也可以調(diào)用PackageManager#queryIntentActivities來(lái)查看某個(gè)intent究竟可以被哪個(gè)act處理。有一點(diǎn)需要特別注意的是狞贱,這些方法會(huì)考察設(shè)備上所有安裝的app里的activity刻获,即使是那些被顯式標(biāo)記成了exported=false的act,這就是我上文說(shuō)到的理解偏差,這讓我很驚訝蝎毡。因?yàn)槲乙郧暗恼J(rèn)識(shí)中厚柳,既然標(biāo)記了不對(duì)外暴露,那么這些act也不應(yīng)該被找到才對(duì)沐兵,但很可惜别垮,看起來(lái)Android的實(shí)現(xiàn)不是這樣的,關(guān)于這點(diǎn)扎谎,可以參考以下問(wèn)題:Android queryintentactivities.

7.x(包括)之前雖然也能查到別的app里exported=false的act(這個(gè)行為看起來(lái)一直都有)碳想,但最終會(huì)正確打開(kāi)匹配到的本app里exported=false的act,但在8.0上這個(gè)行為break掉了毁靶,直接變成了上文提到的“沒(méi)有應(yīng)用可執(zhí)行此操作”胧奔,真是一個(gè)憂傷的故事。

8.0解決辦法

關(guān)于8.0的這個(gè)問(wèn)題预吆,AOSP上也有人報(bào)了bug:intent有多個(gè)match時(shí)無(wú)法正確跳轉(zhuǎn)龙填。不過(guò)看起來(lái)僅僅是個(gè)沒(méi)多少關(guān)注的P3bug,而且我手頭的5x升級(jí)到了8.1.0拐叉,此問(wèn)題依然存在岩遗,看來(lái)指望google修復(fù)希望不大。還好我們也有辦法處理下凤瘦,看下Intent.setPackage方法宿礁,如下:

/**
     * (Usually optional) Set an explicit application package name that limits
     * the components this Intent will resolve to.  If left to the default
     * value of null, all components in all applications will considered.
     * If non-null, the Intent can only match the components in the given
     * application package.
     *
     * @param packageName The name of the application package to handle the
     * intent, or null to allow any application package.
     *
     * @return Returns the same Intent object, for chaining multiple calls
     * into a single statement.
     *
     * @see #getPackage
     * @see #resolveActivity
     */
    public Intent setPackage(String packageName) {
        if (packageName != null && mSelector != null) {
            throw new IllegalArgumentException(
                    "Can't set package name when selector is already set");
        }
        mPackage = packageName;
        return this;
    }

我們面臨的主要問(wèn)題就是系統(tǒng)API在startActivity的過(guò)程中查到了別的app里面的非暴露act,這個(gè)方法看起來(lái)剛好可以將系統(tǒng)的這個(gè)查找行為局限在本app內(nèi)蔬芥,所以我們的fix如下:

    if (Build.VERSION.SDK_INT >= 26) {
        intent.setPackage(mContext.getPackageName());
    }

最后梆靖,關(guān)于exported=false的實(shí)現(xiàn),我個(gè)人的看法是應(yīng)該再提早些,直接一開(kāi)始在匹配的過(guò)程中就找不到這樣的act,而不是一股腦全找到(導(dǎo)致本來(lái)就1個(gè)target滿足生蚁,結(jié)果找了多個(gè)出來(lái))档悠,等到最后要打開(kāi)了,看下exported是false壁酬,才彈個(gè)無(wú)權(quán)限的錯(cuò)誤4巫谩!舆乔!之前魅族更新了次系統(tǒng)后也出過(guò)這問(wèn)題岳服,彈出讓用戶選,結(jié)果選了之后又告訴用戶無(wú)權(quán)限(因?yàn)閷?shí)際是exported=false的activity)希俩。就像在實(shí)現(xiàn)某個(gè)方法的時(shí)候吊宋,有些前置條件不滿足,我們應(yīng)該盡早return颜武,而不是埋頭做了很多工作后璃搜,才檢查一些必要條件拖吼,發(fā)現(xiàn)不對(duì)了才退出,fail fast常常是很好用的策略这吻。

ps:實(shí)在是沒(méi)明白google這里的實(shí)現(xiàn)為啥要找到這些實(shí)際上private的act吊档,看起來(lái)完全是在做無(wú)用功啊,反正怎么著都不可能打開(kāi)唾糯,你把它找出來(lái)干啥呢5∨稹!移怯!有想法的同學(xué)可以留言交流下香璃,謝謝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末芋酌,一起剝皮案震驚了整個(gè)濱河市增显,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌脐帝,老刑警劉巖同云,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異堵腹,居然都是意外死亡炸站,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)疚顷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)旱易,“玉大人,你說(shuō)我怎么就攤上這事腿堤》Щ担” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵笆檀,是天一觀的道長(zhǎng)忌堂。 經(jīng)常有香客問(wèn)我,道長(zhǎng)酗洒,這世上最難降的妖魔是什么士修? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮樱衷,結(jié)果婚禮上棋嘲,老公的妹妹穿的比我還像新娘。我一直安慰自己矩桂,他們只是感情好沸移,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般雹锣。 火紅的嫁衣襯著肌膚如雪流妻。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,198評(píng)論 1 299
  • 那天笆制,我揣著相機(jī)與錄音绅这,去河邊找鬼。 笑死在辆,一個(gè)胖子當(dāng)著我的面吹牛证薇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播匆篓,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼浑度,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了鸦概?” 一聲冷哼從身側(cè)響起箩张,我...
    開(kāi)封第一講書(shū)人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎窗市,沒(méi)想到半個(gè)月后先慷,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡咨察,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年论熙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片摄狱。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡脓诡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出媒役,到底是詐尸還是另有隱情祝谚,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布酣衷,位于F島的核電站交惯,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏鸥诽。R本人自食惡果不足惜商玫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一箕憾、第九天 我趴在偏房一處隱蔽的房頂上張望牡借。 院中可真熱鬧,春花似錦袭异、人聲如沸钠龙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)碴里。三九已至沈矿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咬腋,已是汗流浹背羹膳。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留根竿,地道東北人陵像。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像寇壳,于是被迫代替她去往敵國(guó)和親醒颖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354