适配最新版的安卓11版本,针对原书中的已经过时的方法做了修改:
1.原:String directory= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
修改后:String directory =context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getPath();
2.原:Notification notification=NotificationCompat.Builder(this)
修改后://增加渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel("1","my_channel", NotificationManager.IMPORTANCE_DEFAULT);
manager.createNotificationChannel(notificationChannel);
}
//针对书上过时无法使用的方法进行修改
Notification notification = new NotificationCompat.Builder(getBaseContext(), "message")
.setSmallIcon(R.mipmap.ic_launcher2)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher2))
.setContentIntent(pi)
.setChannelId("1") //要和渠道中的id一致
.setContentTitle(title)
.setContentText(progress +"%")
.setProgress(100,progress,false)
.build();
return notification;
}
且通知已经加了渠道可以正常使用
1.DownloadListener接口
/**
* @anthor liu
* @createTime 2021/12/9 10:21
* @decription
*/
public interface DownloadListener {
void onProgress(int progress);//通知当前的下载进度
void onSuccess();//通知下载成功事件
void onFailed();//通知下载失败
void onPause();//通知下载暂停
void onCanceled();//通知下载取消
}
2.DownloadTask:下载的任务逻辑
/**
* @anthor liu
* @createTime 2021/12/9 10:25
* @decription 参数1:传入字符串给后台任务
* 参数2.使用整型数据作为显示进度的单位
* 参数3.使用整型数据来反馈执行结果
*/
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;
Context context;
private DownloadListener listener;
private boolean isCanceled=false;
private boolean isPaused=false;
private int lastProgress;
public DownloadTask(DownloadListener listener,Context context){
this.listener=listener;
this.context=context;
}
/*doInBackground方法 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中,执行具体的下载任务。
* 这里将主要负责执行那些很耗时的后台处理工作。可以调用 publishProgress方法来更新实时的任务进度。
* 该方法是抽象方法,子类必须实现。
*/
@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 =context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getPath();
//String directory= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
file=new File(directory + fileName);
Log.e("xx", file.getAbsolutePath());
//Log.e("xx", String.valueOf(file.exists()));
if(file.exists()){
downloadedLength=file.length();
}
long contentLength=getContentLength(downloadUrl);
if(contentLength==0){
Log.d("xx","文件有问题");
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 []b=new byte[1024];
int total=0;
int len;
while((len =is.read(b)) != -1){
if(isCanceled){
return TYPE_CANCELED;
}else if (isPaused){
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(isCanceled && file!=null){
file.delete();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return TYPE_FAILED;
}
/**
* 当在后台任务中调用publishProgress方法后,该方法被调用
* 界面上更新下载进度
* @param values
*/
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
int progress=values[0];
if(progress>lastProgress){
listener.onProgress(progress);
lastProgress=progress;
}
}
/**
* 当后台任务执行完毕并通过return语句进行返回时,返回的数据会作为参数传递,统治下在结果
* @param status
*/
@Override
protected void onPostExecute(Integer status) {
super.onPostExecute(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(){
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.DownloadService
public class DownloadService extends Service {
private DownloadTask downloadTask;
// Context context;
private String downloadUrl;
private DownloadListener listener=new DownloadListener() {
@Override
public void onProgress(int progress) {
getNotificationManager().notify(1,getNotification("Download ...",progress));
Log.d("DownloadService","Download ...");
}
@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 onPause() {
downloadTask=null;
Toast.makeText(DownloadService.this,"Paused",Toast.LENGTH_SHORT).show();
}
@Override
public void onCanceled() {
downloadTask=null;
stopForeground(true);
Toast.makeText(DownloadService.this,"Canceled",Toast.LENGTH_SHORT).show();
}
};
private DownLoadBinder mBinder=new DownLoadBinder();
public DownloadService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
// throw new UnsupportedOperationException("Not yet implemented");
return mBinder;
}
class DownLoadBinder extends Binder {
//开始下载
public void startDownload(String url){
if(downloadTask==null){
downloadUrl=url;
downloadTask=new DownloadTask(listener,getBaseContext());
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();
}
if(downloadUrl!=null){
//取消下载时将文件删除,并关闭通知
String fileName=downloadUrl.substring(downloadUrl.lastIndexOf("/"));
String directory =getBaseContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getPath();
//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();
}
}
}
private NotificationManager getNotificationManager(){
return (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
}
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);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//增加渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel("1","my_channel", NotificationManager.IMPORTANCE_DEFAULT);
manager.createNotificationChannel(notificationChannel);
}
//针对书上过时无法使用的方法进行修改
Notification notification = new NotificationCompat.Builder(getBaseContext(), "message")
.setSmallIcon(R.mipmap.ic_launcher2)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher2))
.setContentIntent(pi)
.setChannelId("1") //要和渠道中的id一致
.setContentTitle(title)
.setContentText(progress +"%")
.setProgress(100,progress,false)
.build();
return notification;
}
}
4.布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/start_download"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start Download"/>
<Button
android:id="@+id/pause_download"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Pause Download"/>
<Button
android:id="@+id/cancel_download"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Cancel Download"/>
</LinearLayout>
5.activity活动类
public class Activity10_2 extends AppCompatActivity implements View.OnClickListener {
private DownloadService.DownLoadBinder downloadBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
downloadBinder = (DownloadService.DownLoadBinder) service;//向下转型得到DownLoadBinder实例
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity10_2);
Button startDownload = findViewById(R.id.start_download);
Button pauseDownload = findViewById(R.id.pause_download);
Button cancelDownload = 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,connection,BIND_AUTO_CREATE);//
if(ContextCompat.checkSelfPermission(Activity10_2.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(Activity10_2.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
}
}
@Override
public void onClick(View v){
if (downloadBinder == null){
return;
}
switch (v.getId()){
case R.id.start_download:
String url = "https://img-s-msn-com.akamaized.net/tenant/amp/entityid/BB193Wn2.img?h=530&w=799&m=6&q=60&o=f&l=f";
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, String[] permissions, int[] grantResults){//运行时权限将会回调的方法
switch (requestCode){
case 1:
if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED){
Toast.makeText(this, "拒绝权限将无法使用程序", Toast.LENGTH_SHORT).show();
finish();
}
break;
default:
}
}
@Override
protected void onDestroy() {//程序销毁前,进行解绑
super.onDestroy();
unbindService(connection);
}
}