增加任务列表和下载功能
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
package com.navinfo.omqs.http
|
||||
|
||||
class DefaultTaskResponse<T> {
|
||||
var success: Boolean = false
|
||||
var msg: String = ""
|
||||
var obj: T? = null
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.navinfo.omqs.http
|
||||
|
||||
import com.navinfo.omqs.bean.OfflineMapCityBean
|
||||
import com.navinfo.omqs.bean.TaskBean
|
||||
|
||||
|
||||
/**
|
||||
@@ -11,4 +12,8 @@ interface NetworkService {
|
||||
* 获取离线地图城市列表
|
||||
*/
|
||||
suspend fun getOfflineMapCityList():NetResult<List<OfflineMapCityBean>>
|
||||
/**
|
||||
* 获取任务列表
|
||||
*/
|
||||
suspend fun getTaskList(evaluatorNo:String): NetResult<DefaultTaskResponse<List<TaskBean>>>
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.navinfo.omqs.http
|
||||
|
||||
import com.navinfo.omqs.bean.OfflineMapCityBean
|
||||
import com.navinfo.omqs.bean.TaskBean
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
@@ -32,4 +33,23 @@ class NetworkServiceImpl @Inject constructor(
|
||||
NetResult.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getTaskList(evaluatorNo: String): NetResult<DefaultTaskResponse<List<TaskBean>>> =
|
||||
//在IO线程中运行
|
||||
withContext(Dispatchers.IO) {
|
||||
return@withContext try {
|
||||
val result = netApi.retrofitGetTaskList(evaluatorNo)
|
||||
if (result.isSuccessful) {
|
||||
if (result.code() == 200) {
|
||||
NetResult.Success(result.body())
|
||||
} else {
|
||||
NetResult.Failure(result.code(), result.message())
|
||||
}
|
||||
} else {
|
||||
NetResult.Failure(result.code(), result.message())
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
NetResult.Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
package com.navinfo.omqs.http
|
||||
|
||||
import com.navinfo.omqs.bean.OfflineMapCityBean
|
||||
import com.navinfo.omqs.bean.TaskBean
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.Query
|
||||
import retrofit2.http.Streaming
|
||||
import retrofit2.http.Url
|
||||
|
||||
@@ -46,8 +48,15 @@ interface RetrofitNetworkServiceAPI {
|
||||
*/
|
||||
@Streaming
|
||||
@GET
|
||||
suspend fun retrofitDownLoadFile(@Header("RANGE") start: String? = "0", @Url url: String):Response<ResponseBody>
|
||||
suspend fun retrofitDownLoadFile(
|
||||
@Header("RANGE") start: String? = "0",
|
||||
@Url url: String
|
||||
): Response<ResponseBody>
|
||||
|
||||
@GET("/devcp/task?evaluatType=2")
|
||||
suspend fun retrofitGetTaskList(
|
||||
@Query("evaluatorNo") evaluatorNo: String,
|
||||
): Response<DefaultTaskResponse<List<TaskBean>>>
|
||||
|
||||
/**
|
||||
* @FormUrlEncoded 请求格式注解,请求实体是一个From表单,每个键值对需要使用@Field注解
|
||||
|
||||
@@ -112,6 +112,4 @@ class OfflineMapDownloadManager(
|
||||
scopeMap[id]!!.removeObserver()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import com.navinfo.omqs.Constant
|
||||
import com.navinfo.omqs.bean.OfflineMapCityBean
|
||||
import com.navinfo.omqs.tools.FileManager
|
||||
import com.navinfo.omqs.tools.FileManager.Companion.FileDownloadStatus
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
@@ -23,7 +25,7 @@ class OfflineMapDownloadScope(
|
||||
private val downloadManager: OfflineMapDownloadManager,
|
||||
val cityBean: OfflineMapCityBean,
|
||||
) :
|
||||
CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
||||
CoroutineScope by CoroutineScope(Dispatchers.IO + CoroutineName("OfflineMapDownLoad")) {
|
||||
/**
|
||||
*下载任务,用来取消的
|
||||
*/
|
||||
@@ -46,7 +48,7 @@ class OfflineMapDownloadScope(
|
||||
|
||||
//改进的代码
|
||||
fun start() {
|
||||
change(OfflineMapCityBean.WAITING)
|
||||
change(FileDownloadStatus.WAITING)
|
||||
downloadManager.launchScope(this@OfflineMapDownloadScope)
|
||||
}
|
||||
|
||||
@@ -56,7 +58,7 @@ class OfflineMapDownloadScope(
|
||||
*/
|
||||
fun pause() {
|
||||
downloadJob?.cancel("pause")
|
||||
change(OfflineMapCityBean.PAUSE)
|
||||
change(FileDownloadStatus.PAUSE)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,11 +67,8 @@ class OfflineMapDownloadScope(
|
||||
*/
|
||||
fun launch() {
|
||||
downloadJob = launch() {
|
||||
Log.e("jingo", "启动下载1")
|
||||
download()
|
||||
Log.e("jingo", "启动下载2")
|
||||
downloadManager.launchNext(cityBean.id)
|
||||
Log.e("jingo", "启动下载3")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +77,7 @@ class OfflineMapDownloadScope(
|
||||
* 是否是等待任务
|
||||
*/
|
||||
fun isWaiting(): Boolean {
|
||||
return cityBean.status == OfflineMapCityBean.WAITING
|
||||
return cityBean.status == FileDownloadStatus.WAITING
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,7 +85,7 @@ class OfflineMapDownloadScope(
|
||||
* @param status [OfflineMapCityBean.Status]
|
||||
*/
|
||||
private fun change(status: Int) {
|
||||
if (cityBean.status != status || status == OfflineMapCityBean.LOADING) {
|
||||
if (cityBean.status != status || status == FileDownloadStatus.LOADING) {
|
||||
cityBean.status = status
|
||||
downloadData.postValue(cityBean)
|
||||
launch(Dispatchers.IO) {
|
||||
@@ -128,7 +127,7 @@ class OfflineMapDownloadScope(
|
||||
url = cityBean.url
|
||||
)
|
||||
val responseBody = response.body()
|
||||
change(OfflineMapCityBean.LOADING)
|
||||
change(FileDownloadStatus.LOADING)
|
||||
responseBody ?: throw IOException("jingo ResponseBody is null")
|
||||
//写入文件
|
||||
randomAccessFile = RandomAccessFile(fileTemp, "rwd")
|
||||
@@ -144,7 +143,7 @@ class OfflineMapDownloadScope(
|
||||
if (readLength != -1) {
|
||||
randomAccessFile.write(buffer, 0, readLength)
|
||||
cityBean.currentSize += readLength
|
||||
change(OfflineMapCityBean.LOADING)
|
||||
change(FileDownloadStatus.LOADING)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
@@ -155,15 +154,15 @@ class OfflineMapDownloadScope(
|
||||
val res =
|
||||
fileTemp.renameTo(File("${Constant.OFFLINE_MAP_PATH}${cityBean.fileName}"))
|
||||
Log.e("jingo", "文件下载完成 修改文件 $res")
|
||||
change(OfflineMapCityBean.DONE)
|
||||
change(FileDownloadStatus.DONE)
|
||||
withContext(Dispatchers.Main) {
|
||||
downloadManager.mapController.layerManagerHandler.loadBaseMap()
|
||||
}
|
||||
} else {
|
||||
change(OfflineMapCityBean.PAUSE)
|
||||
change(FileDownloadStatus.PAUSE)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
change(OfflineMapCityBean.ERROR)
|
||||
change(FileDownloadStatus.ERROR)
|
||||
} finally {
|
||||
inputStream?.close()
|
||||
randomAccessFile?.close()
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.navinfo.omqs.http.taskdownload
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.Observer
|
||||
import com.navinfo.omqs.bean.TaskBean
|
||||
import com.navinfo.omqs.http.RetrofitNetworkServiceAPI
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
|
||||
/**
|
||||
* 管理任务数据下载
|
||||
*/
|
||||
|
||||
class TaskDownloadManager(
|
||||
val netApi: RetrofitNetworkServiceAPI,
|
||||
) {
|
||||
/**
|
||||
* 最多同时下载数量
|
||||
*/
|
||||
private val MAX_SCOPE = 3
|
||||
|
||||
/**
|
||||
* 存储有哪些城市需要下载的队列
|
||||
*/
|
||||
private val scopeMap: ConcurrentHashMap<Int, TaskDownloadScope> by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
|
||||
ConcurrentHashMap<Int, TaskDownloadScope>()
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储正在下载的城市队列
|
||||
*/
|
||||
private val taskScopeMap: ConcurrentHashMap<Int, TaskDownloadScope> by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
|
||||
ConcurrentHashMap<Int, TaskDownloadScope>()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 启动下载任务
|
||||
* 请不要直接使用此方法启动下载任务,它是交由[OfflineMapDownloadScope]进行调用
|
||||
*/
|
||||
fun launchScope(scope: TaskDownloadScope) {
|
||||
if (taskScopeMap.size >= MAX_SCOPE) {
|
||||
return
|
||||
}
|
||||
if (taskScopeMap.contains(scope.taskBean.id)) {
|
||||
return
|
||||
}
|
||||
taskScopeMap[scope.taskBean.id] = scope
|
||||
scope.launch()
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动下一个任务,如果有正在等待中的任务的话
|
||||
* 请不要直接使用此方法启动下载任务,它是交由[OfflineMapDownloadScope]进行调用
|
||||
* @param previousUrl 上一个下载任务的下载连接
|
||||
*/
|
||||
fun launchNext(id: Int) {
|
||||
taskScopeMap.remove(id)
|
||||
for (entrySet in scopeMap) {
|
||||
val downloadScope = entrySet.value
|
||||
if (downloadScope.isWaiting()) {
|
||||
launchScope(downloadScope)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停任务
|
||||
* 只有等待中的任务和正在下载中的任务才可以进行暂停操作
|
||||
*/
|
||||
fun pause(id: Int) {
|
||||
if (taskScopeMap.containsKey(id)) {
|
||||
val downloadScope = taskScopeMap[id]
|
||||
downloadScope?.let {
|
||||
downloadScope.pause()
|
||||
}
|
||||
launchNext(id)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 将下载任务加入到协程作用域的下载队列里
|
||||
* 请求一个下载任务[OfflineMapDownloadScope]
|
||||
* 这是创建[OfflineMapDownloadScope]的唯一途径,请不要通过其他方式创建[OfflineMapDownloadScope]
|
||||
*/
|
||||
fun start(id: Int) {
|
||||
scopeMap[id]?.start()
|
||||
}
|
||||
|
||||
|
||||
fun addTask(taskBean: TaskBean) {
|
||||
if (!scopeMap.containsKey(taskBean.id)) {
|
||||
scopeMap[taskBean.id] = TaskDownloadScope(this, taskBean)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun observer(
|
||||
id: Int, lifecycleOwner: LifecycleOwner, observer: Observer<TaskBean>
|
||||
) {
|
||||
if (scopeMap.containsKey(id)) {
|
||||
scopeMap[id]!!.observer(lifecycleOwner, observer)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeObserver(id: Int) {
|
||||
if (scopeMap.containsKey(id)) {
|
||||
scopeMap[id]!!.removeObserver()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package com.navinfo.omqs.http.taskdownload
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import com.navinfo.omqs.Constant
|
||||
import com.navinfo.omqs.bean.TaskBean
|
||||
import com.navinfo.omqs.tools.FileManager.Companion.FileDownloadStatus
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.RandomAccessFile
|
||||
|
||||
class TaskDownloadScope(
|
||||
private val downloadManager: TaskDownloadManager,
|
||||
val taskBean: TaskBean,
|
||||
) :
|
||||
CoroutineScope by CoroutineScope(Dispatchers.IO + CoroutineName("OfflineMapDownLoad")) {
|
||||
/**
|
||||
*下载任务,用来取消的
|
||||
*/
|
||||
private var downloadJob: Job? = null
|
||||
|
||||
/**
|
||||
* 管理观察者,同时只有一个就行了
|
||||
*/
|
||||
private val observer = Observer<Any> {}
|
||||
// private var lifecycleOwner: LifecycleOwner? = null
|
||||
|
||||
/**
|
||||
*通知UI更新
|
||||
*/
|
||||
private val downloadData = MutableLiveData<TaskBean>()
|
||||
|
||||
init {
|
||||
downloadData.value = taskBean
|
||||
}
|
||||
|
||||
//改进的代码
|
||||
fun start() {
|
||||
change(FileDownloadStatus.WAITING)
|
||||
downloadManager.launchScope(this@TaskDownloadScope)
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停任务
|
||||
* 其实就是取消任务,移除监听
|
||||
*/
|
||||
fun pause() {
|
||||
downloadJob?.cancel("pause")
|
||||
change(FileDownloadStatus.PAUSE)
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动协程进行下载
|
||||
* 请不要尝试在外部调用此方法,那样会脱离[OfflineMapDownloadManager]的管理
|
||||
*/
|
||||
fun launch() {
|
||||
downloadJob = launch() {
|
||||
download()
|
||||
downloadManager.launchNext(taskBean.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 是否是等待任务
|
||||
*/
|
||||
fun isWaiting(): Boolean {
|
||||
return taskBean.status == FileDownloadStatus.WAITING
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务
|
||||
* @param status [OfflineMapCityBean.Status]
|
||||
*/
|
||||
private fun change(status: Int) {
|
||||
if (taskBean.status != status || status == FileDownloadStatus.LOADING) {
|
||||
taskBean.status = status
|
||||
downloadData.postValue(taskBean)
|
||||
launch(Dispatchers.IO) {
|
||||
// downloadManager.roomDatabase.getOfflineMapDao().update(taskBean)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加下载任务观察者
|
||||
*/
|
||||
fun observer(owner: LifecycleOwner, ob: Observer<TaskBean>) {
|
||||
removeObserver()
|
||||
// this.lifecycleOwner = owner
|
||||
downloadData.observe(owner, ob)
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
*/
|
||||
private suspend fun download() {
|
||||
var inputStream: InputStream? = null
|
||||
var randomAccessFile: RandomAccessFile? = null
|
||||
try {
|
||||
//创建离线地图 下载文件夹,.map文件夹的下一级
|
||||
val fileDir = File("${Constant.OFFLINE_MAP_PATH}download")
|
||||
if (!fileDir.exists()) {
|
||||
fileDir.mkdirs()
|
||||
}
|
||||
|
||||
val fileTemp =
|
||||
File("${Constant.OFFLINE_MAP_PATH}download/${taskBean.id}_${taskBean.dataVersion}")
|
||||
val startPosition = taskBean.currentSize
|
||||
//验证断点有效性
|
||||
if (startPosition < 0) throw IOException("jingo Start position less than zero")
|
||||
val response = downloadManager.netApi.retrofitDownLoadFile(
|
||||
start = "bytes=$startPosition-",
|
||||
url = taskBean.getDownLoadUrl()
|
||||
)
|
||||
val responseBody = response.body()
|
||||
change(FileDownloadStatus.LOADING)
|
||||
responseBody ?: throw IOException("jingo ResponseBody is null")
|
||||
//写入文件
|
||||
randomAccessFile = RandomAccessFile(fileTemp, "rwd")
|
||||
randomAccessFile.seek(startPosition)
|
||||
taskBean.currentSize = startPosition
|
||||
inputStream = responseBody.byteStream()
|
||||
val bufferSize = 1024 * 2
|
||||
val buffer = ByteArray(bufferSize)
|
||||
|
||||
var readLength = 0
|
||||
while (downloadJob?.isActive == true) {
|
||||
readLength = inputStream.read(buffer)
|
||||
if (readLength != -1) {
|
||||
randomAccessFile.write(buffer, 0, readLength)
|
||||
taskBean.currentSize += readLength
|
||||
change(FileDownloadStatus.LOADING)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Log.e("jingo", "文件下载完成 ${taskBean.currentSize} == ${taskBean.fileSize}")
|
||||
if (taskBean.currentSize == taskBean.fileSize) {
|
||||
val res =
|
||||
fileTemp.renameTo(File("${Constant.OFFLINE_MAP_PATH}${taskBean.evaluationTaskName}.zip"))
|
||||
Log.e("jingo", "文件下载完成 修改文件 $res")
|
||||
change(FileDownloadStatus.DONE)
|
||||
} else {
|
||||
change(FileDownloadStatus.PAUSE)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
change(FileDownloadStatus.ERROR)
|
||||
} finally {
|
||||
inputStream?.close()
|
||||
randomAccessFile?.close()
|
||||
}
|
||||
}
|
||||
|
||||
fun removeObserver() {
|
||||
downloadData.observeForever(observer)
|
||||
// lifecycleOwner?.let {
|
||||
downloadData.removeObserver(observer)
|
||||
// null
|
||||
// }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user