我的整个操作在fragment中进行,与在活动中进行无异;
一、在xml中添加两个控件:按钮(用于开启下载操作),进度条(用于查看下载进度);
二、准备好所需常量(用于handler传递消息)以及下载链接;
三、实例化控件对象,以及为按钮设置监听;
点击按钮便开始进行下载操作,download方法用于进行下载操作,传入的形参为下载链接,由于下载方法内有相关网络操作,需放在线程中进行(线程通过Thread匿名内部类简单实现,由于并没有创建线程池,AS给出相应提示(冒黄线),但并不影响程序执行);
四、准备好handler对象,使用handler进行异步操作,并通过handler传送下载进度;
重写handler类的handMessage方法,用于接收download方法传来的消息,消息定义为两种:网络连接成功和网络连接失败;
DOWNLOAD_MESSAGE_CODE表示网络连接成功,DOWNLOAD_MESSAGE_FILE_CODE则表示传输失败
被注释掉的行为放在后面讲解;
五、实现download方法(核心),代码稍微有点长,但是细节慢慢、通俗易懂,请耐心观看!!!
private void download(String appUrl) {
try {
//创建URL对象
URL url = new URL(appUrl);
//获取URL连接对象
URLConnection connection = url.openConnection();
//获取连接的输入流对象
InputStream inputStream = connection.getInputStream();
//获取文件总长度
int contentLength = connection.getContentLength();
Log.d(TAG, "文件总长度: " + contentLength);
/*
getContext().getFilesDir()表示外部存储
通过getAbsolutePath()获得外部存储路径
File.separator表示分隔符 "\"
以下路径表示SD卡的”imooc“文件
*/
//创建存储apk的目录文件路径
String downloadFolderName = getContext().getFilesDir().getAbsolutePath()
+ File.separator + "APP" + File.separator;
//通过路径创建文件夹对象,用于存放apk文件
File file = new File(downloadFolderName);
//如果该目录文件不存在则创建
if (!file.exists()){
file.mkdir();
}
//在刚创建的文件下,创建文件名,用于存放下载的apk
String fileName = downloadFolderName + "APP.apk";
//定义一个下载的长度
int downloadSize = 0;
//用于传输文件
byte[] bytes = new byte[1024];
//用于判断
int length = 0;
/*
OutputStream需要注意的细节:
1、如何文件夹存在则先清空文件夹,如果不存在则创建,然后再写入(但如果目标文件所在的文件夹不存在,则抛出异常)
2、如果目标文件已经存在,则会先清空文件中的数据,然后再写入新的数据
*/
OutputStream outputStream = new FileOutputStream(fileName,false);
//读取数据
while((length = inputStream.read(bytes)) != -1){
outputStream.write(bytes,0,length);
downloadSize += length;
/*
message对象可携带四个参数:what、arg1、arg2、obj;
第一个参数为消息编码,整形。用于区分不同消息
第二和第三都为整形参数,用于传输整形数据
第四个为obj类型。可携带任意对象
*/
//创建消息对象,用于发送进度值给主线程来更新UI
Message message = Message.obtain();
//将下载进度比作为对象进行传输(由于我给进度条设置的最大值是一百,算出进度比,然后乘以100便是对应的进度值)
message.obj = downloadSize * 100 /contentLength;
message.what = DOWNLOAD_MESSAGE_CODE;
//发送消息
handler.sendMessage(message);
}
//关闭流
inputStream.close();
outputStream.close();
} catch (MalformedURLException e) {
notifyDownloadFile(e.getMessage());
e.printStackTrace();
} catch (IOException e) {
notifyDownloadFile(e.getMessage());
e.printStackTrace();
}
}
该方法用于发送错误消息
protected void notifyDownloadFile(String error){
Message message = Message.obtain();
message.obj = error;
message.what = DOWNLOAD_MESSAGE_FILE_CODE;
handler.sendMessage(message);
}
再回到接收消息的地方
switch (msg.what){
//传输成功
case DOWNLOAD_MESSAGE_CODE:
// TODO: 设置进度条
bar.setProgress((Integer) msg.obj);
if (bar.getProgress() == 100){
Toast.makeText(getActivity(),"下载完成",Toast.LENGTH_SHORT).show();
bar.setProgress(0);
}
break;
//传输失败
case DOWNLOAD_MESSAGE_FILE_CODE:
// TODO: 打印错误日志
/*String Message = (String) msg.obj;
Log.d(TAG, "ErrorMessage: " + Message);*/
break;
default:
}
通过switch区分开消息类型,在下载的过程中,download方法会远远不断的向主线程发送消息,直到下载完成,而主线程(UI线程)则来完成进度条的加载操作;
六、最后一步,同时也是最最最不应该忘记的一步,在AndroidManifest.xml写入相关权限
一个为网络访问权限,二为读文件权限,三为写文件权限;
七、!!!重要的事情说三遍
在Android9.0版本中,对网络安全有较高的要求,如果访问的是https协议则不受影响;
而若是访问了http协议的网站,则还需要添加安全配置文件,步骤如下:
1、创建安全配置文件
在res中创建一个xml目录,在此目录下新建一个”network_security_config"文件(文件名和内容不可修改),文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted = "true"/>
</network-security-config>
2、添加安全配置
在 AndroidManifest.xml添加该配置