最近做二次開發(fā)瞬痘,修改別人代碼的時候發(fā)現(xiàn)清單文件中多了很多奇怪的屬性和標(biāo)簽(自己以前沒見過的),在不明白的情況下直接開發(fā)出現(xiàn)了很多奇怪的問題板熊。所以痛下決心框全,重新復(fù)習(xí)下這些基礎(chǔ)知識,以下以6.0系統(tǒng)中的Settings模塊源碼為例講解。
<manifest/>標(biāo)簽層:
這是整個清單文件的最上層干签,用來做一些最基本的聲明津辩,如(包名,權(quán)限,資源命名空間等)喘沿。老規(guī)矩情萤,通過栗子來講解:
<manifest coreApp="true"
package="com.android.settings"
xmlns:android="http://schemas.android.com/apk/res/android"
android:sharedUserId="android.uid.system"
android:versionCode="20150101"
android:versionName="3.0.5">
<original-package android:name="com.android.settings"/>
<uses-sdk
android:minSdkVersion="21"
android:targetSdkVersion="21"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
1.package="com.android.settings"
整個應(yīng)用的包名。這里有個坑摹恨,當(dāng)我們通過ComponentName
來啟動某個Activity時筋岛,所用的包名一定是這個應(yīng)用的包名,而不是當(dāng)前Activity的包名晒哄。2.xmlns:android="http://schemas.android.com/apk/res/android"
命名空間的聲明睁宰,使得各種Android系統(tǒng)級的屬性能讓我們使用。當(dāng)我們需要使用自定義屬性時寝凌,可以將其修改為res-auto柒傻,編譯時會為我們自動去找到該自定義屬性。3.android:sharedUserId="android.uid.system"
將當(dāng)前應(yīng)用進(jìn)程設(shè)置為系統(tǒng)級進(jìn)程(不推介隨意這么做较木,會產(chǎn)生很多隱患)红符。擁有此屬性后,我們的應(yīng)用就可以無視用戶伐债,無法無天地處理很多事情预侯,比如擅自修改手機(jī)system分區(qū)的內(nèi)容、靜默安裝等峰锁。之前開發(fā)過一個類似切換多套開關(guān)機(jī)動畫和音效的模塊萎馅,添加此屬性后,就可以明目張膽地將我們的數(shù)據(jù)節(jié)點存在system分區(qū)虹蒋,可以讓用戶恢復(fù)出廠設(shè)置都清空不了我們的數(shù)據(jù)糜芳。
但是添加此屬性后,我們需要在當(dāng)前模塊的MakeFile中添加LOCAL_CERTIFICATE := platform
魄衅,然后在安卓源碼環(huán)境下使用原生make命令編譯才能生效(原生編譯雖然比使用ide工具麻煩很多峭竣,但是卻能使用很多ide工具無權(quán)限使用的api)。
如果非要在ide工具中使用則必須通過系統(tǒng)密鑰重簽名生成的apk才行(未親自驗證)晃虫。4.uses-permission
為我們的應(yīng)用添加必須的權(quán)限皆撩。同時我們也可以該層聲明自定義的權(quán)限。
<permission
android:name="com.cold.permission.appfreeze"
android:protectionLevel="signatureOrSystem"/>
--*
<application/>標(biāo)簽層:
應(yīng)用層標(biāo)簽傲茄,用來配置我們的apk的整體屬性毅访,也可以統(tǒng)一指定所有界面的主題沮榜。栗子如下:
<application
android:name=".SettingsApp"
android:allowBackup="false"
android:hardwareAccelerated="true"
android:icon="@mipmap/ic_launcher_settings"
android:label="@string/settings_label"
android:requiredForAllUsers="true"
android:supportsRtl="true"
android:taskAffinity=""
android:theme="@style/Theme.Aui">
1."android:name"盘榨、"android:icon"、"android:label"
顧名思義蟆融,用來指定應(yīng)用的名稱草巡、在桌面的啟動圖標(biāo)、應(yīng)用的標(biāo)簽名2."android:theme"
為當(dāng)前應(yīng)用的每個界面都默認(rèn)設(shè)置一個主題型酥,可以后續(xù)在activity標(biāo)簽層單獨覆蓋此Theme山憨。3."android:allowBackup"
關(guān)閉應(yīng)用程序數(shù)據(jù)的備份和恢復(fù)功能查乒,注意該屬性值默認(rèn)為true,如果你不需要你的應(yīng)用被恢復(fù)導(dǎo)致隱私數(shù)據(jù)暴露(如果值為true郁竟,甚至可以直接通過adb命令獲取該應(yīng)用中的數(shù)據(jù))玛迄,必須手動設(shè)置此屬性。4.android:hardwareAccelerated="true"
開啟硬件加速棚亩,一般應(yīng)用不推介使用蓖议。就算非要使用也最好在某個Activity單獨開啟,避免過大的內(nèi)存開銷讥蟆。5.android:taskAffinity
設(shè)置Activity任務(wù)棧的名稱,可忽略勒虾。
--*
<具體組件/>標(biāo)簽層:
因為</provider>、</service>在實際開發(fā)中接觸得不多瘸彤,這部分主要講解 </activity> 修然、</receiver>標(biāo)簽。
關(guān)于Activity標(biāo)簽的屬性质况,個人最覺得繞和難掌握的就是Intent-filter的匹配規(guī)則了愕宋,每次使用錯了都要去查資料修改,所以這邊總結(jié)得盡可能仔細(xì)结榄。
</activity>
先來一段熱身的代碼掏婶,一個最簡單的Activity聲明:
<activity
android:name="AgedModeActivity"
android:icon="@drawable/ic_aged_mode"
android:label="@string/app_name_label"
android:theme="@android:style/Theme.NoDisplay"
android:exported="true"
android:configChanges="orientation|screenSize|fontScale">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
1.android:configChanges
當(dāng)我們的界面大小,方向潭陪,字體等config參數(shù)改變時雄妥,我們的Activity就會重新執(zhí)行onCreate的生命周期。而當(dāng)我們設(shè)置此屬性后依溯,就可以強制讓Activity不重新啟動老厌,而是只會調(diào)用一次onConfigurationChanged
方法,所以我們可以在這里做一些相關(guān)參數(shù)改變的操作黎炉。2."android.intent.category.LAUNCHER"枝秤、"android.intent.action.MAIN"
這兩個屬性共同將當(dāng)前Activity聲明為了我們應(yīng)用的入口,將應(yīng)用注冊至系統(tǒng)的應(yīng)用列表中慷嗜,缺一不可淀弹。
這里還有一點需要注意,如果希望我們的應(yīng)用有多個入口庆械,每個入口能進(jìn)入到app的不同Activity中時薇溃,光設(shè)置這兩個屬性還不夠,還要為它指定一個進(jìn)程和啟動模式缭乘。
android:process=".otherProcess"
android:launchMode ="singleInstance"
至于Activity的四種啟動模式請各位看官自己復(fù)習(xí)沐序,就不在這兒重述了。
- 3.android:exported="true"
將當(dāng)前組件暴露給外部。屬性決定它是否可以被另一個Application的組件啟動策幼。
熱身結(jié)束邑时,我們就來重點分析<intent-filter>的匹配規(guī)則(顯式調(diào)用只需正確使用包名類名即可,隱式調(diào)用才需要考慮匹配的問題)特姐。
<activity android:name="Settings$WirelessSettingsActivity"
android:taskAffinity="com.android.settings"
android:label="@string/wireless_networks_settings_title"
android:parentActivityName="Settings">
<intent-filter android:priority="1">
<action android:name="android.settings.WIRELESS_SETTINGS" />
<action android:name="android.settings.AIRPLANE_MODE_SETTINGS" />
<action android:name="android.settings.NFC_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.VOICE_LAUNCH" />
<data
android:scheme="content"
android:host="com.android.externalstorage.documents"
android:mimeType="vnd.android.document/root" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.WirelessSettings" />
<meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
android:resource="@id/wireless_settings" />
<!-- Note that this doesn't really show any Wireless settings. -->
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
android:value="true" />
</activity>
當(dāng)我們通過intent去隱式調(diào)用一個Activity時晶丘,需要同時匹配注冊activity中的action、category唐含、data才能正常啟動铣口,而這三個屬性的匹配規(guī)則也略有不同。
1.action
action是最簡單的匹配項觉壶,我們將其理解為一個區(qū)分大小寫的字符串即可脑题,一般用來代表某一種特定的動作,隱式調(diào)用時intent必須setAction铜靶。一個過濾器中可以有多個action屬性叔遂,只要我們的itent和其中任意一項equal則就算匹配成功。2.category
category屬性也是一個字符串争剿,匹配時也必須和過濾器中定義的值相同已艰。當(dāng)我們不為intent主動地addCategory
時,系統(tǒng)為幫我們默認(rèn)添加一個值為"android.intent.category.DEFAULT"的category蚕苇。反過來說哩掺,如果我們需要我們自己寫的Activity能接受隱式intent啟動,我們就必須在它的過濾器中添加"android.intent.category.DEFAULT"(深坑I浴)嚼吞,否則無法成功啟動。3.data
data比較復(fù)雜蹬碧,幸運地是我們幾乎用不到它舱禽。data可以分為mimeType和URI路徑兩部分:
mimeType指定媒體格式類型,音頻恩沽、文件誊稚、圖片都有特定的屬性值。
URI則有android:scheme罗心、android:host里伯、android:port等屬性組成,scheme代表模式(常用的有http,content,file,package)渤闷,Host就是一個主機(jī)地址疾瓮,Port則是端口號。
依照上面給出的代碼肤晓,為intent設(shè)置data時我們可以這樣做:
intent.setDataAndType(Uri.parse("content://com.android.externalstorage.documents"), "vnd.android.document/root");
額外擴(kuò)展一些關(guān)于activity的屬性:
- <meta-data/>標(biāo)簽:
標(biāo)簽<meta-data>是提供組件額外的數(shù)據(jù)用的爷贫,它本身是一個鍵值對,寫在清單文件中之后补憾,可以在代碼中獲取漫萄。栗:
private void getMetaData() {
PDebug.Start("getMetaData");
try {
ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
PackageManager.GET_META_DATA);
if (ai == null || ai.metaData == null) return;
mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
// Check if it has a parent specified and create a Header object
final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE);
String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
if (parentFragmentClass != null) {
mParentHeader = new Header();
mParentHeader.fragment = parentFragmentClass;
if (parentHeaderTitleRes != 0) {
mParentHeader.title = getResources().getString(parentHeaderTitleRes);
}
}
} catch (NameNotFoundException nnfe) {
// No recovery
}
PDebug.End("getMetaData");
}
- android:excludeFromRecents="true"
設(shè)置為true后,當(dāng)用戶按了“最近任務(wù)列表”時候盈匾,該activity不會出現(xiàn)在最近任務(wù)列表中腾务,可達(dá)到隱藏應(yīng)用的目的。很黑科技吧~
</receiver>
關(guān)于receiver削饵,個人覺得容易混淆的就一個permission問題:
<receiver
android:name="com.android.settings.AliAgeModeReceiver"
android:permission="com.android.settings.permission.SWITH_SETTING">
<intent-filter>
<action android:name="com.android.settings.action.SWITH_AGED_MODE"/>
</intent-filter>
</receiver>
起初認(rèn)為這是receiver中處理一些操作需要使用到此權(quán)限岩瘦,后來查閱資料后發(fā)現(xiàn)是通過在</receiver>中添加permission標(biāo)簽,我可以發(fā)送一些敏感的廣播窿撬,只有添加了該permission的receiver才能接收到启昧,而不讓其他的應(yīng)用收到。栗:
Intent intent = new Intent("com.android.settings.action.SWITH_AGED_MODE");
sendBroadcast(intent,"com.android.settings.permission.SWITH_SETTING");
這要就只有我們自己的接收者才能收到該廣播劈伴,但是當(dāng)我們
Intent intent = new Intent("com.android.settings.action.SWITH_AGED_MODE");
sendBroadcast(intent);
則所有有此action的接收者都能收到我們發(fā)出的廣播密末。
總結(jié)來說就是:
- 一些敏感的廣播并不想讓第三方的應(yīng)用收到 ;
- 要限制自己的Receiver接收某廣播來源跛璧,避免被惡意的同樣的ACTION的廣播所干擾严里。
--*
11.01更新:
- 發(fā)現(xiàn)一個可以處理Activity中界面與軟鍵盤顯示的屬性:
android:windowSoftInputMode="stateAlwaysHidden"
共有9個屬性,可以分別為軟鍵盤設(shè)置禁止追城、顯示刹碾、大小調(diào)整等情況。具體怎么使用座柱,同學(xué)們請自行使用搜索引擎~
--*