使用Service进行后台下载
来源:互联网 发布:知乎 个性签名 编辑:程序博客网 时间:2024/05/16 12:51
今天学习service一章,现在总结其中知识点并通过实例进行分析:
一、在学习service之前首先讲解了关于多线程的知识,因为service在主线程中开启,但往往自身会开启线程进行UI等其他操作。
多线程调用方法不在多说,调用Runnable接口实现run方法即可。andriod中关于多线程的问题主要包括UI操作这部分,UI操作不可在子线程中进行,需要用特殊的处理方法。
1.使用Handler
大致使用方式:
首先声明handler实例并重写handleMessage方法进行message处理,这里的处理其实都在主线程中。
private Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { switch(msg.what) { case 1: //这里进行UI操作... } } };
然后在子线程中设置message并调用sendMessage方法将message传递过去。
Runnable runnable=new Runnable() { @Override public void run() { ...... Message message=new Message(); message.what=1; handler.sendMessage(message); } };
其实这种异步消息处理机制有四个部分:Message、Handler、MessageQueue、Looper。
具体机制如下:
2.使用AsyncTask
也是基于异步消息处理机制,只不过是安卓封装的一个类。
基本方法:
onPreEexcute():初始化
doInBackground(Params…):处理耗时逻辑,比如文件读写。可以通过publishProgress(progress)方法传递进度。
onProgressUpdate(Progress…):传递的进度值会传入到这里,这里根据进度值变化做出相关操作。比如UI的更新。
onPostExecute():doInBackground方法返回时调用,进行收尾操作,包括UI处理,关闭前台服务等。
3.使用runOnUiThread
机制相同,不做过多说明。
二、 Service学习主要通过下载文件的实例来总结
如何实现给定下载地址,将文件下载到手机指定路径中:
Layout布局:三个按钮,分别执行开始下载,暂停,取消操作
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:text="START" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/start_download" /> <Button android:text="PAUSE" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/pause_download" /> <Button android:text="CANCEL" android:id="@+id/cancel_download" android:layout_width="match_parent" android:layout_height="wrap_content" /></LinearLayout>
MainActivity代码:定义downloadBinder和serviceConnection,创建活动和服务绑定的intent,当调用bindService绑定成功时,downloadBinder可从onServiceConnected方法中向下转型实例化。然后可通过单击按钮监听事件调用downloadBinder的方法进行下载的各种操作。
DownloadBinder类是在使用的自定义Service类里头定义的。
import android.Manifest;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.content.pm.PackageManager;import android.os.IBinder;import android.support.annotation.NonNull;import android.support.v4.app.ActivityCompat;import android.support.v4.content.ContextCompat;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.Toast;public class MainActivity extends AppCompatActivity implements View.OnClickListener { private DownloadService.DownloadBinder downloadBinder; private ServiceConnection serviceConnection=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { downloadBinder= (DownloadService.DownloadBinder) service; } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button startDownload= (Button) findViewById(R.id.start_download); Button pauseDownload= (Button) findViewById(R.id.pause_download); Button cancelDownload= (Button) findViewById(R.id.cancel_download); startDownload.setOnClickListener(this); pauseDownload.setOnClickListener(this); cancelDownload.setOnClickListener(this); 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); }//申请读写权限权限以保存下载文件 } @Override public void onClick(View v) {//通过downloadBinder的相关方法进行下载操作 switch (v.getId()) { case R.id.start_download: String url="https://raw.githubusercontent.com/guolindev/eclipse/master/eclipse-inst-win64.exe"; downloadBinder.startDownload(url); break; case R.id.pause_download: downloadBinder.pauseDownload(); break; case R.id.cancel_download: downloadBinder.cancelDownload(); break; default: break; } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {//请求权限结果操作 switch (requestCode) { case 1: if (grantResults.length>0&&grantResults[0]!=PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "permission denied", Toast.LENGTH_SHORT).show(); finish(); } break; default: break; } } @Override protected void onDestroy() {//活动退出时取消绑定 super.onDestroy(); unbindService(serviceConnection); }}
那么自定义Service做了什么事情?
基本就是两点:
1.定义downloadBinder内部类并在onBind中返回实例。
这个downloadBinder继承于Binder类,主要内容是调用downloadTask来处理下载的操作,为何要用downloadTask?因为downloadTask是继承于AsyncTask,可以异步处理UI操作和下载操作,并回调下载监听接口downloadListener的方法。downloadBinder只是负责阐明服务所做的事情,而downloadTask用于具体实现。
2.设置下载的监听接口downloadListener
downloadTask回调的接口downListener在这里定义,用于通知下载情况。
import android.annotation.SuppressLint;import android.app.Notification;import android.app.NotificationManager;import android.app.PendingIntent;import android.app.Service;import android.content.Intent;import android.graphics.BitmapFactory;import android.os.Binder;import android.os.Environment;import android.os.IBinder;import android.support.annotation.Nullable;import android.support.v4.app.NotificationCompat;import android.widget.Toast;import java.io.File;/** * Created by 邓苏桃 on 2017/7/9. */public class DownloadService extends Service { private DownloadTask downloadTask; private String downloadUrl; private DownloadBinder mBinder=new DownloadBinder(); private DownloadListener listener=new DownloadListener() {//下载的监听接口 @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() { stopForeground(true); getNotificationManager().notify(1,getNotification("Download Failed",-1)); Toast.makeText(DownloadService.this, "Download Failed", Toast.LENGTH_SHORT).show(); } @Override public void onPause() { 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(); } }; private Notification getNotification(String title, int progress) {//用于创建通知,显示下载进度和成功与否等信息,通知的创建基本就是以下步骤 Intent intent=new Intent(this,MainActivity.class); PendingIntent pending=PendingIntent.getActivity(this,0,intent,0); NotificationCompat.Builder builder=new NotificationCompat.Builder(this); builder.setSmallIcon(R.mipmap.ic_launcher); builder.setContentIntent(pending); builder.setContentInfo(title); builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher)); if (progress>0) { builder.setContentText(progress+"%"); builder.setProgress(100,progress,false); } return builder.build(); } @SuppressLint("ServiceCast") private NotificationManager getNotificationManager() {//获取系统NotificationManager return (NotificationManager) getSystemService(NOTIFICATION_SERVICE); } @Nullable @Override public IBinder onBind(Intent intent) { return mBinder; } public class DownloadBinder extends Binder{//DownloadBinder定义,用于调用downloadTask执行下载操作并在通知栏显示进度和情况 public void startDownload(String url) { if (downloadTask==null) { downloadUrl=url; downloadTask=new DownloadTask(listener); 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(); } } public void cancelDownload(){ if (downloadTask!=null) { downloadTask.cancelDownload(); } else { if (downloadUrl!=null) { String fileName=downloadUrl.substring(downloadUrl.lastIndexOf("/")); String directory= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath(); File file=new File(fileName+directory); if (file.exists()) { file.delete(); } getNotificationManager().cancel(1); stopForeground(true); Toast.makeText(DownloadService.this, "Canceled", Toast.LENGTH_SHORT).show(); } } } }}
接着就是DownloadTask的具体内容:
构造方法里引入监听接口downloadListener以便调用。
然后在doInBackground中实现下载的具体操作:首先设定下载的文件名和路径,文件名从函数参数中获得,这个参数其实是在上边调用downloadTask.execute(String)中传来的。然后判断文件大小以及是否下载完毕,来确定接下来的下载位置或者直接返回,通过Okhttp返回的数据进行下载,每次将数据存入本地前都会判断是否暂停或者取消,如果中途暂停或取消会直接返回相应结果。并将下载进度publish。
在onProgressUpdate以及onPostExecute中更新通知栏进度和Toast。
import android.os.AsyncTask;import android.os.Environment;import android.os.Handler;import android.os.Message;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.RandomAccessFile;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.Response;/** * Created by 邓苏桃 on 2017/7/8. */public class DownloadTask extends AsyncTask<String,Integer,Integer> { public static final int TYPE_SUCCESS=0; public static final int TYPE_FAILED=1; public static final int TYPE_PAUSED=2; public static final int TYPE_CANCELED=3; private DownloadListener listener; private boolean is_canceled=false; private boolean is_paused=false; private int lastProgress; public DownloadTask(DownloadListener listener){ this.listener=listener; } @Override protected Integer doInBackground(String... params) { InputStream is=null; RandomAccessFile savedFile=null; File file=null; try { long downloadedLength=0; String downloadUrl=params[0]; String fileName=downloadUrl.substring(downloadUrl.lastIndexOf("/")); String directory= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath(); file=new File(directory+fileName); if (file.exists()) { downloadedLength=file.length(); } long contentLength=getContentLength(downloadUrl); if (contentLength==0) { return TYPE_FAILED; } else if (contentLength==downloadedLength) { return TYPE_SUCCESS; } OkHttpClient okhttpclient=new OkHttpClient(); Request request=new Request.Builder() .addHeader("RANGE","bytes="+downloadedLength+"-") .url(downloadUrl) .build(); Response response=okhttpclient.newCall(request).execute(); if (response!=null) { is=response.body().byteStream(); savedFile=new RandomAccessFile(file,"rw"); savedFile.seek(downloadedLength); byte[] b=new byte[1024]; int total=0; int len; while((len=is.read(b))!=-1) { if (is_canceled){ return TYPE_CANCELED; } else if (is_paused){ return TYPE_PAUSED; } else { total+=len; savedFile.write(b,0,len); int progress=(int) ((total+downloadedLength)*100/contentLength); publishProgress(progress); } } response.body().close(); return TYPE_SUCCESS; } } catch (IOException e) { e.printStackTrace(); } finally { try{ if (is!=null) { is.close(); } if (savedFile!=null) { savedFile.close(); } if(is_canceled&&file!=null) { file.delete(); } } catch (IOException e) { e.printStackTrace(); } } return TYPE_FAILED; } @Override protected void onProgressUpdate(Integer... values) { int progress=values[0]; if(progress>lastProgress) { listener.onProgress(progress); lastProgress=progress; } } @Override protected void onPostExecute(Integer status) { switch (status) { case TYPE_SUCCESS: listener.onSuccess(); break; case TYPE_FAILED: listener.onFailed(); break; case TYPE_PAUSED: listener.onPause(); break; case TYPE_CANCELED: listener.onCanceled(); break; default: break; } } public void pauseDownload(){ is_paused=true; } public void cancelDownload(){ is_canceled=true; } public long getContentLength(String downloadUrl) { OkHttpClient client=new OkHttpClient(); Request request=new Request.Builder() .url(downloadUrl) .build(); try { Response response=client.newCall(request).execute(); if (response!=null&&response.isSuccessful()) { long contentLength=response.body().contentLength(); response.close(); return contentLength; } } catch (IOException e) { e.printStackTrace(); } return 0; }}
DownloadListener接口:
public interface DownloadListener { void onProgress(int progress); void onSuccess(); void onFailed(); void onPause(); void onCanceled();}
以上是Service的基本实例,其次还有它生命周期的相关知识,简单来说,startService会调用onCreate(仅未创建服务是调用)和onStartConmand方法,stopService会调用onDestory方法,其他bindService也会牵扯到,目前碰到问题较少,暂不细说。
另外关于IntentService,它是Service的一个封装,会自己开启线程并在最后服务,以后用到再细聊。
- 使用Service进行后台下载
- Android 后台Service下载
- service 后台下载
- android Service实现后台下载
- Android通过后台service下载
- 后台Service简单使用
- iOS使用NSURLSession进行下载(包括后台下载,断点下载)
- Android实现Service后台下载Notification进度条
- Android实现Service后台下载Notification进度条
- Android实现Service后台下载Notification进度条
- HttpURLConnection实现文件下载,Service加Notification实现后台下载
- 后台进行数据更新和下载
- 后台进行数据更新和下载
- iOS后台进行数据更新和下载
- android--利用Service进行文件下载
- 使用Downloadmanager进行下载
- Service的使用:断点下载
- Service使用 一直运行的后台服务
- MySql从一窍不通到入门(一)基本概念梳理
- PYTHON数据分析之分组计算
- WebStorm开发工具设置React Native智能提示
- 服务器Apache配置以及改变工作目录
- Eclipse中的控制台
- 使用Service进行后台下载
- AbstractQueuedSynchronizer 原理分析
- 安装php+apache+mysql
- Linux Socket编程(不限Linux)
- OpenStack多节点安装(五):Neutron
- 编码/字符集(UTF-8,UTF-16,UTF-32)的简单介绍
- 【CSDN如何创建博客专栏?】
- 使用eclipse启动服务时,程序的发布目录
- 0710C++基本数据类型之间的转换