先看一下效果
首先写一个监听器用来处理返回结果
public interface DownloadListener {
void onProgress(int progreess);
void onPuase();
void onCancel();
void onFailed();
void onSuccess();
}
然后写一个异步任务来下载,在异步任务中传入DownloadListener 来处理下载的情况。使用异步任务的目的是为了能根据返回值方便的处理监听。
package down;
import android.os.AsyncTask;
import android.os.Environment;
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 yuanteng on 2017/6/30.
*/
public class DownLoadTask extends AsyncTask<String,Integer,Integer> {
public static final int TYPE_SUCESS = 0;
public static final int TYPE_FAILED = 1;
public static final int TYPE_PAUSE = 2;
public static final int TYPE_CANCEL = 3;
private boolean isCancel = false;
private boolean isPause = false;
private DownloadListener listener;
private int lastProgress;
public DownLoadTask(DownloadListener listener){
this.listener = listener;
}
@Override
protected Integer doInBackground(String... strings) {
InputStream inputStream = null;
//随机读取文件类可以指定文件从哪个地方开始写入,所以不用OutputStream
RandomAccessFile saveFile = null;
long downLoadLength = 0;//记录已经下载下的长度
File file = null;
try {
String downUrl = strings[0];
String fileName = downUrl.substring(downUrl.lastIndexOf("/"));
String dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
file = new File(dir + fileName);
if (file.exists()){
downLoadLength = file.length();
}
long contentLength = getContentLength(downUrl);
if (contentLength == 0){
return TYPE_FAILED;
}
if (contentLength == downLoadLength){
return TYPE_SUCESS;
}
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
//断点下载
.addHeader("RANGE","bytes="+downLoadLength+"-")
.url(downUrl)
.build();
Response response = client.newCall(request).execute();
if (response != null && response.isSuccessful()){
inputStream = response.body().byteStream();
saveFile = new RandomAccessFile(file,"rw");
//断点续传
saveFile.seek(downLoadLength);
byte[] b = new byte[4096];
int total = 0;
int len ;
while ((len = inputStream.read(b)) != -1){
if (isCancel){
return TYPE_CANCEL;
}
else if (isPause){
return TYPE_PAUSE;
}
else {
total += len;
saveFile.write(b,0,len);
int progress = (int)((total + downLoadLength)*100/contentLength);
publishProgress(progress);
}
}
response.body().close();
return TYPE_SUCESS;
}
}
catch (Exception e){
e.printStackTrace();
}
finally {
try {
if (inputStream != null){
inputStream.close();
}
if (saveFile != null){
saveFile.close();
}
if (isCancel && file != null){
file.delete();
}
}
catch (IOException e){
e.printStackTrace();
}
}
return TYPE_FAILED;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
int progress = values[0];
if (progress > lastProgress){
listener.onProgress(progress);
lastProgress = progress;
}
}
/*根据不同的返回值进行处理*/
@Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
switch (integer){
case TYPE_SUCESS:
listener.onSuccess();
break;
case TYPE_CANCEL:
listener.onCancel();
break;
case TYPE_PAUSE:
listener.onPuase();
break;
case TYPE_FAILED:
listener.onFailed();
break;
default:
break;
}
}
/*改变暂停或者取消的状态*/
public void pauseDownLoad(){
isPause = true;
}
public void cancelDownLoad(){
isCancel = true;
}
/*
* 获取文件的长度*/
private long getContentLength(String downUrl){
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder().url(downUrl).build();
try {
Response response = okHttpClient.newCall(request).execute();
if (response != null && response.isSuccessful()){
long length = response.body().contentLength();
response.close();
return length;
}
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
}
现在是关键部分写一个service
package down;
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.v7.app.NotificationCompat;
import com.petecc.potevio.testvitmionew.MainActivity;
import com.petecc.potevio.testvitmionew.R;
import com.petecc.potevio.testvitmionew.TestDownLoadActivity;
import java.io.File;
public class DownLoadService extends Service {
private DownLoadTask downLoadTask;
private String downloadUrl;
private DownloadListener listener = new DownloadListener() {
@Override
public void onProgress(int progreess) {
getNotificationManager().notify(1,getNotification("下载中...",progreess));
}
@Override
public void onPuase() {
downLoadTask = null;
}
@Override
public void onCancel() {
downLoadTask = null;
//将前台服务通知关闭
stopForeground(true);
}
@Override
public void onFailed() {
downLoadTask = null;
//将前台服务通知关闭,并创建一个下载失败通知
stopForeground(true);
getNotificationManager().notify(1,getNotification("下载失败!",-1));
}
@Override
public void onSuccess() {
downLoadTask = null;
//将前台服务通知关闭,并创建一个下载成功通知
stopForeground(true);
getNotificationManager().notify(1,getNotification("下载成功!",-1));
}
};
public DownLoadService() {
}
private DownLoadBinder downLoadBinder = new DownLoadBinder();
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return downLoadBinder;
}
public class DownLoadBinder extends Binder{
public void startDownLoad(String url){
if (downLoadTask == null){
downloadUrl = url;
downLoadTask = new DownLoadTask(listener);
downLoadTask.execute(url);
//开启前台服务,保证在内存低的情况下服务不会被杀死。同时系统会创建一个notification
startForeground(1,getNotification("下载中...",0));
}
}
public void pauseDownload(){
if (downLoadTask != null){
downLoadTask.pauseDownLoad();
}
}
public void cancelDownload(){
if (downLoadTask != null){
downLoadTask.cancelDownLoad();
}
else{
//如果downloadUrl不等于null删除已经下载的文件,并关闭通知
if (downloadUrl != null){
String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
String dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
File file = new File(dir+fileName);
if (file.exists()){
file.delete();
}
getNotificationManager().cancel(1);
//取消前台服务
stopForeground(true);
}
}
}
}
/*获取Notification*/
private Notification getNotification(String title,int progress){
//intent 需要根据自己的需要来制定
Intent intent = new Intent(DownLoadService.this, TestDownLoadActivity.class);
PendingIntent penIntent = PendingIntent.getActivity(DownLoadService.this,0,intent,PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentTitle(title);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher));
builder.setAutoCancel(true);
builder.setWhen(System.currentTimeMillis());
builder.setContentIntent(penIntent);
if (progress >= 0){
builder.setContentText(progress+" %");
builder.setProgress(100,progress,false);
}
return builder.build();
}
/*获取NotificationManager*/
private NotificationManager getNotificationManager(){
return ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
}
}
测试使用
import android.Manifest;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Bundle;
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.view.View;
import android.widget.Button;
import android.widget.Toast;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import down.DownLoadService;
public class TestDownLoadActivity extends AppCompatActivity {
@BindView(R.id.start)
Button start;
@BindView(R.id.pause)
Button pause;
@BindView(R.id.cancel)
Button cancel;
@BindView(R.id.agin)
Button agin;
private DownLoadService.DownLoadBinder binder;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
binder = (DownLoadService.DownLoadBinder) iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
private String path = "https://raw.githubusercontent.com/guolindev/eclipse/master/eclipse-inst-win64.exe";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_down_load);
ButterKnife.bind(this);
Intent intent = new Intent(this, DownLoadService.class);
startService(intent);
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
//检查权限是否授权
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}
}
@OnClick({R.id.start, R.id.pause, R.id.cancel,R.id.agin})
public void onViewClicked(View view) {
if (binder == null) {
return;
}
switch (view.getId()) {
case R.id.start:
binder.startDownLoad(path);
break;
case R.id.pause:
binder.pauseDownload();
break;
case R.id.cancel:
binder.cancelDownload();
break;
case R.id.agin:
binder.startDownLoad("http://imgsrc.baidu.com/image/c0%3Dshijue%2C0%2C0%2C245%2C40/sign=2f6a98db3cd3d539d530078052ee8325/8c1001e93901213f5e8ef6f95ee736d12f2e9552.jpg");
break;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "无法使用", Toast.LENGTH_SHORT).show();
finish();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
@OnClick(R.id.agin)
public void onViewClicked() {
}
}
记得在清单文件注册service
android:name="down.DownLoadService"
android:enabled="true"
android:exported="true" />
所有代码就这么多,除布局文件都已经贴出!