Android 后台下载功能

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

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

这次的示例将使用3种方式进行:前台服务WorkManagerDownloadManager

一、前台服务

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清单文件中加入前台服务的配置

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<service android:name=".manager.MyIntentService" android:exported="false" />

其它网络请求权限

	<uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

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

		/**
         * 注意:
         * 在通知栏显示下载进度,以下三个属性值需要设置
         * 否则会直接抛出异常
         */
        builder.setContentTitle("Downloading...");
        builder.setContentText("0%");
        builder.setSmallIcon(id);

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

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

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

二、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<Result> startWork() {
        Log.i("测试", "startWork is called!");

        WorkCallback callback = ConfigUtil.getInstance().getWorkCallback();
        if (callback == null) {
            SettableFuture<Result> 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值