All Downloads are FREE. Search and download functionalities are using the official Maven repository.

main.com.catchpig.download.manager.DownloadManager.kt Maven / Gradle / Ivy

The newest version!
package com.catchpig.download.manager

import android.os.Environment
import android.util.ArrayMap
import com.catchpig.download.api.DownloadService
import com.catchpig.download.callback.DownloadProgressListener
import com.catchpig.download.interceptor.DownloadInterceptor
import com.catchpig.utils.ext.deleteAll
import com.catchpig.utils.manager.ContextManager
import okhttp3.OkHttpClient
import okhttp3.ResponseBody
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.io.File
import java.io.IOException
import java.io.RandomAccessFile
import java.net.URL
import java.nio.channels.FileChannel
import java.util.concurrent.TimeUnit

open class DownloadManager {
    companion object {
        /**
         * 连接超时时间(秒)
         */
        private const val CONNECT_TIMEOUT = 5L

        /**
         * 读取数据超时时间(分钟)
         */
        private const val READ_TIMEOUT = 30L

        /**
         * 下载路径
         */
        private var downloadPath: String? = null

        private var downloadServiceMap: MutableMap = ArrayMap()

        private val logInterceptor by lazy {
            val httpLoggingInterceptor = HttpLoggingInterceptor()
            httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
            httpLoggingInterceptor
        }

        private val downloadInterceptor by lazy {
            DownloadInterceptor()
        }

        private var okHttpClient: OkHttpClient? = null

        private fun getOkHttpClient(): OkHttpClient {
            return okHttpClient.run {
                OkHttpClient
                    .Builder()
                    /**
                     * 连接超时时间5秒
                     */
                    .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                    /**
                     * 读取数据超时时间30分钟
                     */
                    .readTimeout(READ_TIMEOUT, TimeUnit.MINUTES)
                    .addInterceptor(logInterceptor)
                    .addInterceptor(downloadInterceptor)
                    .retryOnConnectionFailure(true)
                    .build()
            }
        }

        /**
         * 设置下载路径(如果不设置,会有默认的下载路径)
         * @param path String
         */
        fun setDownloadPath(path: String) {
            downloadPath = path
        }

        private fun getDownloadService(baseUrl: String, rxJava: Boolean): DownloadService {
            if (rxJava) {
                return getRxjavaDownloadService(baseUrl)
            }
            return getCoroutinesDownloadService(baseUrl)
        }

        private fun getRxjavaDownloadService(baseUrl: String): DownloadService {
            return Retrofit
                .Builder()
                .baseUrl(baseUrl)
                .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
                .client(getOkHttpClient())
                .addConverterFactory(GsonConverterFactory.create())
                .build()
                .create(DownloadService::class.java)
        }

        private fun getCoroutinesDownloadService(baseUrl: String): DownloadService {
            return Retrofit
                .Builder()
                .baseUrl(baseUrl)
                .client(getOkHttpClient())
                .addConverterFactory(GsonConverterFactory.create())
                .build()
                .create(DownloadService::class.java)
        }

        /**
         * 初始化下载器接口类
         * @param url URL
         * @param downloadProgressListener DownloadProgressListener
         * @return DownloadService
         */
        fun initDownloadService(
            url: URL,
            downloadProgressListener: DownloadProgressListener,
            rxJava: Boolean
        ): DownloadService {
            val baseUrl = "${url.protocol}://${url.authority}/"
            var downloadService = downloadServiceMap[baseUrl]
            downloadService = downloadService.run {
                getDownloadService(baseUrl, rxJava)
            }
            downloadServiceMap[baseUrl] = downloadService
            downloadInterceptor.addProgressListener(url.toString(), downloadProgressListener)
            return downloadService
        }
    }

    /**
     * 生成文件的地址
     * @param downloadUrl String
     * @return String
     */
    protected fun localFileName(downloadUrl: String): String {
        val fileName = downloadUrl.replace("/", "").replace("\\", "")
        var cashDir = getDownloadFilePath()
        return "$cashDir/$fileName"
    }

    private fun getDownloadFilePath(): String {
        downloadPath?.let {
            return it
        }
        val path = if (Environment.MEDIA_MOUNTED == Environment.getExternalStorageState()) {
            //有SD卡,拿到SD卡的/storage/sdcard0/Android/data/包名/cash目录
            ContextManager.getInstance().getContext().externalCacheDir!!.absolutePath
        } else {
            //没有SD卡的,拿到/data/data/包名/cash目录
            ContextManager.getInstance().getContext().cacheDir.absolutePath
        }
        return "$path/download"
    }

    fun clearFiles() {
        File(getDownloadFilePath()).deleteAll()
    }

    /**
     * 将下载的数据写入本地缓存
     * @param responseBody ResponseBody 下载的文件数据
     * @param fileName String 文件名称
     * @return String 本地路径
     * @throws IOException
     */
    @Throws(IOException::class)
    protected fun writeCache(responseBody: ResponseBody, fileName: String): String {
        var file = File(fileName)
        if (!file.parentFile.exists()) {
            file.parentFile.mkdirs()
        } else if (file.exists()) {
            if (file.length() == responseBody.contentLength()) {
                return fileName
            } else {
                file.delete()
            }
        }
        var randomAccessFile = RandomAccessFile(file, "rwd")
        var fileChannel = randomAccessFile.channel
        var mappedByteBuffer =
            fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, responseBody.contentLength())
        var buffer = ByteArray(1024 * 8)
        var len: Int
        while (true) {
            len = responseBody.byteStream().read(buffer)
            if (len == -1) {
                break
            }
            mappedByteBuffer.put(buffer, 0, len)
        }
        responseBody.byteStream().close()
        fileChannel?.close()
        randomAccessFile.close()
        return fileName
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy