前言
在寫 Android 應(yīng)用時(shí),當(dāng)你新建一個(gè) Activity,Service,ContentProvider,Broadcast(著名的四大組件)時(shí),你是不是經(jīng)常性的寫完就直接運(yùn)行,然后程序就崩潰了,通過查看日志,你才發(fā)覺原來忘記在 AndroidManifest.xml 中進(jìn)行注冊(cè)。甚至于,當(dāng)我們代碼運(yùn)行需要某些權(quán)限時(shí),你也要跳轉(zhuǎn)到 AndroidManifest.xml 中進(jìn)行權(quán)限聲明,然后代碼才能正確運(yùn)行,而這些操作,我們往往都會(huì)忘記。
筆者個(gè)人認(rèn)為,導(dǎo)致我們經(jīng)常性忘記在 AndroidManifest.xml 中進(jìn)行注冊(cè)的一個(gè)主要的原因就在于編寫代碼和進(jìn)行注冊(cè)是發(fā)生在兩個(gè)文件中的,也就是我們需要進(jìn)行文件切換,這種切換操作對(duì)于我們正在編寫程序的思路起到了切斷作用,所以我們往往在專注于寫代碼的時(shí)候,就會(huì)忘記進(jìn)行組件注冊(cè)。
基于以上原因,筆者開發(fā)了一套開源框架:InjectManifest,這套框架致力于解決上面我們提到的編寫代碼和進(jìn)行組件注冊(cè)需要進(jìn)行文件切換的不便,框架提供注解進(jìn)行注冊(cè),讓我們?cè)诰帉懴嚓P(guān)需要進(jìn)行注冊(cè)的代碼的同時(shí),可以很方便地直接使用注解進(jìn)行相關(guān)內(nèi)容的注冊(cè),再也無需切換到 AndroidManifest.xml 去做這些事。
優(yōu)點(diǎn)
- 采用編譯期注解與自定義
Gradle插件完成注冊(cè)過程,對(duì)程序運(yùn)行無任何影響; - 支持注解和原生
AndroidManifest.xml協(xié)同工作,最終會(huì)將兩者結(jié)合起來,保留不一致的元素,相同的元素只保留一份; - 對(duì)支持的標(biāo)簽的所有屬性配置均支持;
缺點(diǎn)
- 在每次使用注解注冊(cè)后,需要
rebuild一下才能生成新的AndroidManifest.xml文件,如果采用注解注冊(cè)后,直接運(yùn)行程序,可以看到新的AndroidManifest.xml也生成了,但是程序此時(shí)使用的是舊的(也就是原生的)AndroidManifest.xml配置。這個(gè)地方的原因我猜測應(yīng)該是processDebugManifest/processReleaseManifest運(yùn)行在 新的AndroidManifest.xml生成前,所以這個(gè)問題我猜測是不是可以有什么辦法把processDebugManifest/processReleaseManifest放到文件生成后再執(zhí)行····這個(gè)地方我暫時(shí)也沒找出什么辦法進(jìn)行解決,如果有誰知道怎么解決這個(gè)問題的,麻煩跟我講下,謝謝。 - 目前只支持
manifest,application,activity,service,receiver,provider,uses-permission標(biāo)簽的解析,對(duì)于其他標(biāo)簽,無法進(jìn)行融合,在新生成的AndroidManifest.xml中這些元素不會(huì)被保留;
示例
-
manifest標(biāo)簽注冊(cè):
@InjectManifest(
pkName = "com.yn.injectmanifest",
installLocation = INTERNAL_ONLY,
sharedUserId = "android.uid.system"
)
public class App extends Application {
}
rebuild 一下,你就可以看到 AndroidManifest.xml 變成這樣:

manifest 標(biāo)簽的其他屬性 @InjectManifest 均支持。
-
application標(biāo)簽注冊(cè):
@InjectApp(
name = ".App", //you can full class name or just simply using a .classSimpleName
label = "i am app",
debuggable = TRUE,
metaData = @InjectMetaData(name = "app/meta-data")
)
public class App extends Application {
}
rebuild 一下,你就可以看到 AndroidManifest.xml 變成這樣:

application 標(biāo)簽的其他屬性 @InjectApp 均支持。
-
activity標(biāo)簽注冊(cè):
@InjectActivity(
name = ".MainActivity",
intentFilter = @InjectIntentFilter(
action = {"android.intent.action.MAIN", "android.intent.action_whyn_test"},
category = {"android.intent.category.LAUNCHER", "android.intent.category.whyn"},
data = @InjectData(mimeType = "image/*")
))
public class MainActivity extends AppCompatActivity {}
rebuild 一下,你就可以看到 AndroidManifest.xml 變成這樣:

activity 標(biāo)簽的其他屬性 @InjectActivity 均支持。
-
service標(biāo)簽注冊(cè):
@InjectService(
enabled = TRUE,
name = ".FirstService",
label = "Inject Service test",
intentFilter = @InjectIntentFilter(
action = "com.yn.action.FirstService",
category = "com.yn.category.serviceTest",
data = @InjectData(
host = "sdcard",
mimeType = "video/mp4",
path = "/sdcard/1.MP4",
pathPattern = ".*\\.mp4",
pathPrefix = "/sdcard/",
port = "-2",
scheme = "file"
)
),
metaData = @InjectMetaData(name = "com.yn.meta-data.service")
)
public class FirstService extends Service {···}
rebuild 一下,你就可以看到 AndroidManifest.xml 變成這樣:

service 標(biāo)簽的其他屬性 @InjectService 均支持。
-
receiver標(biāo)簽注冊(cè):
@InjectReceiver(
name = ".FirstReceiver",
label = "hi i am first receiver",
process = ".remote",
enabled = TRUE
)
public class FirstReceiver extends BroadcastReceiver {···}
rebuild 一下,你就可以看到 AndroidManifest.xml 變成這樣:

receiver 標(biāo)簽的其他屬性 @InjectReceiver 均支持。
-
provider標(biāo)簽注冊(cè):
@InjectProvider(
authorities = "com.yn.authorities",
name = ".FirstProvider",
label = "I am ContentProvider"
)
public class FirstProvider extends android.content.ContentProvider {···}

provider 標(biāo)簽的其他屬性 @InjectProvider 均支持。
-
uses-permission標(biāo)簽注冊(cè):
@InjectUsesPermission({
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.BLUETOOTH,
Manifest.permission.ACCESS_WIFI_STATE,
})
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
rebuild 一下,你就可以看到 AndroidManifest.xml 變成這樣:

uses-permission 標(biāo)簽的其他屬性 @InjectUsesPermission 均支持。
目前暫時(shí)就只支持以上所講的標(biāo)簽,后續(xù)我有時(shí)間就會(huì)不定時(shí)更新下。
下載
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.whyn:injectmanifest-plugin:1.1.0'
}
}
然后,apply 到你的 module:
apply plugin: 'com.android.application'
apply plugin: 'com.whyn.plugin.injectmanifest'
注意事項(xiàng):
-
InjectManifest 默認(rèn)會(huì)將生成的
AndroidManifest.xml替換掉原來的AndroidManifest.xml,但在替換前,會(huì)將原來的AndroidManifest.xml保存為AndroidManifest_old.xml,所以,對(duì)于暫時(shí)未支持的xml標(biāo)簽,新生成的文件無法保留,那么你就可以從AndroidManifest_old.xml中找回。
如果想更換上面的默認(rèn)行為,那就需要在module的build.gradle中增加下面的擴(kuò)展屬性:
manifestConfig {
//the defautl AndroidManifest.xml path
originManifestPath android.sourceSets.main.manifest.srcFile.absolutePath
//the AndroidManifest.xml path generated by annotation processor
genManifestPath "$project.buildDir/generated/source/apt/debug/Collections.xml"
//to save the original AndroidManifest: true -- save,false -- not save
saveOrigin false
}
- 如果你在開發(fā)過程中,要為注解處理器傳遞參數(shù),請(qǐng)記住加上
+號(hào),代表追加,否則,會(huì)導(dǎo)致gradle插件里面默認(rèn)設(shè)置的注解參數(shù)失效,這樣就不會(huì)合并原生AndroidManifest.xml了。
android {
defaultConfig{
···
···
javaCompileOptions {
annotationProcessorOptions {
arguments += [xxxxx: 'yyyyy']
}
}
}
}
- 如果你對(duì)
AndroidManifest.xml的默認(rèn)路徑進(jìn)行了修改,如果你還希望能合并AndroidManifest.xml,那你需要手動(dòng)傳遞最新路徑給annotation processor:
android {
defaultConfig{
···
···
javaCompileOptions {
annotationProcessorOptions {
arguments = [AndroidManifestPath: android.sourceSets.main.manifest.srcFile.absolutePath]
}
}
}
}
附錄
源碼傳送門:InjectManifest
AndroidManifest.xml 應(yīng)用清單官方文檔: here