前言
在写 Android 应用时,当你新建一个 Activity
,Service
,ContentProvider
,Broadcast
(著名的四大组件)时,你是不是经常性的写完就直接运行,然后程序就崩溃了,通过查看日志,你才发觉原来忘记在 AndroidManifest.xml
中进行注册。甚至于,当我们代码运行需要某些权限时,你也要跳转到 AndroidManifest.xml
中进行权限声明,然后代码才能正确运行,而这些操作,我们往往都会忘记。
笔者个人认为,导致我们经常性忘记在 AndroidManifest.xml
中进行注册的一个主要的原因就在于编写代码和进行注册是发生在两个文件中的,也就是我们需要进行文件切换,这种切换操作对于我们正在编写程序的思路起到了切断作用,所以我们往往在专注于写代码的时候,就会忘记进行组件注册。
基于以上原因,笔者开发了一套开源框架:InjectManifest,这套框架致力于解决上面我们提到的编写代码和进行组件注册需要进行文件切换的不便,框架提供注解进行注册,让我们在编写相关需要进行注册的代码的同时,可以很方便地直接使用注解进行相关内容的注册,再也无需切换到 AndroidManifest.xml
去做这些事。
优点
- 采用编译期注解与自定义
Gradle
插件完成注册过程,对程序运行无任何影响; - 支持注解和原生
AndroidManifest.xml
协同工作,最终会将两者结合起来,保留不一致的元素,相同的元素只保留一份; - 对支持的标签的所有属性配置均支持;
缺点
- 在每次使用注解注册后,需要
rebuild
一下才能生成新的AndroidManifest.xml
文件,如果采用注解注册后,直接运行程序,可以看到新的AndroidManifest.xml
也生成了,但是程序此时使用的是旧的(也就是原生的)AndroidManifest.xml
配置。这个地方的原因我猜测应该是processDebugManifest/processReleaseManifest
运行在 新的AndroidManifest.xml
生成前,所以这个问题我猜测是不是可以有什么办法把processDebugManifest/processReleaseManifest
放到文件生成后再执行····这个地方我暂时也没找出什么办法进行解决,如果有谁知道怎么解决这个问题的,麻烦跟我讲下,谢谢。 - 目前只支持
manifest
,application
,activity
,service
,receiver
,provider
,uses-permission
标签的解析,对于其他标签,无法进行融合,在新生成的AndroidManifest.xml
中这些元素不会被保留;
示例
-
manifest
标签注册:
@InjectManifest(
pkName = "com.yn.injectmanifest",
installLocation = INTERNAL_ONLY,
sharedUserId = "android.uid.system"
)
public class App extends Application {
}
rebuild
一下,你就可以看到 AndroidManifest.xml
变成这样:
manifest
标签的其他属性 @InjectManifest
均支持。
-
application
标签注册:
@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
标签的其他属性 @InjectApp
均支持。
-
activity
标签注册:
@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
标签的其他属性 @InjectActivity
均支持。
-
service
标签注册:
@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
标签的其他属性 @InjectService
均支持。
-
receiver
标签注册:
@InjectReceiver(
name = ".FirstReceiver",
label = "hi i am first receiver",
process = ".remote",
enabled = TRUE
)
public class FirstReceiver extends BroadcastReceiver {···}
rebuild
一下,你就可以看到 AndroidManifest.xml
变成这样:
receiver
标签的其他属性 @InjectReceiver
均支持。
-
provider
标签注册:
@InjectProvider(
authorities = "com.yn.authorities",
name = ".FirstProvider",
label = "I am ContentProvider"
)
public class FirstProvider extends android.content.ContentProvider {···}
provider
标签的其他属性 @InjectProvider
均支持。
-
uses-permission
标签注册:
@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
标签的其他属性 @InjectUsesPermission
均支持。
目前暂时就只支持以上所讲的标签,后续我有时间就会不定时更新下。
下载
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'
注意事项:
-
InjectManifest 默认会将生成的
AndroidManifest.xml
替换掉原来的AndroidManifest.xml
,但在替换前,会将原来的AndroidManifest.xml
保存为AndroidManifest_old.xml
,所以,对于暂时未支持的xml
标签,新生成的文件无法保留,那么你就可以从AndroidManifest_old.xml
中找回。
如果想更换上面的默认行为,那就需要在module
的build.gradle
中增加下面的扩展属性:
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
}
- 如果你在开发过程中,要为注解处理器传递参数,请记住加上
+
号,代表追加,否则,会导致gradle
插件里面默认设置的注解参数失效,这样就不会合并原生AndroidManifest.xml
了。
android {
defaultConfig{
···
···
javaCompileOptions {
annotationProcessorOptions {
arguments += [xxxxx: 'yyyyy']
}
}
}
}
- 如果你对
AndroidManifest.xml
的默认路径进行了修改,如果你还希望能合并AndroidManifest.xml
,那你需要手动传递最新路径给annotation processor
:
android {
defaultConfig{
···
···
javaCompileOptions {
annotationProcessorOptions {
arguments = [AndroidManifestPath: android.sourceSets.main.manifest.srcFile.absolutePath]
}
}
}
}
附录
源码传送门:InjectManifest
AndroidManifest.xml
应用清单官方文档: here