点击之后下载完成
文件结构:
DownLoadListener.interface
package com.example.download2;
/**
* 下载状态的监听接口
*/
public interface DownLoadListener {
void onProgress(int progress);
void onSuccess();
void onFailed();
void onPaused();
void onCanceled();
}
DownLoadService.class
package com.example.download2;
import android.app.Notification;
import android.app.NotificationChannel;
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.Build;
import android.os.Environment;
import android.os.IBinder;
import android.widget.Toast;
import java.io.File;
/**
* 后台下载服务
*/
public class DownLoadService extends Service {
private DownLoadTask downLoadTask;
private String downLoadUrl;
private DownLoadListener downLoadListener = new DownLoadListener() {
@Override
public void onProgress(int progress) {
//getNotificationManager().notify(1, getNotification("正在下载", progress));
}
@Override
public void onSuccess() {
downLoadTask = null;
stopForeground(true);
// getNotificationManager().notify(1, getNotification("下载完成", -1));
Toast.makeText(DownLoadService.this, "下载完成!", Toast.LENGTH_SHORT).show();
}
@Override
public void onFailed() {
downLoadTask = null;
stopForeground(true);
//getNotificationManager().notify(1, getNotification("下载失败", -1));
Toast.makeText(DownLoadService.this, "下载失败!", Toast.LENGTH_SHORT).show();
}
@Override
public void onPaused() {
downLoadTask = null;
Toast.makeText(DownLoadService.this, "下载暂停!", Toast.LENGTH_SHORT).show();
}
@Override
public void onCanceled() {
downLoadTask = null;
stopForeground(true);
Toast.makeText(DownLoadService.this, "下载取消!", Toast.LENGTH_SHORT).show();
}
};
public DownLoadService() {
}
/**
* 自定义Binder类
*/
class DownLoadBinder extends Binder {
/**
* 开始下载
*
* @param url
*/
public void startDownLoad(String url) {
if (downLoadTask == null) {
downLoadUrl = url;
downLoadTask = new DownLoadTask(downLoadListener);
downLoadTask.execute(downLoadUrl);
//startForeground(1, getNotification("开始下载", 0));
}
}
/**
* 暂停下载
*/
public void pauseDownLoad() {
if (downLoadTask != null) {
downLoadTask.pauseDownLoad();
}
}
/**
* 取消下载
*/
public void cancelDownLoad() {
if (downLoadTask != null) {
downLoadTask.cancelDownLoad();
}
//文件删除
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, "下载取消!", Toast.LENGTH_SHORT).show();
}
}
}
private DownLoadBinder downLoadBinder = new DownLoadBinder();
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return downLoadBinder;
}
private NotificationManager getNotificationManager() {
return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
}
DownLoadTask.class
package com.example.download2;
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;
/**
* 下载的异步任务
*/
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 boolean isPaused = false;
private boolean isCanceled = false;
private int lastProgress;
private DownLoadListener downLoadListener;
public DownLoadTask(DownLoadListener downLoadListener) {
this.downLoadListener = downLoadListener;
}
public void setDownLoadListener(DownLoadListener downLoadListener) {
this.downLoadListener = downLoadListener;
}
@Override
protected Integer doInBackground(String... strings) {
InputStream inputStream = null;
RandomAccessFile randomAccessFile = null;
File file = null;
try {
long downLoadLength = 0;//记录已下载的文件长度
String downLoadUrl = strings[0];
//获取文件名称
String fileName = downLoadUrl.substring(downLoadUrl.lastIndexOf("/"));
//外部存储的公共文件夹目录
String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
file = new File(directory, fileName);
if (file.exists()) {
downLoadLength = file.length();
}
long contentLength = getContentLength(downLoadUrl);
if (contentLength == 0) {
return TYPE_FAILED;
} else if (contentLength == downLoadLength) {
//如果已下载的字节和文件总字节相等,说明已经下载完成了
return TYPE_SUCCESS;
}
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.addHeader("RANGE", "bytes=" + downLoadLength + "-") //断点继续下载
.url(downLoadUrl)
.build();
Response response = client.newCall(request).execute();
if (response != null) {
inputStream = response.body().byteStream();
randomAccessFile = new RandomAccessFile(file, "rw");
randomAccessFile.seek(downLoadLength);//跳过已下载的字节
byte[] bytes = new byte[1024];
int total = 0;
int len;
//inputStream.read(bytes)--读取多个字节写到bytes中
while ((len = inputStream.read(bytes)) != -1) {
if (isCanceled) {
return TYPE_CANCELED;
} else if (isPaused) {
return TYPE_PAUSED;
} else {
total += len;
randomAccessFile.write(bytes, 0, len);
int progress = (int)((total + downLoadLength) * 100 / contentLength);
publishProgress(progress);
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (randomAccessFile != null) {
randomAccessFile.close();
}
if (isCanceled && file != null) {
file.delete();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return TYPE_FAILED;
}
@Override
protected void onProgressUpdate(Integer... values) {
int progress = values[0];
if (progress > lastProgress) {
downLoadListener.onProgress(progress);
lastProgress = progress;
}
}
@Override
protected void onPostExecute(Integer integer) {
switch (integer) {
case TYPE_SUCCESS: {
downLoadListener.onSuccess();
break;
}
case TYPE_FAILED: {
downLoadListener.onFailed();
break;
}
case TYPE_PAUSED: {
downLoadListener.onPaused();
break;
}
case TYPE_CANCELED: {
downLoadListener.onCanceled();
break;
}
default:break;
}
}
/**
* 暂停下载
*/
public void pauseDownLoad() {
this.isPaused = true;
}
/**
* 取消下载
*/
public void cancelDownLoad() {
this.isCanceled = true;
}
/**
* 获取文件长度
*
* @param downLoadUrl
* @return
*/
private 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.body().close();
return contentLength;
}
} catch (IOException e) {
e.printStackTrace();
return 0;
}
return 0;
}
}
MainActivity.class
package com.example.download2;
import android.Manifest;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.IBinder;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final int WRITE_PERMISSION_CODE = 1000;
//文件下载链接
//private String url = "http://cloud.video.taobao.com/play/u/2577498496/p/1/e/6/t/1/50016582633.mp4";
private String url = "http://192.168.191.1:8080/First13/files/mmm/wode的右脚/1.pdf";
private Context mContext;
private Button btnStartDownLoad, btnPauseDownLoad, btnCancelDownLoad;
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);
baseDataInit();
bindViews();
viewsAddListener();
viewsDataInit();
}
private void baseDataInit() {
mContext = this;
}
private void bindViews() {
btnStartDownLoad = findViewById(R.id.Main_btnStartDownLoad);
btnPauseDownLoad = findViewById(R.id.Main_btnPauseDownLoad);
btnCancelDownLoad = findViewById(R.id.Main_btnCancelDownLoad);
}
private void viewsAddListener() {
btnStartDownLoad.setOnClickListener(this);
btnPauseDownLoad.setOnClickListener(this);
btnCancelDownLoad.setOnClickListener(this);
}
private void viewsDataInit() {
checkPermission(MainActivity.this);
Intent intent = new Intent(mContext, DownLoadService.class);
startService(intent);
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
@Override
public void onClick(View view) {
if (downLoadBinder == null) {
Toast.makeText(mContext, "下载服务创建失败!", Toast.LENGTH_SHORT).show();
return;
}
switch (view.getId()) {
case R.id.Main_btnStartDownLoad: {
downLoadBinder.startDownLoad(url);
break;
}
case R.id.Main_btnPauseDownLoad: {
downLoadBinder.pauseDownLoad();
break;
}
case R.id.Main_btnCancelDownLoad: {
downLoadBinder.cancelDownLoad();
break;
}
default:break;
}
}
private void checkPermission(Activity activity) {//开启本地的照片读取与写入权限
// Storage Permissions
final int REQUEST_EXTERNAL_STORAGE = 1;
String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,//读内存权限
Manifest.permission.WRITE_EXTERNAL_STORAGE};//写内存权限
try {
//检测是否有写的权限
int permission = ActivityCompat.checkSelfPermission(MainActivity.this,
"android.permission.WRITE_EXTERNAL_STORAGE");
if (permission != PackageManager.PERMISSION_GRANTED) {
// 没有写的权限,去申请写的权限,会弹出对话框
ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:id="@+id/Main_btnStartDownLoad"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="开始下载"/>
<Button
android:id="@+id/Main_btnPauseDownLoad"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="暂停下载"/>
<Button
android:id="@+id/Main_btnCancelDownLoad"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="取消下载"/>
</LinearLayout>
network-security-config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.download2">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:networkSecurityConfig="@xml/network_security_config"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".DownLoadService"
android:enabled="true"
android:exported="true"></service>
</application>
</manifest>
参考:https://blog.csdn.net/lpCrazyBoy/article/details/88776833
但是这里的很多地方都会有错误
采用okhttp,所以要在build.gradle加入implementation ‘com.squareup.okhttp3:okhttp:3.12.0’
1.像里面的通知方面由于过时api,同时我也不知道怎么替换,所以直接删去了这些通知功能
2.下面这些在最新的环境下都过时
上面两句替换成适合的即可
3.下面两句是为了访问本地存储权限,可采用现在流行的开启存储权限语句来访问
一个以前遇到过的小问题:
参考:https://www.jianshu.com/p/e3a163ad7815
如果出现能在模拟器中下载到本地,但是在手机上无法下载到手机中 ,则需要配置一下network-security-config.xml。
因为在手机上访问需要加上新的权限。