Android 后台下载功能

Android 后台下载功能

随着Google规范政策的不断完善,后台服务这个东东已经不再被允许了,那么,该如何去实现一个后台下载的功能呢?

注意:这里的后台是指应用进入后台,并非杀掉进程也能下载哈!(DownloadManager除外)

这次的示例将使用3种方式进行:前台服务、WorkManager和DownloadManager

一、前台服务

Android14开始对前台服务有更加严格的要求,具体细节请参考:前台服务类型

实现方式,可通过继承IntentService,Service等启动前台服务

示例:

public class MyIntentService extends IntentService {

private static final String TAG = "MyIntentService";

private static final int DOWNLOAD_NOTIFY_ID = 1000;

/**

* 服务启动

*/

public static void startService(Context context) {

Log.i("测试", "startService:called!");

NotifyUtil.createNotification(context);

Intent intent = new Intent(context, MyIntentService.class);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

context.startForegroundService(intent);

} else {

context.startService(intent);

}

}

/**

* 服务停止

*/

public static void stopService(Context context) {

Intent intent = new Intent(context, MyIntentService.class);

context.stopService(intent);

}

public MyIntentService() {

super("MyIntentService");

}

@Override

public void onCreate() {

super.onCreate();

// 获取服务通知

Notification notification = NotifyUtil.getNotification();

//将服务置于启动状态 ,NOTIFICATION_ID指的是创建的通知的ID

startForeground(DOWNLOAD_NOTIFY_ID, notification);

}

@Override

protected void onHandleIntent(Intent intent) {

Log.i("测试", "onHandleIntent:called!");

//如果想要实现同步队列,可以使用ExecutorService executorService = Executors.newSingleThreadExecutor();

}

}

耗时较长和复杂的逻辑可以在自带线程的onHandleIntent中处理

AndroidManifest.xml清单文件中加入前台服务的配置

其它网络请求权限

注意: 1.前台服务需要绑定一个Notification,而Notification有三个值是一定要设置的,不然就会报错

/**

* 注意:

* 在通知栏显示下载进度,以下三个属性值需要设置

* 否则会直接抛出异常

*/

builder.setContentTitle("Downloading...");

builder.setContentText("0%");

builder.setSmallIcon(id);

2.在Android13开始,通知权限需要动态申请了,否则接收不到通知,也就是说在通知栏显示进度的也无法显示,所以,记得申请通知权限

//动态申请的代码请自行百度

二、WorkManager

WorkManager是Google推荐使用的,它也可以实现后台下载,大概的原理是:类似于定时轮询检测的一个工作任务,每隔一段时间就会轮询检测一次工作任务是否还在进行,因此startWork是会存在多次回调的问题!

Google对WorkManager的介绍:WorkManager 调度任务

示例:

public class DownloadWorker extends ListenableWorker {

private static final String TAG = DownloadWorker.class.getSimpleName();

public DownloadWorker(@NonNull Context context, @NonNull WorkerParameters params) {

super(context, params);

}

@SuppressLint("RestrictedApi")

@NonNull

@Override

public ListenableFuture startWork() {

Log.i("测试", "startWork is called!");

WorkCallback callback = ConfigUtil.getInstance().getWorkCallback();

if (callback == null) {

SettableFuture future = SettableFuture.create();

future.set(Result.failure());

return future;

}

return callback.doWork();

}

@Override

public void onStopped() {

super.onStopped();

Log.i("测试", "onStopped is called!");

WorkCallback callback = ConfigUtil.getInstance().getWorkCallback();

if (callback != null) {

callback.onStopped();

}

}

}

注意: 建议直接继承ListenableWorker,上面说到startWork是会存在多次回调的问题,继承ListenableWorker 可以通过SettableFuture去设置成功或失败,失败后不会重复轮询去检查调用;但是如果继承的是Worker,当回调失败时,Worker会定时去轮询这个失败的任务,直到成功为止,这里如果逻辑处理不当,就有可能出现多个任务同时下载同一个资源的问题!

其它方法调用

//启动工作任务

private static void enqueue(Context context) {

Log.i("测试", "enqueue is called!");

// 创建网络状态变化约束条件,断网时自动停止,重新链接网络时,自动继续下载

Constraints constraints = new Constraints.Builder()

.setRequiredNetworkType(NetworkType.CONNECTED)//网络连接

.setRequiresBatteryNotLow(false)//设备电量低于指定阈值时仍会执

.setRequiresCharging(false)//设备不在充电时也会执行

.build();

OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(DownloadWorker.class)

.setConstraints(constraints)

.build();

WorkManager.getInstance(context).enqueue(request);

}

/**

* 停止所有 Worker

*

* @param context 上下文

*/

public static void stopWorker(Context context) {

Log.i(TAG, "stopWorker is called!");

if (context == null) {

Log.w(TAG, "stopWorker context == null!");

return;

}

WorkManager.getInstance(context).cancelAllWork();

}

/**

* 停止某个Worker

*

* @param context 上下文

* @param workId 获取 Worker 的 ID

*/

public static void stopWorker(Context context, String workId) {

Log.i(TAG, "stopWorker is called!");

if (context == null || TextUtils.isEmpty(workId)) {

Log.w(TAG, "stopWorker context == null or workId isEmpty!");

return;

}

WorkManager.getInstance(context).cancelWorkById(UUID.fromString(workId));

}

三、DownloadManager

系统自带的下载器,这玩意有点秀,是真的后台下载,杀掉进程也还会继续下载,直到资源下载完成,或调用了停止方法remove()

注意: 1.调用remove()方法不会立刻停止下载,会把当前资源下载完成才会停止,在某些版本上可能需要全部下载完成才能停止,蛋疼!! 2.不能暂停,接口已不能调用了!

示例:

public class MyDownloadManager {

private static DownloadManager downloadManager;

private static long downloadId;

public static void download(Context context) {

String url = "https://********";

DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));

request.setTitle("Download"); // 设置通知标题

request.setDescription("Downloading resource"); // 设置通知描述

request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); // 设置下载完成后显示通知

// 设置下载文件的保存位置

File file = new File(context.getExternalFilesDir(null), "aa.apk");

request.setDestinationUri(Uri.parse("file://" + file.getPath()));

downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);

downloadId = downloadManager.enqueue(request);

}

public static void stop() {

downloadManager.remove(downloadId);

}

示例图:

通知栏:

那么示例已经完成了,可以结合需求,自行添加逻辑,数据库等进行封装使用,根据需求各取所需哈!!感谢~~

完整demo地址:Android 后台下载功能demo

相关推荐

合作伙伴