最后
光有这些思路和搞懂单个知识的应用是还远远不够的,在Android开源框架设计思想中的知识点还是比较多的,想要搞懂还得学会整理和规划:我们常见的**Android热修复框架、插件化框架、组件化框架、图片加载框架、网络访问框架、RxJava响应式编程框架、IOC依赖注入框架、最近架构组件Jetpack等等Android第三方开源框架,**这些都是属于Android开源框架设计思想的。如下图所示:
这位阿里P8大佬针对以上知识点,熬夜整理出了一本长达1042页的完整版如何解读开源框架设计思想PDF文档,内容详细,把Android热修复框架、插件化框架、组件化框架、图片加载框架、网络访问框架、RxJava响应式编程框架、IOC依赖注入框架、最近架构组件Jetpack等等Android第三方开源框架这些知识点从源码分析到实战应用都讲的简单明了。
由于文档内容过多,篇幅受限,只能截图展示部分
整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~
你的支持,我的动力;祝各位前程似锦,offer不断!!!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
作者:不怕天黑
链接:https://juejin.im/post/6884986439587594247
老规矩,先看看请求三部曲
如果你想了解RxHttp更过功能,请查看以下系列文章
gradle依赖
//使用kapt依赖rxhttp-compiler,需要导入kapt插件
apply plugin: ‘kotlin-kapt’
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [
//必须,告知RxHttp你依赖的okhttp版本,目前已适配 v3.12.0 - v4.9.0版本 (v4.3.0除外)
rxhttp_okhttp: ‘4.9.0’,
//使用asXxx方法时必须,告知RxHttp你依赖的rxjava版本,可传入rxjava2、rxjava3
rxhttp_rxjava: ‘rxjava3’,
rxhttp_package: ‘rxhttp’ //非必须,指定RxHttp类包名
]
}
}
}
//必须,java 8或更高
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
//以下3个为必须,
implementation ‘com.ljx.rxhttp:rxhttp:2.4.1’
implementation ‘com.squareup.okhttp3:okhttp:4.9.0’ //rxhttp v2.2.2版本起,需要手动依赖okhttp
kapt ‘com.ljx.rxhttp:rxhttp-compiler:2.4.1’ //生成RxHttp类,纯Java项目,请使用annotationProcessor代替kapt
implementation ‘com.ljx.rxlife:rxlife-coroutine:2.0.1’ //管理协程生命周期,页面销毁,关闭请求
//rxjava2 (RxJava2/Rxjava3二选一,使用asXxx方法时必须)
implementation ‘io.reactivex.rxjava2:rxjava:2.2.8’
implementation ‘io.reactivex.rxjava2:rxandroid:2.1.1’
implementation ‘com.ljx.rxlife2:rxlife-rxjava:2.0.0’ //管理RxJava2生命周期,页面销毁,关闭请求
//rxjava3
implementation ‘io.reactivex.rxjava3:rxjava:3.0.6’
implementation ‘io.reactivex.rxjava3:rxandroid:3.0.0’
implementation ‘com.ljx.rxlife3:rxlife-rxjava:3.0.0’ //管理RxJava3生命周期,页面销毁,关闭请求
//非必须,根据自己需求选择 RxHttp默认内置了GsonConverter
implementation ‘com.ljx.rxhttp:converter-fastjson:2.4.1’
implementation ‘com.ljx.rxhttp:converter-jackson:2.4.1’
implementation ‘com.ljx.rxhttp:converter-moshi:2.4.1’
implementation ‘com.ljx.rxhttp:converter-protobuf:2.4.1’
implementation ‘com.ljx.rxhttp:converter-simplexml:2.4.1’
}
2、Android 10/11 分区存储
当我们App的targetSdkVersion
更改为28以上,并且运行在Android 10以上设备时,我们无法再以绝对路径的方式,去读写非沙盒目录下的文件,当然,如果App是覆盖安装(如:targetSdkVersion 28 覆盖安装为 29),则会保持原来的访问方式。
requestLegacyExternalStorage属性
如果我们的app将targetSdkVersion更改为28以上,且想保持原来的访问方式,则需要在清单文件中将 requestLegacyExternalStorage
的值设置为 true
,如下:
<manifest …>
<application android:requestLegacyExternalStorage=“true” … >
…
此时,便可继续以原来的方式去读写文件,然而,在Android 11上,Google又给了它新的含义,来看看官网的原话
也就是说,在Android 11设备上,targetSdkVersion
为29以上的app,将强制开启分区存储,requestLegacyExternalStorage
属性失效
注意,只要同时满足以上两个条件,不管是覆盖安装还是requestLegacyExternalStorage = true
,都会强制开启分区存储
分区存储优势
- 对用户来说,解决了文件乱放的现象
- 对于开发者来说,我们无需写权限,就可以在分区目录下创建文件,并且访问自己创建的文件,不需要读权限(访问其它应用创建的文件,还是需要读权限)
新的文件访问方式
此图来源于作者[连续三届村草]分享的Android 10(Q)/11® 分区存储适配一文,感谢作者的总结
3、上传
3.1、简单上传
在介绍Android 10文件上传前,我们先来看看Android 10之前是如何上传文件的,如下:
//kotlin 协程
val result = RxHttp.postForm(“/service/…”)
.add(“key”, “value”)
.addFile(“file”, new File(“xxx/1.jpg”))
.awaitString() //awaitXxx系列方法是挂断方法
//RxJava
RxHttp.postForm(“/service/…”)
.add(“key”, “value”)
.addFile(“file”, new File(“xxx/1.jpg”))
.asString()
.subscribe({
//成功回调
}, {
//异常回调
})
以上,我们仅需调用 addFile
方法添加文件对象即可,RxHttp提供了一系列addFile
方法,列出几个常用的,如下:
//添加单个文件
addFile(String, File)
//添加多个文件,每个文件对应相同的key
addFile(String, List<? extends File> fileList)
//添加多个文件,每个文件对应不同的key
addFile(Map<String, ? extends File> fileMap)
//等等其它addFile方法
在Android 10,我们需要通过Uri对象去上传文件,在RxHttp中,通过addPart
方法添加Uri
对象,如下:
val context = getContext(); //获取上下文对象
//获取Uri对象,这里为了方便,随便写了一个Downlaod目录下的Uri地址
val uri = Uri.parse(“content://media/external/downloads/13417”)
//kotlin 协程
val result = RxHttp.postForm(“/service/…”)
.add(“key”, “value”)
.addPart(context, “file”, uri)
.awaitString() //awaitXxx系列方法是挂断方法
//RxJava
RxHttp.postForm(“/service/…”)
.add(“key”, “value”)
.addPart(context, “file”, uri)
.asString()
.subscribe({
//成功回调
}, {
//异常回调
})
同样的,RxHttp内部提供了一系列addPart
方法供大家选择,列出几个常用的,如下:
//添加单个Uri对象
addPart(Context, String, Uri)
//添加多个Uri对象,每个Uri对应相同的key
addParts(Context,String, List<? extends Uri> uris)
//添加多个Uri对象,每个Uri对应不同的key
addParts(Context context, Map<String, ? extends Uri> uriMap)
//等等其它addPart方法
3.2、带进度上传
老规矩,看看Android 10之前是如何监听上传进度的,如下:
//kotlin 协程
val result = RxHttp.postForm(“/service/…”)
.add(“key”, “value”)
.addFile(“file”, new File(“xxx/1.jpg”))
.upload(this) {//this为当前协程CoroutineScope对象,用于控制回调线程
//上传进度回调,0-100,仅在进度有更新时才会回调
val currentProgress = it.getProgress() //当前进度 0-100
val currentSize = it.getCurrentSize() //当前已上传的字节大小
val totalSize = it.getTotalSize() //要上传的总字节大小
}
.awaitString() //awaitXxx系列方法是挂断方法
//RxJava
RxHttp.postForm(“/service/…”)
.add(“key”, “value”)
.addFile(“file”, new File(“xxx/1.jpg”))
.upload(AndroidSchedulers.mainThread()) {
//上传进度回调,0-100,仅在进度有更新时才会回调
int currentProgress = it.getProgress() //当前进度 0-100
long currentSize = it.getCurrentSize() //当前已上传的字节大小
long totalSize = it.getTotalSize() //要上传的总字节大小
}
.asString()
.subscribe({
//成功回调
}, {
//异常回调
})
相比于单纯的上传文件,我们仅需额外调用upload
操作符,传入线程调度器及进度回调即可。
同样的,对于Andorid 10,我们仅需要将File对象换成Uri对象即可,如下:
val context = getContext(); //获取上下文对象
//获取Uri对象,这里为了方便,随便写了一个Downlaod目录下的Uri地址
val uri = Uri.parse(“content://media/external/downloads/13417”)
//kotlin 协程
val result = RxHttp.postForm(“/service/…”)
.add(“key”, “value”)
.addPart(context, “file”, uri)
.upload(this) {//this为当前协程CoroutineScope对象,用于控制回调线程
//上传进度回调,0-100,仅在进度有更新时才会回调
val currentProgress = it.getProgress() //当前进度 0-100
val currentSize = it.getCurrentSize() //当前已上传的字节大小
val totalSize = it.getTotalSize() //要上传的总字节大小
}
.awaitString() //awaitXxx系列方法是挂断方法
//RxJava
RxHttp.postForm(“/service/…”)
.add(“key”, “value”)
.addPart(context, “file”, uri)
.upload(AndroidSchedulers.mainThread()) {
//上传进度回调,0-100,仅在进度有更新时才会回调
int currentProgress = it.getProgress() //当前进度 0-100
long currentSize = it.getCurrentSize() //当前已上传的字节大小
long totalSize = it.getTotalSize() //要上传的总字节大小
}
.asString()
.subscribe({
//成功回调
}, {
//异常回调
})
怎么样?是不是so easy!!
4、下载
下载较于上传,要丰富很多,RxHttp
内部提供类一系列下载方法来满足不同的需求,如下:
//kotlin
fun IRxHttp.toDownload(
destPath: String,
context: CoroutineContext? = null,
progress: (suspend (ProgressT) -> Unit)? = null
)
fun IRxHttp.toDownload(
context: Context,
uri: Uri,
coroutineContext: CoroutineContext? = null,
progress: (suspend (ProgressT) -> Unit)? = null
)
fun IRxHttp.toDownload(
osFactory: OutputStreamFactory,
context: CoroutineContext? = null,
progress: (suspend (ProgressT) -> Unit)? = null
)
4.1、简单下载
在Android 10之前,我们仅需传入一个本地文件路径即可,如下:
val localPath = “/sdcard/…/xxx.apk”
//kotlin 协程
val result = RxHttp.get(“/service/…/xxx.apk”)
.toDownload(localPath)
.await() //这里返回sd卡存储路径
//RxJava
RxHttp.get(“/service/…/xxx.apk”)
.asDownload(localPath)
.subscribe({
//成功回调,这里返回sd卡存储路径
}, {
//异常回调
})
而到了Android 10,我们需要自定义一个Android10DownloadFactory
类,继承UriFactory
类,如下:
class Android10DownloadFactory @JvmOverloads constructor(
context: Context,
fileName: String,
queryUri: Uri? = null
) : UriFactory(context, queryUri, fileName) {
override fun getUri(response: Response): Uri {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentValues().run {
put(MediaStore.MediaColumns.DISPLAY_NAME, fileName) //文件名
//取contentType响应头作为文件类型
put(MediaStore.MediaColumns.MIME_TYPE, response.body?.contentType().toString())
//下载到Download目录
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
val uri = queryUri ?: MediaStore.Downloads.EXTERNAL_CONTENT_URI
context.contentResolver.insert(uri, this)
} ?: throw NullPointerException(“Uri insert fail, Please change the file name”)
} else {
Uri.fromFile(File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), displayName))
}
}
}
这里简单介绍下上面的代码,本文后续会详细介绍为啥定义这样一个类,以及如何构建一个Uri
对象。
- 首先就是继承
UriFactory
抽象类,实现getUri(Response)
方法 - 接着就在实现方法里判断SDK版本,Android 10以上,就通过
contentResolver
构建Uri对象,否则就根据File
对象构建Uri对象,这样就可以兼容到所有系统版本 - 最后返回
Uri
对象即可
注:以上代码,基本可以满足大部分人的需求,如你有特殊需求,构建Uri的过程的作出简单的修改即可
有了Android10DownloadFactory
类,执行Android 10
下载就会及其方便,如下:
val factory = Android10DownloadFactory(context, “test.apk”)
//kotlin 协程
val uri = RxHttp.get(“/service/…/xxx.apk”)
.toDownload(factory)
.await() //这里返回工厂类构建的Uri对象
尾声
开发是需要一定的基础的,我是08年开始进入Android这行的,在这期间经历了Android的鼎盛时期,和所谓的Android”凉了“。中间当然也有着,不可说的心酸,看着身边朋友,同事一个个转前端,换行业,其实当时我的心也有过犹豫,但是我还是坚持下来了,这次的疫情就是一个好的机会,大浪淘沙,优胜劣汰。再等等,说不定下一个黄金浪潮就被你等到了。
- 330页 PDF Android核心笔记
- 几十套阿里 、字节跳动、腾讯、华为、美团等公司2020年的面试题
- PDF和思维脑图,包含知识脉络 + 诸多细节
- Android进阶系统学习视频
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
69278595)]
- PDF和思维脑图,包含知识脉络 + 诸多细节
[外链图片转存中…(img-gE5kcaSh-1715269278596)]
- Android进阶系统学习视频
[外链图片转存中…(img-vwnrCzLc-1715269278596)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!