模块化
将一个大项目拆分成互相独立的模块,降低各个部分之间的耦合性。 能大大提高代码复用能力,也能缕清项目的架构。便于团队协同开发。 模块化后,便于进行单模块调试。
对于常用功能,可抽出来作为基础模块。
重构工程,模块化思路和步骤¶
- 新建模块
- 移动枚举类和接口
- 移动工具类
- 减少全局变量,尽可能消除对Application的直接引用
- 移动执行最小操作的类
- 增加回调接口,增加通知方法,移动其他类
- 设计对外统一访问管理的类
Android 蓝牙管理模块¶
Android中需要调用蓝牙设备。
一开始是在Service中管理蓝牙设备的连接和业务。随着业务代码的增长,蓝牙的连接部分日趋复杂和混乱。 整个Service的代码在变大。业务要求针对同一款蓝牙硬件设备开发出不同的App。各个App要实现的功能不同。 为避免大量的重复代码,需要将这个蓝牙设备管理模块化。
单例化一个BtDevice
,相关信息都存储在单例中,可以直接调用。参照Google示例,在子线程中进行蓝牙连接。
BtDevice
模块与Service解耦。添加回调接口,发送蓝牙设备发回的数据。
App中有多个activity,各个页面生命周期不同。app主工程中增设一个消息管理Service,专门用于接受BtService的数据,
然后再用别的方法发送出去(EventBus,Broadcast等)。
在Android Studio中新建一个module,把蓝牙管理相关的代码放进模块,调整好API。编译后会有aar文件。 新的工程只需要引入aar文件即可。
模块化后,代码层级和逻辑更清晰,耦合度下降,更易维护。
使用git submodule管理模块¶
git子模块功能允许往git管理的项目中添加另一个由git管理的工程。
使用Android Studio创建Android App工程并由git进行管理。工程里可以用git submodule添加子模块。
可以在as中新建模块,然后把整个模块复制出去,模块目录下git init,提交到服务器。
回到工程中,git submodule add 模块地址
。添加后修改gradle即可。
问题与方法¶
xml中找不到module中的自定义view¶
- Android Studio 2.2.3
模块的build和调用方的build的配置要一致,最好全部配置成一样的。
比如compileSdkVersion等等。然后重新make module和rebuild project
使用Android Studio打包module得到aar文件¶
Android App的模块可以用aar文件来表示。
在as的Gradle侧边栏直接运行:mylib
-Tasks
-build
-assembleRelease
可以在module目录下project\mylib\build\outputs\aar
找到release版的aar文件
可以在模块的Gradle中加入重命名的任务
android {
//...
libraryVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.aar')) {
def fileName = "${archivesBaseName}-${defaultConfig.versionName}-${variant.buildType.name}.aar"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
}
gradle 3以后,不能使用outputFile
属性,报错:
Cannot set the value of read-only property 'outputFile' for object of type com.android.build.gradle.internal.api.LibraryVariantOutputImpl
修改gradle方法,添加一个getTime
方法
// 获取当前时间
static def getTime() {
String timeNow = new Date().format('YYYYMMdd-HHmmss')
return timeNow
}
android {
// ...
libraryVariants.all { variant ->
variant.outputs.all { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.aar')) {
outputFileName = "${archivesBaseName}-${defaultConfig.versionName}-${variant.buildType.name}_${getTime()}.aar"
}
}
}
}
执行assembleRelease后得到aar文件,mylib-1.0.0.0-release_20180914-091348.aar
。
gradle重复引用¶
子模块mylib
和主app工程中引用了相同的jar包。假设是commons-net-3.0.1.jar
。
编译时报错Multiple dex files define Lxxx/xxx/xxx;
。
app gradle引用子模块时申明transitive false
。
排除一些传递性依赖中的某个模块,此时便不能靠单纯的关闭依赖传递特性来解决了。
子模块中的FileProvider问题¶
- gradle 3.1.4
- compileSdkVersion 27
假设有子模块mylib
和app
模块。
子模块中要使用FileProvider
,步骤如下:
- 新建类继承
android.support.v4.content.FileProvider
- 编写path信息
- 模块
AndroidManifest.xml
中声明provider - 定义能修改
authorities
的方法
新建类RustFileProvider
,不复写其他方法。
import android.support.v4.content.FileProvider;
public class RustFileProvider extends FileProvider {
}
模块中新建文件res/xml/rust_provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="external_files"
path="." />
</paths>
模块AndroidManifest.xml
中声明provider
<application>
<provider
android:name=".RustFileProvider"
android:authorities="${applicationId}.file_provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/rust_provider_paths" />
</provider>
</application>
定义一个模块的配置文件
/**
* 全局配置文件
*/
public class DFConfig {
// 文件提供器名字前缀 要根据appID而改变
private static String fileProviderAuthoritiesPrefix = "applicationId";
public static String getFileProviderAuthoritiesPrefix() {
return fileProviderAuthoritiesPrefix;
}
/**
* @param fileProviderAuthoritiesPrefix 传入app的applicationId
*/
public static void setFileProviderAuthoritiesPrefix(String fileProviderAuthoritiesPrefix) {
DFConfig.fileProviderAuthoritiesPrefix = fileProviderAuthoritiesPrefix;
}
}
app模块代码中要预先将自己的applicationId设置进去。
在模块中的Activity调用分享功能。
private void shareOneFile(File sFile, String title) {
if (null != sFile && sFile.exists()) {
String sPath = sFile.getPath().toLowerCase();
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
Uri uri = RustFileProvider.getUriForFile(this,
DFConfig.getFileProviderAuthoritiesPrefix() + ".file_provider", sFile);
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
if (sPath.endsWith("jpg") || sPath.endsWith("jpeg")) {
shareIntent.setType("image/jpeg");
} else if (sPath.endsWith("mp4")) {
shareIntent.setType("video/*");
} else {
shareIntent.setType("*/*");
}
startActivity(Intent.createChooser(shareIntent, title));
}
}
本站说明
一起在知识的海洋里呛水吧。广告内容与本站无关。如果喜欢本站内容,欢迎投喂作者,谢谢支持服务器。如有疑问和建议,欢迎在下方评论~