Android改造下载图片进度通知

时间:2020-02-23 14:29:13  来源:igfitidea点击:

在本教程中,我们将使用Retrofit库以便从URL下载图像。
我们将在通知任务中显示下载进度。
我们将使用来自unsplash.com的图片。

Android改造下载图片

在上一教程中,我们构建了一个android应用程序,该应用程序使用Retrofit下载文件并在ProgressBar中显示进度。

为了构建在后台下载并在通知中显示和更新下载进度的应用程序,我们需要执行以下操作:

  • 创建一个意图服务。

  • 处理其中的下载逻辑和通知创建。

  • 在我们的活动中创建一个广播接收器,以收听来自Intent服务的更新。

  • 下载完成后,将文件保存到磁盘。

  • 在活动中检索文件(在这种情况下为图像)。

  • 使用毕加索将其显示在屏幕上。

项目设置

在您应用的build.gradle文件中添加以下依赖项:

implementation 'com.android.support:design:28.0.0-alpha3'
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.picasso:picasso:2.71828'

在您的AndroidManifest.xml文件中添加以下权限

<uses-permission android:name="android.permission.INTERNET"
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"

Android Retrofit在Notification项目结构中下载图像进度

代码

下面给出了activity_main.xml布局的代码:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  xmlns:tools="https://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:layout_margin="@android:dimen/app_icon_size"
  tools:context=".MainActivity">

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignParentTop="true"
      android:gravity="center"
      android:text="Download Image With Progress In Notifications Using Retrofit" 

  <ImageView
      android:id="@+id/imageView"
      android:layout_width="150dp"
      android:layout_height="150dp"
      android:layout_centerInParent="true"
      app:srcCompat="@mipmap/ic_launcher" 

  <android.support.design.widget.FloatingActionButton
      android:id="@+id/fab"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_below="@+id/imageView"
      android:layout_centerHorizontal="true"
      android:layout_margin="16dp"
      android:src="@android:drawable/stat_sys_download" 

</RelativeLayout>

下面给出了RetrofitInterface.java的代码。

package com.theitroad.androidretrofitprogressnotification;

import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Streaming;
import retrofit2.http.Url;

public interface RetrofitInterface {

  @GET()
  @Streaming
  Call<ResponseBody> downloadImage(@Url String fileUrl);
}

MainActivity.java类的代码如下:

package com.theitroad.androidretrofitprogressnotification;

import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

import com.squareup.picasso.Picasso;

import java.io.File;

public class MainActivity extends AppCompatActivity {

  public static final String PROGRESS_UPDATE = "progress_update";
  private static final int PERMISSION_REQUEST_CODE = 1;
  ImageView imageView;
  FloatingActionButton fab;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      imageView = findViewById(R.id.imageView);
      fab = findViewById(R.id.fab);

      fab.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {
              if (checkPermission()) {
                  startImageDownload();
              } else {
                  requestPermission();
              }
          }
      });

      registerReceiver();
  }

  private void registerReceiver() {

      LocalBroadcastManager bManager = LocalBroadcastManager.getInstance(this);
      IntentFilter intentFilter = new IntentFilter();
      intentFilter.addAction(PROGRESS_UPDATE);
      bManager.registerReceiver(mBroadcastReceiver, intentFilter);

  }

  private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
      @Override
      public void onReceive(Context context, Intent intent) {

          if (intent.getAction().equals(PROGRESS_UPDATE)) {

              boolean downloadComplete = intent.getBooleanExtra("downloadComplete", false);
              //Log.d("API123", download.getProgress() + " current progress");

              if (downloadComplete) {

                  Toast.makeText(getApplicationContext(), "File download completed", Toast.LENGTH_SHORT).show();

                  File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() + File.separator +
                          "theitroad-image-downloaded.jpg");

                  Picasso.get().load(file).into(imageView);

              }
          }
      }
  };

  private boolean checkPermission() {
      int result = ContextCompat.checkSelfPermission(this,
              Manifest.permission.WRITE_EXTERNAL_STORAGE);
      return result == PackageManager.PERMISSION_GRANTED;
  }

  private void startImageDownload() {

      Intent intent = new Intent(this, BackgroundNotificationService.class);
      startService(intent);

  }

  private void requestPermission() {

      ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE);

  }

  @Override
  public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
      switch (requestCode) {
          case PERMISSION_REQUEST_CODE:
              if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                  startImageDownload();
              } else {

                  Toast.makeText(getApplicationContext(), "Permission Denied", Toast.LENGTH_SHORT).show();

              }
              break;
      }
  }
}

在上面的代码中,当单击FloatingActionButton时,我们将启动BackgroundNotificationService.java IntentService。

在此之前,我们检查运行时权限。

我们还创建并注册了一个隐式的BroadcastReceiver。

下载完成后,它将通过Intents向BroadcastReceiver发送一条消息。

下载成功完成后,我们将从内部存储中检索保存的文件,并使用Picasso将其显示在ImageView中。

让我们看一下发生所有下载内容的BackgroundNotificationService.java类:

package com.theitroad.androidretrofitprogressnotification;

import android.app.IntentService;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.os.Environment;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.widget.Toast;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Retrofit;

public class BackgroundNotificationService extends IntentService {

  public BackgroundNotificationService() {
      super("Service");
  }

  private NotificationCompat.Builder notificationBuilder;
  private NotificationManager notificationManager;

  @Override
  protected void onHandleIntent(Intent intent) {

      notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
          NotificationChannel notificationChannel = new NotificationChannel("id", "an", NotificationManager.IMPORTANCE_LOW);

          notificationChannel.setDescription("no sound");
          notificationChannel.setSound(null, null);
          notificationChannel.enableLights(false);
          notificationChannel.setLightColor(Color.BLUE);
          notificationChannel.enableVibration(false);
          notificationManager.createNotificationChannel(notificationChannel);
      }

      notificationBuilder = new NotificationCompat.Builder(this, "id")
              .setSmallIcon(android.R.drawable.stat_sys_download)
              .setContentTitle("Download")
              .setContentText("Downloading Image")
              .setDefaults(0)
              .setAutoCancel(true);
      notificationManager.notify(0, notificationBuilder.build());

      initRetrofit();

  }

  private void initRetrofit() {

      Retrofit retrofit = new Retrofit.Builder()
              .baseUrl("https://unsplash.com/")
              .build();

      RetrofitInterface retrofitInterface = retrofit.create(RetrofitInterface.class);

      Call<ResponseBody> request = retrofitInterface.downloadImage("photos/YYW9shdLIwo/download?force=true");
      try {

          downloadImage(request.execute().body());

      } catch (IOException e) {
          e.printStackTrace();
          Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();

      }
  }

  private void downloadImage(ResponseBody body) throws IOException {

      int count;
      byte data[] = new byte[1024 * 4];
      long fileSize = body.contentLength();
      InputStream inputStream = new BufferedInputStream(body.byteStream(), 1024 * 8);
      File outputFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "theitroad-image-downloaded.jpg");
      OutputStream outputStream = new FileOutputStream(outputFile);
      long total = 0;
      boolean downloadComplete = false;
      //int totalFileSize = (int) (fileSize/(Math.pow(1024, 2)));

      while ((count = inputStream.read(data)) != -1) {

          total += count;
          int progress = (int) ((double) (total * 100)/(double) fileSize);

          updateNotification(progress);
          outputStream.write(data, 0, count);
          downloadComplete = true;
      }
      onDownloadComplete(downloadComplete);
      outputStream.flush();
      outputStream.close();
      inputStream.close();

  }

  private void updateNotification(int currentProgress) {

      notificationBuilder.setProgress(100, currentProgress, false);
      notificationBuilder.setContentText("Downloaded: " + currentProgress + "%");
      notificationManager.notify(0, notificationBuilder.build());
  }

  private void sendProgressUpdate(boolean downloadComplete) {

      Intent intent = new Intent(MainActivity.PROGRESS_UPDATE);
      intent.putExtra("downloadComplete", downloadComplete);
      LocalBroadcastManager.getInstance(BackgroundNotificationService.this).sendBroadcast(intent);
  }

  private void onDownloadComplete(boolean downloadComplete) {
      sendProgressUpdate(downloadComplete);

      notificationManager.cancel(0);
      notificationBuilder.setProgress(0, 0, false);
      notificationBuilder.setContentText("Image Download Complete");
      notificationManager.notify(0, notificationBuilder.build());

  }

  @Override
  public void onTaskRemoved(Intent rootIntent) {
      notificationManager.cancel(0);
  }

}

在onHandleIntent内部,我们先创建Notification,然后再创建Retrofit实例。

在Retrofit调用中,我们进行下载并在Notification上更新进度。
要在Notification中显示ProgressBar,您只需要在Notification Builder实例上调用setProgress。

sendProgressUpdate将更新发送到广播接收器。

从Android Oreo开始,要构建Notifications,我们必须使用NotificationChannels。