组件之间AndroidManifest合并问题
由于 Android 项目在 Eclipse 和 AndroidStudio开发时 AndroidManifest.xml 文件的位置是不一样的,我们需要在build.gradle 中指定下 AndroidManifest.xml 的位置,AndroidStudio 才能读取到 AndroidManifest.xml,这样解决办法也就有了,我们可以为组件开发模式下的业务组件再创建一个 AndroidManifest.xml,然后根据isModule指定AndroidManifest.xml的文件路径,让业务组件在集成模式和组件模式下使用不同的AndroidManifest.xml,这样表单冲突的问题就可以规避了。
————————————————
2,如果组件模式下, 则需要重新设置 AndroidManifest.xml 文件,里面配置新的Application路径。比如Login组件单独运行 AndroidManifest 清单文件
if (!isModule.toBoolean()) {
implementation project(':b-main')
implementation project(':b-login')
implementation project(':b-setting')
}
解决的问题
1.Application
需要看下applicatin的初始化
有自己的application。
代理的application的作用
LoginApplicationDelegate
/**
* login组件全局应用配置
* Created by chenran3 on 2017/12/6.
*/
@EFModuleAnnotation(moduleName = "b-login",delegateName = "com.cryallen.applogin.LoginApplicationDelegate")
public class LoginApplicationDelegate implements IApplicationDelegate {
private static final String TAG = "LoginApplicationDelegate";
@Override
public void onCreate() {
EFLog.d(TAG, "*------------------onCreate()---------------->");
}
@Override
public void enterBackground() {
EFLog.d(TAG, "*------------------enterBackground()---------------->");
}
@Override
public void enterForeground() {
EFLog.d(TAG, "*------------------enterForeground()---------------->");
}
@Override
public void receiveRemoteNotification(Map<String, String> message) {
EFLog.d(TAG, "receiveRemoteNotification msg = " + message);
}
@Override
public void onTerminate() {
EFLog.d(TAG, "*------------------onTerminate()---------------->");
}
@Override
public void onConfigurationChanged(Configuration configuration) {
EFLog.d(TAG, "*------------------onConfigurationChanged()---------------->");
}
@Override
public void onLowMemory() {
EFLog.d(TAG, "*------------------onLowMemory()---------------->");
}
@Override
public void onTrimMemory(int var1) {
EFLog.d(TAG, "*------------------onTrimMemory()---------------->");
}
}
2.清单文件多个,解决app和单独module的配置
1)组件模式和集成模式的转换
Android Studio中的Module主要有两种属性,分别为:
1、application属性,可以独立运行的Android程序,也就是我们的APP;
apply plugin: ‘com.android.application’
1
2、library属性,不可以独立运行,一般是Android程序依赖的库文件;
apply plugin: ‘com.android.library’
————————————————
sourceSets {
main {
if (isModule.toBoolean()) {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
//集成开发模式下排除debug文件夹中的所有Java文件
java {
exclude 'debug/**'
}
}
}
}
3.切换app和切换组件的变量????
目录中生成一个文件 gradle.properties,我们将使用这个文件的一个重要属性:在Android项目中的任何一个build.gradle文件中都可以把gradle.properties中的常量读取出来;那么我们在上面提到解决办法就有了实际行动的方法,首先我们在gradle.properties中定义一个常量值 isModule(是否是组件开发模式,true为是,false为否):
# 每次更改“isModule”的值后,需要点击 "Sync Project" 按钮
isModule=false
———————————————
实现原理:
asset目录下的json配置
/**
*
* 加载模块信息
*/
public void loadModule() {
Context context = getApplicationContext();
appDelegateList = new ArrayList();
delegateNameList = new ArrayList();
try {
AssetManager assetManager = context.getResources().getAssets();
String[] fileList = assetManager.list("");
int fileLength = fileList.length;
for(int i = 0; i < fileLength; ++i) {
String fileName = fileList[i];
if(fileName.startsWith(MODULE_PREFIX)) {
//解析json配置文件
ModuleInfo moduleInfo = parse(assetManager.open(fileName));
if(moduleInfo == null){
continue;
}
moduleInfoList.add(moduleInfo);
delegateNameList.add(moduleInfo.getPackageName());
EFLog.d(TAG, "load Module: " + moduleInfo.getModuleName());
}
}
appDelegateList.addAll(ModuleClassUtils.getObjectsWithClassName(context, IApplicationDelegate.class, delegateNameList));
} catch (Exception e) {
e.printStackTrace();
}
}
登入的解决办法:
业务组件自己的 Application 可以在组件开发模式下初始化一些数据,例如在组件开发模式下,A组件没有登录页面也没法登录,因此就无法获取到 Token,这样请求网络就无法成功,因此我们需要在A组件这个 APP 启动后就应该已经登录了,这时候组件自己的 Application 类就有了用武之地,我们在组件的 Application的 onCreate 方法中模拟一个登陆接口,在登陆成功后将数据保存到本地,这样就可以处理A组件中的数据业务了;另外我们也可以在组件Application中初始化一些第三方库。
————————————————
4)library依赖问题
在介绍这一节的时候,先说一个问题,在组件化工程模型图中,多媒体组件和Common组件都依赖了日志组件,而A业务组件有同时依赖了多媒体组件和Common组件,这时候就会有人问,你这样搞岂不是日志组件要被重复依赖了,而且Common组件也被每一个业务组件依赖了,这样不出问题吗?
其实大家完全没有必要担心这个问题,如果真有重复依赖的问题,在你编译打包的时候就会报错,如果你还是不相信的话可以反编译下最后打包出来的APP,看看里面的代码你就知道了。组件只是我们在代码开发阶段中为了方便叫的一个术语,在组件被打包进APP的时候是没有这个概念的,这些组件最后都会被打包成arr包,然后被app壳工程所依赖,在构建APP的过程中Gradle会自动将重复的arr包排除,APP中也就不会存在相同的代码了;
但是虽然组件是不会重复了,但是我们还是要考虑另一个情况,我们在build.gradle中compile的第三方库,例如AndroidSupport库经常会被一些开源的控件所依赖,而我们自己一定也会compile AndroidSupport库 ,这就会造成第三方包和我们自己的包存在重复加载,解决办法就是找出那个多出来的库,并将多出来的库给排除掉,而且Gradle也是支持这样做的,分别有两种方式:根据组件名排除或者根据包名排除,下面以排除support-v4库为例:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile("com.jude:easyrecyclerview:$rootProject.easyRecyclerVersion") {
exclude module: 'support-v4'//根据组件名排除
exclude group: 'android.support.v4'//根据包名排除
}
}
library重复依赖的问题算是都解决了,但是我们在开发项目的时候会依赖很多开源库,而这些库每个组件都需要用到,要是每个组件都去依赖一遍也是很麻烦的,尤其是给这些库升级的时候,为了方便我们统一管理第三方库,我们将给给整个工程提供统一的依赖第三方库的入口,前面介绍的Common库的作用之一就是统一依赖开源库,因为其他业务组件都依赖了Common库,所以这些业务组件也就间接依赖了Common所依赖的开源库。
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
//Android Support
compile "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"
compile "com.android.support:design:$rootProject.supportLibraryVersion"
compile "com.android.support:percent:$rootProject.supportLibraryVersion"
//网络请求相关
compile "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion"
compile "com.squareup.retrofit2:retrofit-mock:$rootProject.retrofitVersion"
compile "com.github.franmontiel:PersistentCookieJar:$rootProject.cookieVersion"
//稳定的
compile "com.github.bumptech.glide:glide:$rootProject.glideVersion"
compile "com.orhanobut:logger:$rootProject.loggerVersion"
compile "org.greenrobot:eventbus:$rootProject.eventbusVersion"
compile "com.google.code.gson:gson:$rootProject.gsonVersion"
compile "com.github.chrisbanes:PhotoView:$rootProject.photoViewVersion"
compile "com.jude:easyrecyclerview:$rootProject.easyRecyclerVersion"
compile "com.github.GrenderG:Toasty:$rootProject.toastyVersion"
//router
compile "com.github.mzule.activityrouter:activityrouter:$rootProject.routerVersion"
}
————————————————
APT:
这一步我们需要先了解 APT这个概念,APT(Annotation Processing Tool)是一种处理注解的工具,
它对源代码文件进行检测找出其中的Annotation,使用Annotation进行额外的处理。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。在这里我们将在每一个业务组件的 build.gradle 都引入ActivityRouter 的 Annotation处理器,我们将会在声明组件和Url的时候使用,annotationProcessor是Android官方提供的Annotation处理器插件,代码如下:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
annotationProcessor "com.github.mzule.activityrouter:compiler:$rootProject.annotationProcessor"
}
————————————————
4、组件化项目的工程类型
在组件化工程模型中主要有:app壳工程、业务组件和功能组件3种类型,而业务组件中的Main组件和功能组件中的Common组件比较特殊,下面将分别介绍。
1)app壳工程
app壳工程是从名称来解释就是一个空壳工程,没有任何的业务代码,也不能有Activity,但它又必须被单独划分成一个组件,而不能融合到其他组件中,是因为它有如下几点重要功能:
1、app壳工程中声明了我们Android应用的 Application,这个 Application 必须继承自 Common组件中的 BaseApplication(如果你无需实现自己的Application可以直接在表单声明BaseApplication),因为只有这样,在打包应用后才能让BaseApplication中的Context生效,当然你还可以在这个 Application中初始化我们工程中使用到的库文件,还可以在这里解决Android引用方法数不能超过 65535 的限制,对崩溃事件的捕获和发送也可以在这里声明。
2、app壳工程的 AndroidManifest.xml 是我Android应用的根表单,应用的名称、图标以及是否支持备份等等属性都是在这份表单中配置的,其他组件中的表单最终在集成开发模式下都被合并到这份 AndroidManifest.xml 中。
3、app壳工程的 build.gradle 是比较特殊的,app壳不管是在集成开发模式还是组件开发模式,它的属性始终都是:com.android.application,因为最终其他的组件都要被app壳工程所依赖,被打包进app壳工程中,这一点从组件化工程模型图中就能体现出来,所以app壳工程是不需要单独调试单独开发的。另外Android应用的打包签名,以及buildTypes和defaultConfig都需要在这里配置,而它的dependencies则需要根据isModule的值分别依赖不同的组件,在组件开发模式下app壳工程只需要依赖Common组件,或者为了防止报错也可以根据实际情况依赖其他功能组件,而在集成模式下app壳工程必须依赖所有在应用Application中声明的业务组件,并且不需要再依赖任何功能组件。
————————————————
2)功能组件和Common组件(公共依赖的库)
功能组件是为了支撑业务组件的某些功能而独立划分出来的组件,功能实质上跟项目中引入的第三方库是一样的,功能组件的特征如下:
1、功能组件的 AndroidManifest.xml 是一张空表,这张表中只有功能组件的包名;
2、功能组件不管是在集成开发模式下还是组件开发模式下属性始终是: com.android.library,所以功能组件是不需要读取 gradle.properties 中的 isModule 值的;另外功能组件的 build.gradle 也无需设置 buildTypes ,只需要 dependencies 这个功能组件需要的jar包和开源库。
manifest.xml重复权限问题
> Task :module_bracelet:processDebugManifest
/Users/mac/android-bracelet/bracelet/module_bracelet/src/main/module/AndroidManifest.xml:50:5-69 Warning:
Element uses-permission#android.permission.FLASHLIGHT at AndroidManifest.xml:50:5-69 duplicated with element declared at AndroidManifest.xml:48:5-69
/Users/mac/android-bracelet/bracelet/module_bracelet/src/main/module/AndroidManifest.xml:23:9-27:56 Warning:
activity#com.tencent.bugly.beta.ui.BetaActivity@tools:name was tagged at AndroidManifest.xml:23 to replace other declarations but no other declaration present
/Users/mac/android-bracelet/bracelet/module_bracelet/src/main/module/AndroidManifest.xml:23:9-27:56 Warning:
activity#com.tencent.bugly.beta.ui.BetaActivity@tools:configChanges was tagged at AndroidManifest.xml:23 to replace other declarations but no other declaration present
/Users/mac/android-bracelet/bracelet/module_bracelet/src/main/module/AndroidManifest.xml:23:9-27:56 Warning:
activity#com.tencent.bugly.beta.ui.BetaActivity@tools:theme was tagged at AndroidManifest.xml:23 to replace other declarations but no other declaration present
/Users/mac/android-bracelet/bracelet/module_bracelet/src/main/module/AndroidManifest.xml:29:9-39:20 Warning:
provider#com.tencent.bugly.beta.utils.BuglyFileProvider@tools:name was tagged at AndroidManifest.xml:29 to replace other declarations but no other declaration present
/Users/mac/android-bracelet/bracelet/module_bracelet/src/main/module/AndroidManifest.xml:29:9-39:20 Warning:
provider#com.tencent.bugly.beta.utils.BuglyFileProvider@tools:authorities was tagged at AndroidManifest.xml:29 to replace other declarations but no other declaration present
/Users/mac/android-bracelet/bracelet/module_bracelet/src/main/module/AndroidManifest.xml:29:9-39:20 Warning:
provider#com.tencent.bugly.beta.utils.BuglyFileProvider@tools:exported was tagged at AndroidManifest.xml:29 to replace other declarations but no other declaration present
/Users/mac/android-bracelet/bracelet/module_bracelet/src/main/module/AndroidManifest.xml:29:9-39:20 Warning:
provider#com.tencent.bugly.beta.utils.BuglyFileProvider@tools:grantUriPermissions was tagged at AndroidManifest.xml:29 to replace other declarations but no other declaration present
/Users/mac/android-bracelet/bracelet/module_bracelet/src/main/module/AndroidManifest.xml Warning:
meta-data#android.support.FILE_PROVIDER_PATHS@tools:name was tagged at AndroidManifest.xml:0 to replace other declarations but no other declaration present
/Users/mac/android-bracelet/bracelet/module_bracelet/src/main/module/AndroidManifest.xml Warning:
meta-data#android.support.FILE_PROVIDER_PATHS@tools:resource was tagged at AndroidManifest.xml:0 to replace other declarations but no other declaration present
Android 中使用 fragment 提示error inflating class fragment
需要添加引用
<fragment
android:id="@+id/frag_bracelet_debug"
android:layout_width="match_parent"
android:name="bracelet.yuedong.com.module_bracelet.fragment.BraceletFragment"
android:layout_height="match_parent"></fragment>
需要解决provider的名字重复的问题
app和module切换的时候,R文件总是不一样,切换,每次自己弄?
组件生命周期的管理:
看到了你的frame里的代码实现
明白了,目前管理组件生命周期貌似只有这种方式,通过获取到绝对类名,反射调用。
———————————————
我自己的参考:
简单入门:非常好的入门
非常不错的
完美方案:demo不错
弘扬的demo