- 创建接口用于对下载状态的监听和回调。
public interface DownloadListener {
void onProgress(int progress);
void onSuccess();
void onFailed();
void onPause();
void onCanceld();
}
- 创建下载异步任务。提供开始,暂停,取消方法供service调用。
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 mListener;
private boolean isCanceld = false;
private boolean isPaused = false;
private int lastProgress;
public DownloadTask(DownloadListener downloadListener) {
this.mListener = downloadListener;
}
@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);
if (file.exists()) {
downloadedLength = file.length();
}
long contentLength = getContentLength(downloadUrl);
if (contentLength == 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[] b = new byte[1024];
int total = 0;
int len;
while ((len = is.read(b)) != -1) {
if (isCanceld) {
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(isCanceld && 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) {
mListener.onProgress(progress);
lastProgress = progress;
}
}
@Override
protected void onPostExecute(Integer integer) {
switch (integer) {
case TYPE_CANCELED:
mListener.onCanceld();
break;
case TYPE_SUCCESS:
mListener.onSuccess();
break;
case TYPE_FAILED:
mListener.onFailed();
break;
case TYPE_PAUSED:
mListener.onPause();
break;
}
super.onPostExecute(integer);
}
public void pauseDownload() {
isPaused = true;
}
public void cancelDownload() {
isCanceld = 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.close();
return contentLength;
}
return 0;
}
}
- 创建Service,重写Listener方法接收回调。自定义内部类Binder同Activity通信。
public class DownloadService extends Service {
private DownloadTask mDownloadTask;
private String mDownloadUrl;
private DownloadBinder mBinder = new DownloadBinder();
private DownloadListener mDownloadListener = new DownloadListener() {
@Override
public void onProgress(int progress) {
getNotificationManager().notify(1, getNotification("Downloading...", progress));
}
@Override
public void onSuccess() {
mDownloadTask = null;
stopForeground(true);
getNotificationManager().notify(1, getNotification("Download Success", -1));
Toast.makeText(DownloadService.this, "Download Success", Toast.LENGTH_SHORT).show();
}
@Override
public void onFailed() {
mDownloadTask = null;
stopForeground(true);
getNotificationManager().notify(1, getNotification("Download Failed", -1));
Toast.makeText(DownloadService.this, "Download Failed", Toast.LENGTH_SHORT).show();
}
@Override
public void onPause() {
mDownloadTask = null;
Toast.makeText(DownloadService.this, "Download Paused", Toast.LENGTH_SHORT).show();
}
@Override
public void onCanceld() {
mDownloadTask = null;
stopForeground(true);
Toast.makeText(DownloadService.this, "Download Canceld", Toast.LENGTH_SHORT).show();
}
};
public DownloadService() {
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
class DownloadBinder extends Binder {
public void startDownload(String url) {
if (mDownloadTask == null) {
mDownloadUrl = url;
mDownloadTask = new DownloadTask(mDownloadListener);
mDownloadTask.execute(mDownloadUrl);
startForeground(1, getNotification("Downloading...", 0));
Toast.makeText(DownloadService.this, "Downloading...", Toast.LENGTH_SHORT).show();
}
}
public void pauseDownload() {
if (mDownloadTask != null) {
mDownloadTask.pauseDownload();
}
}
public void cancelDownload() {
if (mDownloadTask != null) {
mDownloadTask.cancelDownload();
} else {
if (mDownloadUrl != null) {
//取消下载时需要将文件删除,并将通知关闭
String fileNmae = mDownloadUrl.substring(mDownloadUrl.lastIndexOf("/"));
String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
File file = new File(directory + fileNmae);
if (file.exists()) {
file.delete();
}
getNotificationManager().cancel(1);
stopForeground(true);
Toast.makeText(DownloadService.this, "Canceld", 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);
//创建channel
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("001", "my_channel", NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights(true); //是否在桌面icon右上角展示小红点
channel.setLightColor(Color.GREEN); //小红点颜色
channel.setShowBadge(true); //是否在久按桌面图标时显示此渠道的通知
manager.createNotificationChannel(channel);
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "001");
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
builder.setContentIntent(pi);
builder.setContentTitle(title);
if (progress > 0) {
builder.setContentText(progress + "%");
builder.setProgress(100, progress, false);
}
return builder.build();
}
}
- 创建Activity,在onCreate()中创建Service,检测请求通知权限,
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private DownloadService.DownloadBinder mDownloadBinder;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mDownloadBinder = (DownloadService.DownloadBinder) iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
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, mConnection, 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);
}
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.FOREGROUND_SERVICE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.FOREGROUND_SERVICE}, 2);
}
}
@Override
public void onClick(View view) {
if (mDownloadBinder == null) {
return;
}
checkNotifySetting();
switch (view.getId()) {
case R.id.start_download:
String url = "https://raw.githubusercontent.com/guolindev/eclipse/master/eclipse-inst-win64.exe";
mDownloadBinder.startDownload(url);
break;
case R.id.pause_download:
mDownloadBinder.pauseDownload();
break;
case R.id.cancel_download:
mDownloadBinder.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, "拒绝权限将无法使用程序", Toast.LENGTH_SHORT).show();
finish();
}
break;
case 2:
if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "拒绝权限将无法使用程序", Toast.LENGTH_SHORT).show();
finish();
}
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mConnection);
}
private void checkNotifySetting() {
NotificationManagerCompat manager = NotificationManagerCompat.from(this);
// areNotificationsEnabled方法的有效性官方只最低支持到API 19,低于19的仍可调用此方法不过只会返回true,即默认为用户已经开启了通知。
boolean isOpened = manager.areNotificationsEnabled();
if (isOpened) {
Toast.makeText(this, "通知权限已经被打开", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "通知权限未开启", Toast.LENGTH_SHORT).show();
openPermission();
}
}
private void openPermission() {
try {
// 根据isOpened结果,判断是否需要提醒用户跳转AppInfo页面,去打开App通知权限
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
//这种方案适用于 API 26, 即8.0(含8.0)以上可以用
intent.putExtra(EXTRA_APP_PACKAGE, getPackageName());
intent.putExtra(EXTRA_CHANNEL_ID, getApplicationInfo().uid);
//这种方案适用于 API21——25,即 5.0——7.1 之间的版本可以使用
//intent.putExtra("app_package", getPackageName());
//intent.putExtra("app_uid", getApplicationInfo().uid);
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
}