最近项目要做软件内更新(在app中下载安装包),因为之前从来没做过下载,所以就照着《第一行代码》上的下载示例敲了一遍。
先看看项目中的文件目录,主要有一个下载监听接口,下载服务,下载
的AsyncTask以及一个测试界面。
DownloadListener文件代码:
public interface DownloadListener {
void onProgress(int progress);
void onSuccess();
void onFailed();
void onPaused();
void onCancel();
}
DownloadService文件代码:(在第一行代码的基础上加了个openFile的方法,用来在下载完成时自动安装)
public class DownloadService extends Service {
private String downloadUrl;
private DownloadTask mDownloadTask;
private final int notifyId = 1;
private DownloadListener mListener = new DownloadListener( ) {
@Override
public void onProgress(int progress) {
getNotificationManager().notify(notifyId, getNotification("下载中...", progress));
}
@Override
public void onSuccess() {
mDownloadTask = null;
// 下载成功后将前台服务关闭,创建一个下载完成的通知
stopForeground(true);
getNotificationManager().notify(notifyId, getNotification("下载成功!", -1));
ToastUtil.showToast("下载成功!");
String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
File file = new File(directory + fileName);
openFile(file);
}
@Override
public void onFailed() {
mDownloadTask = null;
// 下载失败时将前台服务关闭,创建一个下载失败的通知
stopForeground(true);
getNotificationManager().notify(notifyId, getNotification("下载失败!", -1));
ToastUtil.showToast("下载失败!");
}
@Override
public void onPaused() {
downloadUrl = null;
ToastUtil.showToast("已暂停");
}
@Override
public void onCancel() {
mDownloadTask =null;
stopForeground(true);
ToastUtil.showToast("已取消");
}
};
private DownloadBinder mBinder = new DownloadBinder();
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
class DownloadBinder extends Binder {
public void startDownload(String url) {
if (mDownloadTask == null) {
downloadUrl = url;
mDownloadTask = new DownloadTask(mListener);
mDownloadTask.execute(downloadUrl);
startForeground(notifyId, getNotification("下载中...", 0));
ToastUtil.showToast("开始下载");
}
}
public void pauseDownload() {
if (mDownloadTask != null) {
mDownloadTask.pauseDownload();
}
}
public void cancelDownload() {
if (mDownloadTask != null) {
mDownloadTask.cancelDownload();
} else {
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(notifyId);
stopForeground(true);
ToastUtil.showToast("已取消");
}
}
}
}
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, "1000");
builder.setSmallIcon(R.mipmap.logo)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.logo))
.setContentTitle(title)
.setAutoCancel(true);
if (progress >= 0) {
// progress大于等于0时才显示下载进度
builder.setContentText(progress + "%");
builder.setProgress(100, progress, false);
}
//builder.setContentIntent(pi);
return builder.build();
}
private void openFile(File f) {
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(android.content.Intent.ACTION_VIEW);
String type = "application/vnd.android.package-archive";
intent.setDataAndType(Uri.fromFile(f), type);
startActivity(intent);
}
}
DownloadTask文件:
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_PAUSE = 2;
public static final int TYPE_CANCELED = 3;
private DownloadListener mListener;
private boolean isCanceled = false;
private boolean isPaused = false;
private int lastProgress = 0;
public DownloadTask(DownloadListener listener) {
mListener = listener;
}
@Override
protected void onPreExecute() {
super.onPreExecute( );
}
@Override
protected Integer doInBackground(String... strings) {
InputStream is = null;
RandomAccessFile savedFile = null;
File file = null;
try{
// 记录已下载的文件长度
long downloadLength = 0;
// 传入第一个参数为下载地址
String downloadUrl = strings[0];
String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
// 保存到sd卡,这里是获取的是内部sd卡路径
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) {
is = response.body().byteStream();
savedFile = new RandomAccessFile(file, "rw");
// 跳过已下载的字节
savedFile.seek(downloadLength);
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_PAUSE;
} else {
total += len;
savedFile.write(b, 0, len);
// 计算已下载的百分比
int progress = (int) ((total + downloadLength) * 100 / contentLength);
publishProgress(progress);
}
}
response.body().close();
return TYPE_SUCCESS;
}
}catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (savedFile != null) {
savedFile.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) {
mListener.onProgress(progress);
lastProgress = progress;
}
}
@Override
protected void onPostExecute(Integer status) {
switch (status) {
case TYPE_SUCCESS:
mListener.onSuccess();
break;
case TYPE_CANCELED:
mListener.onCancel();
break;
case TYPE_PAUSE:
mListener.onPaused();
break;
case TYPE_FAILED:
mListener.onFailed();
default:
break;
}
}
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;
}
public void pauseDownload() {
isPaused = true;
}
public void cancelDownload() {
isCanceled = true;
}
}
UpdateTestActivity界面:(这里我用了EasyPermission开源库,要用库的同学可自行导库,很简单,这里不多说)
public class UpdateTestActivity extends AppCompatActivity implements View.OnClickListener, EasyPermissions.PermissionCallbacks {
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) {}
};
public static Intent newInstance(Context context) {
return new Intent(context, UpdateTestActivity.class);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_update_test );
Button start = findViewById(R.id.btn_start_download);
Button pause = findViewById(R.id.btn_pause_download);
Button cancel = findViewById(R.id.btn_cancel_download);
start.setOnClickListener(this);
pause.setOnClickListener(this);
cancel.setOnClickListener(this);
Intent intent = new Intent(this, DownloadService.class);
startService(intent);
bindService(intent, mConnection, BIND_AUTO_CREATE);
if (ContextCompat.checkSelfPermission(UpdateTestActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
EasyPermissions.requestPermissions(this, "请求向存储卡写入文件", 1, Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
}
@Override
protected void onDestroy() {
super.onDestroy( );
unbindService(mConnection);
}
@Override
public void onClick(View view) {
if (mDownloadBinder == null) {
return;
}
switch (view.getId()) {
case R.id.btn_start_download:
String url = "放你的测试下载地址,最好不要用书上的地址,会有超时错误";
mDownloadBinder.startDownload(url);
break;
case R.id.btn_pause_download:
mDownloadBinder.pauseDownload();
break;
case R.id.btn_cancel_download:
mDownloadBinder.cancelDownload();
break;
default:
break;
}
}
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
if (requestCode == 1) {
}
}
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
if (requestCode == 1) {
ToastUtil.showToast("拒绝权限无法运行");
finish();
}
}
}
最后在manifast中加上权限:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
运行的下载的图片就不放了,因为我直接放到项目中了,放应用图标好像不太好,等项目上线了我再来更新简书,把图放上。
(ps:找了一晚上升级的火箭图片,还P了一下图,才勉强做完了项目的更新界面,没有美工是真的烦。搞得写简书都没时间,只能简单的贴一贴代码了。哦,还有,天气越来越冷了,大家注意保暖哦!)
网友评论