DownloadManager下载Apk使用详解

仉俊能
2023-12-01

 

  DownloadManager使用比较简单,至于版本大小判断就不说了,直接就贴代码,需要更新执行下面代码就可以了

DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(newApkDownloadUrl));
//设置Notification的标题和描述
request.setTitle("应用名");
request.setDescription("新版本3.5.2下载中...");
//设置漫游状态下是否可以下载
request.setAllowedOverRoaming(false);
request.setVisibleInDownloadsUi(true);
//设置Notification的显示,和隐藏。
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setMimeType("application/vnd.android.package-archive");
request.setDestinationUri(Uri.fromFile(new File(apk_filepath)));
downloadId = downloadManager.enqueue(request);

上面newApkDownloadUrl 就是服务器Apk地址,apk_filepath就是手机下载之后存放路径。

建议存储在App缓存路径内,不建议存储在外部Sd卡,外部需要读写权限,并且存App缓存路径有个好处,就是当卸载App后路径下的Apk也会被清理掉;还有需要兼容7.0之后路径问题,简单贴代码,具体适配请了解7.0特性

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
   downloadUri = FileProvider.getUriForFile(getContext(),  getContext().getPackageName()+".fileprovider", new File(apk_filepath));
}else{
   downloadUri = Uri.fromFile(new File(apk_filepath));
}

据说部分手机对DownloadManager进行了阉割,可能用不了,所以下载前先判断功能是否可用,贴代码

/**
 * 系统的下载组件是否可用
 *
 * @return boolean
 */
public static boolean checkDownloadState(Context context) {
   try {
      int state = context.getPackageManager().getApplicationEnabledSetting("com.android.providers.downloads");
      if (state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
            || state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER
            || state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
         return false;
      }
   } catch (Exception e) {
      e.printStackTrace();
      return false;
   }
   return true;
}

如果可用就运行下载代码,如果不可用则把apkDownloadUrl丢给浏览器下载

/**
 * 跳转浏览器下载
 */
private void openBrowser(){
   Intent intent = new Intent(Intent.ACTION_VIEW);//使用浏览器下载
   intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   intent.setData(Uri.parse(newApkDownloadUrl));
   mContext.get().startActivity(intent);
}

 重复下载或者下载失败问题,如果正在下载用户重复点击了下载怎么办?或者下载已经完成了用户又点击下载等问题,所以进行下载前需要做判断,判断安装包状态,贴代码:

//获取存储的下载ID
long downloadId = SPCache.getObject(mContext.get(),DownloadManager.EXTRA_DOWNLOAD_ID,Long.class,-1L);
if(downloadId != -1) {
   //获取当前状态
   int status = getDownloadStatus(downloadId);

   switch (status) {
      //下载延迟
      case DownloadManager.STATUS_PENDING:
      //正在下载
      case DownloadManager.STATUS_RUNNING:
         Log.d("DownloadHelper", "apk is already downloading");
         if(!isDownloading(newApkDownloadUrl)){
            startDownload(zhsApplication);
         }
         break;
      //下载完成
      case DownloadManager.STATUS_SUCCESSFUL:
         //状态为下载成功
         //获取下载路径URI
         Uri downloadUri = null;
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            downloadUri = FileProvider.getUriForFile(ZHSApplication.getContext(),  ZHSApplication.getContext().getPackageName()+".fileprovider", new File(apk_filepath));
         }else{
            downloadUri = Uri.fromFile(new File(apk_filepath));
         }

         if(null != downloadUri) {
            //存在下载的APK,如果两个APK相同,启动更新界面。否之则删除,重新下载。
            if(compare(getApkInfo(mContext.get(),apk_filepath),mContext.get())) {
               installAPK();
            } else {
               //删除下载任务以及文件
               downloadManager.remove(downloadId);
               startDownload(zhsApplication);
            }
         }else{
            startDownload(zhsApplication);
         }
         break;
      default:
         //取消或者没有记录//下载失败,重新下载
         startDownload(zhsApplication);
         break;
   }
} else {
   //不存在downloadId,没有下载过APK
   startDownload(zhsApplication);
}

这个就是下载前的判断,SPCache就是一个本地保存信息的一个工具,当请求

downloadManager.enqueue(request) 时保存一下downloadId,方便获取下载状态。

其次是下载完成监听,请求完下载就开始注册BroadcastReceiver进行监听

new BroadcastReceiver() {
   @Override
   public void onReceive(Context context, Intent intent) {
      if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
         checkStatus();
      }
   }
};

下载完成后判断下载状态进行安装就可以了

private void installAPK() {
   setPermission(apk_filepath);
   Intent intent = new Intent(Intent.ACTION_VIEW);
   // 由于没有在Activity环境下启动Activity,设置下面的标签
   intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   //Android 7.0以上要使用FileProvider
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
      File file = new File(apk_filepath);
      //参数1 上下文, 参数2 Provider主机地址 和配置文件中保持一致   参数3  共享的文件
      Uri apkUri = FileProvider.getUriForFile(getContext(),  getContext().getPackageName()+".fileprovider", file);
      //添加这一句表示对目标应用临时授权该Uri所代表的文件
      intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
      intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
   } else {
      intent.setDataAndType(Uri.fromFile(new File(apk_filepath)), "application/vnd.android.package-archive");
   }
   mContext.get().startActivity(intent);
}

当然还可以进行下载进度监听

/**
 * 通过query查询下载状态,包括已下载数据大小,总大小,下载状态
 *
 * @param downloadId
 * @return
 */
private int[] getBytesAndStatus(long downloadId) {
   int[] bytesAndStatus = new int[]{
         -1, -1, 0
   };
   DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
   Cursor cursor = null;
   try {
      cursor = downloadManager.query(query);
      if (cursor != null && cursor.moveToFirst()) {
         //已经下载文件大小
         bytesAndStatus[0] = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
         //下载文件的总大小
         bytesAndStatus[1] = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
         //下载状态
         bytesAndStatus[2] = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
      }
   } finally {
      if (cursor != null) {
         cursor.close();
      }
   }
   return bytesAndStatus;
}

使用

ContentObserver就行监听
/**
      * 监听下载进度
      */
 private class DownloadChangeObserver extends ContentObserver {
   /**
    * Creates a content observer.
    *
    * @param handler The handler to run {@link #onChange} on, or null if none.
    */
   public DownloadChangeObserver(Handler handler) {
      super(handler);
      scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
   }
   /**
             * 当所监听的Uri发生改变时,就会回调此方法
             *
             * @param selfChange 此值意义不大, 一般情况下该回调值false
             */
   @Override
   public void onChange(boolean selfChange) {
      scheduledExecutorService.scheduleAtFixedRate(progressRunnable,0,2,TimeUnit.SECONDS);
   }
}
/**
 * 注册ContentObserver
 */
private void registerContentObserver() {
   /** observer download change **/
   if (downloadObserver != null) {
      mContext.get().getContentResolver().registerContentObserver(Uri.parse("content://downloads/my_downloads"), false, downloadObserver);
   }
}

/**
 * 注销ContentObserver
 */
private void unregisterContentObserver() {
   if (downloadObserver != null) {
      mContext.get().getContentResolver().unregisterContentObserver(downloadObserver);
   }
}
使用scheduledExecutorService进行定时任务刷新
/**
 * 关闭定时器,线程等操作
 */
private void close() {
   if (scheduledExecutorService != null && !scheduledExecutorService.isShutdown()) {
      scheduledExecutorService.shutdown();
   }

   if (downLoadHandler != null) {
      downLoadHandler.removeCallbacksAndMessages(null);
   }
}

重点代码已经贴上

 

 类似资料: