Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/219.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 安卓改型下载进度_Android - Fatal编程技术网

Android 安卓改型下载进度

Android 安卓改型下载进度,android,Android,我是新装修的。我搜索过了,但没有找到一个简单的答案。我想知道如何在通知栏中显示下载进度,或者至少显示一个进度对话框,指定下载文件的进程百分比和大小。 这是我的密码: public interface ServerAPI { @GET Call<ResponseBody> downlload(@Url String fileUrl); Retrofit retrofit = new Retrofit.Builder()

我是新装修的。我搜索过了,但没有找到一个简单的答案。我想知道如何在通知栏中显示下载进度,或者至少显示一个进度对话框,指定下载文件的进程百分比和大小。 这是我的密码:

public interface ServerAPI {
    @GET
    Call<ResponseBody> downlload(@Url String fileUrl);

    Retrofit retrofit =
            new Retrofit.Builder()
                    .baseUrl("http://192.168.43.135/retro/") 
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();

}

public void download(){
    ServerAPI api = ServerAPI.retrofit.create(ServerAPI.class);
    api.downlload("https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png").enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            try {
                File path = Environment.getExternalStorageDirectory();
                File file = new File(path, "file_name.jpg");
                FileOutputStream fileOutputStream = new FileOutputStream(file);
                IOUtils.write(response.body().bytes(), fileOutputStream);
            }
            catch (Exception ex){
            }
        }


        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
        }
    });
}
公共接口服务器API{
@得到
调用download(@Url字符串fileUrl);
改装=
新的改型.Builder()
.baseUrl(“http://192.168.43.135/retro/") 
.addConverterFactory(GsonConverterFactory.create())
.build();
}
公开下载{
ServerAPI=ServerAPI.reformation.create(ServerAPI.class);
api.download(“https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png“”。排队(新回调(){
@凌驾
公共void onResponse(调用、响应){
试一试{
文件路径=Environment.getExternalStorageDirectory();
File File=新文件(路径,“File_name.jpg”);
FileOutputStream FileOutputStream=新的FileOutputStream(文件);
写入(response.body().bytes(),fileOutputStream);
}
捕获(例外情况除外){
}
}
@凌驾
失败时公共无效(调用调用,可丢弃的t){
}
});
}
如果可以的话,请引导我。
谢谢

您可以看一看,您不必自己实现它,背后的想法是获取请求的内容长度,当您在缓冲区上写入时,只需计算您的进度

您需要创建一个特定的OkHttp客户端,它将拦截网络请求并发送更新。此客户端应仅用于下载

首先,您需要一个接口,比如:

public interface OnAttachmentDownloadListener {
    void onAttachmentDownloadedSuccess();
    void onAttachmentDownloadedError();
    void onAttachmentDownloadedFinished();
    void onAttachmentDownloadUpdate(int percent);
}
您的下载呼叫应返回一个
响应库
,我们将从中扩展该响应库以获取下载进度

private static class ProgressResponseBody extends ResponseBody {

    private final ResponseBody responseBody;
    private final OnAttachmentDownloadListener progressListener;
    private BufferedSource bufferedSource;

    public ProgressResponseBody(ResponseBody responseBody, OnAttachmentDownloadListener progressListener) {
        this.responseBody = responseBody;
        this.progressListener = progressListener;
    }

    @Override public MediaType contentType() {
        return responseBody.contentType();
    }

    @Override public long contentLength() {
        return responseBody.contentLength();
    }

    @Override public BufferedSource source() {
        if (bufferedSource == null) {
            bufferedSource = Okio.buffer(source(responseBody.source()));
        }
        return bufferedSource;
    }

    private Source source(Source source) {
        return new ForwardingSource(source) {
            long totalBytesRead = 0L;

            @Override public long read(Buffer sink, long byteCount) throws IOException {
                long bytesRead = super.read(sink, byteCount);

                totalBytesRead += bytesRead != -1 ? bytesRead : 0;

                float percent = bytesRead == -1 ? 100f : (((float)totalBytesRead / (float) responseBody.contentLength()) * 100);

                if(progressListener != null)
                    progressListener.onAttachmentDownloadUpdate((int)percent);

                return bytesRead;
            }
        };
    }
}
然后您需要像这样创建OkHttpClient

public OkHttpClient.Builder getOkHttpDownloadClientBuilder(OnAttachmentDownloadListener progressListener) {
    OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();

    // You might want to increase the timeout
    httpClientBuilder.connectTimeout(20, TimeUnit.SECONDS);
    httpClientBuilder.writeTimeout(0, TimeUnit.SECONDS);
    httpClientBuilder.readTimeout(5, TimeUnit.MINUTES);

    httpClientBuilder.addInterceptor(new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            if(progressListener == null) return chain.proceed(chain.request());

        Response originalResponse = chain.proceed(chain.request());
        return originalResponse.newBuilder()
                .body(new ProgressResponseBody(originalResponse.body(), progressListener))
                .build();
        }
    });

    return httpClientBuilder;
}
最后,您只需通过传递新的OkHttp客户端,以不同的方式创建改造客户端。根据您的代码,您可以使用以下内容:

 public Retrofit getDownloadRetrofit(OnAttachmentDownloadListener listener) {

    return new Retrofit.Builder()
                .baseUrl("http://192.168.43.135/retro/") 
                .addConverterFactory(GsonConverterFactory.create())
                .client(getOkHttpDownloadClientBuilder(listener).build())
                .build();

}

您的侦听器将处理通知的创建或您想要的任何其他操作。

这是我与Kotlin的协同程序的变体

  • 指定API接口。我们需要
    @Streaming
    注释来说明我们想要手动处理响应体的改装。否则,改型将尝试将文件直接写入RAM

  • 现在,您可以在
    ViewModel
    Presenter
    中执行
    downloadZip()
    方法,并对其进行回调,回调将链接到一些
    progersbar
    。下载完成后,您将收到下载的文件。

    这里是另一个使用Flow的Kotlin解决方案

    interface MyService {
        @Streaming // allows streaming data directly to fs without holding all contents in ram
        @GET
        suspend fun getUrl(@Url url: String): ResponseBody
    }
    
    sealed class Download {
        data class Progress(val percent: Int) : Download()
        data class Finished(val file: File) : Download()
    }
    
    fun ResponseBody.downloadToFileWithProgress(directory: File, filename: String): Flow<Download> =
        flow {
            emit(Download.Progress(0))
    
            // flag to delete file if download errors or is cancelled
            var deleteFile = true
            val file = File(directory, "${filename}.${contentType()?.subtype}")
    
            try {
                byteStream().use { inputStream ->
                    file.outputStream().use { outputStream ->
                        val totalBytes = contentLength()
                        val data = ByteArray(8_192)
                        var progressBytes = 0L
    
                        while (true) {
                            val bytes = inputStream.read(data)
    
                            if (bytes == -1) {
                                break
                            }
    
                            outputStream.channel
                            outputStream.write(data, 0, bytes)
                            progressBytes += bytes
    
                            emit(Download.Progress(percent = ((progressBytes * 100) / totalBytes).toInt()))
                        }
    
                        when {
                            progressBytes < totalBytes ->
                                throw Exception("missing bytes")
                            progressBytes > totalBytes ->
                                throw Exception("too many bytes")
                            else ->
                                deleteFile = false
                        }
                    }
                }
    
                emit(Download.Finished(file))
            } finally {
                // check if download was successful
    
                if (deleteFile) {
                    file.delete()
                }
            }
        }
            .flowOn(Dispatchers.IO)
            .distinctUntilChanged()
    
    suspend fun Context.usage() {
        coroutineScope {
            myService.getUrl("https://www.google.com")
                .downloadToFileWithProgress(
                    externalCacheDir!!,
                    "my_file",
                )
                .collect { download ->
                    when (download) {
                        is Download.Progress -> {
                            // update ui with progress
                        }
                        is Download.Finished -> {
                            // update ui with file
                        }
                    }
                }
        }
    }
    
    接口MyService{
    @流//允许将数据直接流式传输到fs,而无需将所有内容保存在ram中
    @得到
    suspend fun getUrl(@Url-Url:String):ResponseBody
    }
    密封类下载{
    数据类进度(val百分比:Int):下载()
    数据类完成(val文件:file):下载()
    }
    fun ResponseBody.downloadToFileWithProgress(目录:文件,文件名:字符串):Flow=
    流动{
    发射(下载进度(0))
    //如果下载错误或被取消,则删除文件的标志
    var deleteFile=true
    val file=file(目录“${filename}.${contentType()?.subtype}”)
    试一试{
    ByTestStream()。使用{inputStream->
    file.outputStream()。使用{outputStream->
    val totalBytes=contentLength()
    val数据=字节数组(8_192)
    var progressBytes=0L
    while(true){
    val bytes=inputStream.read(数据)
    如果(字节==-1){
    打破
    }
    outputStream.channel
    outputStream.write(数据,0,字节)
    progressBytes+=字节
    emit(Download.Progress(百分比=((progressBytes*100)/totalBytes.toInt())
    }
    什么时候{
    progressBytes
    抛出异常(“缺少字节”)
    progressBytes>totalBytes->
    抛出异常(“字节太多”)
    其他->
    deleteFile=false
    }
    }
    }
    emit(下载.Finished(文件))
    }最后{
    //检查下载是否成功
    如果(删除文件){
    file.delete()
    }
    }
    }
    .flowOn(Dispatchers.IO)
    .distinctUntilChanged()
    挂起有趣的上下文。用法(){
    共线镜{
    myService.getUrl(“https://www.google.com")
    .下载至文件,并附上进度(
    外部缓存目录!!,
    “我的文件”,
    )
    .收集{下载->
    何时下载{
    正在下载。进度->{
    //使用进度更新用户界面
    }
    正在下载。完成->{
    //使用文件更新用户界面
    }
    }
    }
    }
    }
    
    这是解决问题的一个很好的解决方案:我已经对它进行了测试,它可以帮助您吗您提供的链接指的是上传文件而不是下载,因此实现方式不同。它不是先加载数据,然后给出输入流吗?因为当我添加了
    HttpLoggingInterceptor
    以进行改造时,我看到所有字节都是首先通过流下载的,并且进度不是实际的下载进度,而是从输入流复制到另一个流。如果我不正确,请告诉我。
    onAttachmentDownloadUpdate()
    之后收到
    
    class FilesDataSource(private val parentFolder: File, private val api: Api) {
    
        suspend fun downloadZip(id: Int, processCallback: (Long, Long) -> Unit): File {
            val response = api.getZip(id).awaitResponse()// returns the response, but it's content will be later
            val body = response.body()
            if (response.isSuccessful && body != null) {
                val file = File(parentFolder, "$id")
                body.byteStream().use { inputStream ->
                    FileOutputStream(file).use { outputStream ->
                        val data = ByteArray(8192)
                        var read: Int
                        var progress = 0L
                        val fileSize = body.contentLength()
                        while (inputStream.read(data).also { read = it } != -1) {
                            outputStream.write(data, 0, read)
                            progress += read
                            publishProgress(processCallback, progress, fileSize)
                        }
                        publishProgress(processCallback, fileSize, fileSize)
                    }
                }
                return file
            } else {
                throw HttpException(response)
            }
        }
    
        private suspend fun publishProgress(
            callback: (Long, Long) -> Unit,
            progress: Long, //bytes
            fileSize: Long  //bytes
        ) {
            withContext(Dispatchers.Main) { // invoke callback in UI thtread
                callback(progress, fileSize)
            }
        }
    }
    
    interface MyService {
        @Streaming // allows streaming data directly to fs without holding all contents in ram
        @GET
        suspend fun getUrl(@Url url: String): ResponseBody
    }
    
    sealed class Download {
        data class Progress(val percent: Int) : Download()
        data class Finished(val file: File) : Download()
    }
    
    fun ResponseBody.downloadToFileWithProgress(directory: File, filename: String): Flow<Download> =
        flow {
            emit(Download.Progress(0))
    
            // flag to delete file if download errors or is cancelled
            var deleteFile = true
            val file = File(directory, "${filename}.${contentType()?.subtype}")
    
            try {
                byteStream().use { inputStream ->
                    file.outputStream().use { outputStream ->
                        val totalBytes = contentLength()
                        val data = ByteArray(8_192)
                        var progressBytes = 0L
    
                        while (true) {
                            val bytes = inputStream.read(data)
    
                            if (bytes == -1) {
                                break
                            }
    
                            outputStream.channel
                            outputStream.write(data, 0, bytes)
                            progressBytes += bytes
    
                            emit(Download.Progress(percent = ((progressBytes * 100) / totalBytes).toInt()))
                        }
    
                        when {
                            progressBytes < totalBytes ->
                                throw Exception("missing bytes")
                            progressBytes > totalBytes ->
                                throw Exception("too many bytes")
                            else ->
                                deleteFile = false
                        }
                    }
                }
    
                emit(Download.Finished(file))
            } finally {
                // check if download was successful
    
                if (deleteFile) {
                    file.delete()
                }
            }
        }
            .flowOn(Dispatchers.IO)
            .distinctUntilChanged()
    
    suspend fun Context.usage() {
        coroutineScope {
            myService.getUrl("https://www.google.com")
                .downloadToFileWithProgress(
                    externalCacheDir!!,
                    "my_file",
                )
                .collect { download ->
                    when (download) {
                        is Download.Progress -> {
                            // update ui with progress
                        }
                        is Download.Finished -> {
                            // update ui with file
                        }
                    }
                }
        }
    }