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的协同程序的变体
@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
}
}
}
}
}