Android异步下载
概述
实现App常见下载公共 支持通知栏显示 支持 暂停、取消功能,使用Service、AsyncTask实现异步下载。特点简单、实用、方便源码扩展修改
详细
一、准备工作
1、项目运行环境AndroidStudio
3、通过Service、AsyncTask实现异步下载,并通过通知栏显示下载进度。
二、程序实现
1、工程目录
2、在AsyncTask中实现下载操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | public DownloadTask(DownloadListener listener) { this .listener = listener; } /** * 执行具体下载逻辑 * @param strings * @return 下载状态 */ @Override protected Integer doInBackground(String... strings) { InputStream is = null ; RandomAccessFile savedFile = null ; File file = null ; try { long downloadedLength = 0 ; //记录已下载文件的长度 String downloadUrl = strings[ 0 ]; //获取下载地址 String fileName = downloadUrl.substring(downloadUrl.lastIndexOf( "/" )); String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath(); file = new File(directory + fileName); //将文件指定下载到SD卡的Download目录下 if (file.exists()) { //判断是否已存在文件,是:取出文件大小(字节数) downloadedLength = file.length(); } long contentLength = getContentLength(downloadUrl); //获取待下载文件大小(字节数) if (contentLength == 0 ) { //长度为0,文件异常,下载失败 return TYPE_FAILED; } else if (contentLength == downloadedLength) { //文件长度等于已下载长度,已下载完,直接返回成功 return TYPE_SUCCESS; } OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .addHeader( "RANGE" , "bytes=" + downloadedLength + "-" ) //断点下载,指定从哪个字节开始下载 .url(downloadUrl) .build(); Response response = client.newCall(request).execute(); if (response != null ) { is = response.body().byteStream(); savedFile = new RandomAccessFile(file, "rw" ); savedFile.seek(downloadedLength); //跳过已下载字节 byte [] bytes = new byte [ 1024 ]; int total = 0 ; int len; while ((len = is.read(bytes)) != - 1 ) { if (isCanceled) { //判断是否有取消操作 return TYPE_CANCELED; } else if (isPaused) { //判断是否有暂停操作 return TYPE_PAUSED; } else { total += len; savedFile.write(bytes, 0 , len); //计算已下载百分比 int progress = ( int ) ((total + downloadedLength) * 100 / contentLength); publishProgress(progress); } } } } catch (Exception e) { e.printStackTrace(); } finally { try { } catch (Exception e) { e.printStackTrace(); } } return TYPE_FAILED; } /** * 更新下载进度 * @param values */ @Override protected void onProgressUpdate(Integer... values) { int progress = values[ 0 ]; if (progress > lastProgress) { listener.onProgress(progress); lastProgress = progress; } } /** * 通知下载结果 * @param type */ @Override protected void onPostExecute(Integer type) { switch (type) { case TYPE_SUCCESS: listener.onSuccess(); break ; case TYPE_FAILED: listener.onFailed(); break ; case TYPE_PAUSED: listener.onPaused(); break ; case TYPE_CANCELED: listener.onCanceled(); break ; default : break ; } } public void pauseDownload() { isPaused = true ; } public void cancelDownload() { isCanceled = true ; } private long getContentLength(String downloadUrl) throws IOException { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url(downloadUrl).build(); Response response = client.newCall(request).execute(); if (response != null && response.isSuccessful()) { long contentLength = response.body().contentLength(); response.body().close(); return contentLength; } return 0 ; } |
3、使用interface观察者模式监听下载动作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public interface DownloadListener { //当前下载进度 void onProgress( int progress); //下载成功 void onSuccess(); //下载失败 void onFailed(); //下载暂停 void onPaused(); //下载取消 void onCanceled(); } |
4、在Service中初始化AsyncTask并实例化监听
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | private DownloadTask downloadTask; private String downloadUrl; private DownloadListener downloadListener = new DownloadListener() { //创建Download实例 @Override public void onProgress( int progress) { //构建显示下载进度的通知,并触发通知 getNotificationManager().notify( 1 , getNotification( "Downloading ..." ,progress)); } @Override public void onSuccess() { downloadTask = null ; //下载成功将前台服务关闭,并创建一个下载成功的通知 stopForeground( true ); getNotificationManager().notify( 1 , getNotification( "Download Success" ,- 1 )); Toast.makeText(DownloadService. this , "Download Success" ,Toast.LENGTH_SHORT).show(); } @Override public void onFailed() { downloadTask = null ; //下载失败将前台服务关闭,并创建一个下载失败的通知 stopForeground( true ); getNotificationManager().notify( 1 , getNotification( "Download Failed" ,- 1 )); Toast.makeText(DownloadService. this , "Download Failed" ,Toast.LENGTH_SHORT).show(); } @Override public void onPaused() { downloadTask = null ; Toast.makeText(DownloadService. this , "Download Paused" ,Toast.LENGTH_SHORT).show(); } @Override public void onCanceled() { downloadTask = null ; stopForeground( true ); Toast.makeText(DownloadService. this , "Download Canceled" ,Toast.LENGTH_SHORT).show(); } }; |
5、创建下载进度通知栏
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | private Notification getNotification(String title, int progress) { Intent intent = new Intent( this , MainActivity. class ); PendingIntent pi = PendingIntent.getActivity( this , 0 , intent, 0 ); NotificationCompat.Builder builder = new NotificationCompat.Builder( this ); builder.setSmallIcon(R.mipmap.ic_launcher_round); builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)); builder.setContentIntent(pi); builder.setContentTitle(title); if (progress >= 0 ) { //当progress大于或等0时才需要显示下载进度 builder.setContentText(progress + "%" ); builder.setProgress( 100 , progress, false ); } return builder.build(); } |
6.创见DownloadBinder类,与外部建立连接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | private DownloadBinder mBinder = new DownloadBinder(); @Override public IBinder onBind(Intent intent) { return mBinder; } class DownloadBinder extends Binder{ public void startDownload(String url){ if (downloadTask == null ){ downloadUrl = url; downloadTask = new DownloadTask(downloadListener); downloadTask.execute(downloadUrl); startForeground( 1 ,getNotification( "Downloading ... " , 0 )); Toast.makeText(DownloadService. this , "Downloading ... " ,Toast.LENGTH_SHORT).show(); } } public void pauseDownload(){ if (downloadTask != null ){ downloadTask.pauseDownload(); } else { if (downloadUrl != null ){ //取消下载是删除文件,关闭通知 String fileName = downloadUrl.substring(downloadUrl.lastIndexOf( "/" )); String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath(); File file = new File(directory + fileName); if (file.exists()){ file.delete(); } getNotificationManager().cancel( 1 ); stopForeground( true ); Toast.makeText(DownloadService. this , "Canceled" ,Toast.LENGTH_SHORT).show(); } } } public void cancelDownload(){ if (downloadTask != null ){ downloadTask.cancelDownload(); } } } |
7、绑定Service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | private DownloadService.DownloadBinder downloadBinder; private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { downloadBinder = (DownloadService.DownloadBinder) iBinder; } @Override public void onServiceDisconnected(ComponentName componentName) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent( this ,DownloadService. class ); startService(intent); //启动服务 bindService(intent,serviceConnection,BIND_AUTO_CREATE); //绑定服务 if (ContextCompat.checkSelfPermission(MainActivity. this , Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){ //获取读写权限 ActivityCompat.requestPermissions(MainActivity. this , new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1 ); } } |
三、运行效果
1、调用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public void startDownload(View view) { if (downloadBinder == null ){ return ; } String url = "your URL (你的下载路径)" ; downloadBinder.startDownload(url); } public void pauseDownload(View view) { if (downloadBinder == null ){ return ; } downloadBinder.pauseDownload(); } public void cancelDownload(View view) { if (downloadBinder == null ){ return ; } downloadBinder.cancelDownload(); } |
3、运行时的截图
四、其他补充
1、如何修改通知栏样式?
1 2 3 4 5 | NotificationCompat.Builder builder = new NotificationCompat.Builder( this ); builder.setSmallIcon(R.mipmap.ic_launcher_round); builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)); builder.setContentIntent(pi); builder.setContentTitle(title); |
2、不了解Service、AsyncTask?
Android Service(服务)详解·(一)相关知识点引入
Android Service(服务)详解·(二)Service基本用法
3、还有疑问?
请联系我 留言 或者 E-mail : duyangs1994@gmail.com
一起学习,一起进步
注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?