任务:
1.从网络中下载文件存储到SD卡
2.显示下载进度
人丑话不多,直接撸代码。
xml布局文件:
<?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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击我开始下载"
android:id="@+id/btn_download"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下载进度:"
android:layout_marginTop="40dp"/>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progress="100"
/>
</LinearLayout>
java代码:
package com.example.jiaho.handleproject;
import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class DownLoadActivity extends AppCompatActivity {
public static final int DOWNLOAD_MESSAGE_CODE = 10001;
public static final int DOWNLOAD_MESSAGE_FAIL_CODE = 10002;
public static final String AppURL = "http://192.168.31.138/mukewang.apk";
private Button btn_download;
private ProgressBar progressBar;
private Handler mHandler;
private static final int REQUEST_EXTERNAL_STORAGE=1;
private static String[] PERMISSIONS_STORAGE={
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_download);
btn_download=findViewById(R.id.btn_download);
progressBar=findViewById(R.id.progressBar);
/*
* 主线程
* 1.点击按键
* 2.发起下载
* 3.开启子线程做下载
* 4.下载过程中通知主线程 ------> 主线程更新进度条
* */
btn_download.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//在子线程中执行download方法
new Thread(new Runnable() {
@Override
public void run() {
download(AppURL);
}
}).start();
}
});
//接收子线程的消息
mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//处理消息
switch (msg.what){
case DOWNLOAD_MESSAGE_CODE:
progressBar.setProgress((int)msg.obj);
break;
case DOWNLOAD_MESSAGE_FAIL_CODE:
Toast.makeText(DownLoadActivity.this,"下载失败",Toast.LENGTH_SHORT).show();
}
}
};
}
public void download(String appUrl){
try {
//拿到Url
URL url=new URL(appUrl);
//打开一个connect
URLConnection urlConnection=url.openConnection();
//获取输入流
InputStream inputStream=urlConnection.getInputStream();
//获取文件的总长度
int contentLength=urlConnection.getContentLength();
//创建一个下载路径
String downloadFolderName= Environment.getExternalStorageDirectory()+File.separator+"imooc"+File.separator;
//创建一个文件
File file=new File(downloadFolderName);
if(!file.exists()){
//如果文件不存在,就创建一个文件
file.mkdir();
}
//创建文件
String fileName=downloadFolderName+"mkapp.apk";
File fileMove=new File(fileName);
//如果文件存在就把它删掉,下次运行程序就不会报错
if (fileMove.exists()){
fileMove.delete();
}
//记录下载的大小
int downloadSize=0;
byte[] bytes=new byte[1024];
int length=0;
//创建一个输出流
//verifyStoragePermissions(DownLoadActivity.this);
OutputStream outputStream=new FileOutputStream(fileName);
while ((length=inputStream.read(bytes))!=-1){
outputStream.write(bytes,0,length);
downloadSize+=length;
/*
* 每完成一点下载,就及时更新UI
* */
Message message = Message.obtain();
//提取出一个静态变量
message.what = DOWNLOAD_MESSAGE_CODE;
message.obj=(downloadSize/contentLength)*100;
//将消息通过Handle发出去
mHandler.sendMessage(message);
}
}catch (MalformedURLException e){
//如果下载失败了,也要发送下载失败的消息
NotifyDownLoadFail();
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
NotifyDownLoadFail();
}
}
//提取出一个方法
private void NotifyDownLoadFail() {
Message message=Message.obtain();
message.what= DOWNLOAD_MESSAGE_FAIL_CODE;
mHandler.sendMessage(message);
}
//检查权限的方法,android6.0才需要在java代码中实现权限赋予
public static void verifyStoragePermissions(Activity activity){
//Check if we have write permission
int permission = ActivityCompat.checkSelfPermission(activity,Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission!= PackageManager.PERMISSION_GRANTED){
//没有权限,则通知用户
ActivityCompat.requestPermissions(activity,PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);
}
}
}
解释一下代码段:
//检查权限的方法,android6.0才需要在java代码中实现权限赋予
public static void verifyStoragePermissions(Activity activity){
//Check if we have write permission
int permission = ActivityCompat.checkSelfPermission(activity,Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission!= PackageManager.PERMISSION_GRANTED){
//没有权限,则通知用户
ActivityCompat.requestPermissions(activity,PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);
}
}
这个代码在android6.0上才被需要,因为当时写代码时,老是提示权限不够,就在网上查各种资料,所以各种变态权限都试了一遍,足足尝试了2个多小时,发现还是不行。后来我就纳闷了,我的android才4.4啊,根本不需要这些才对啊。于是我重新认真检查代码,发现了一个隐藏至深的bug,真是痛心疾首,老夫捶胸顿足,差点一口老血喷了出来。原来是我写文件路径的时候,将代码File.separator写成了File.pathSeparator。
简单讲下File.separator和File.pathSeparator的区别:
File.pathSeparator指的是分隔连续多个路径字符串的分隔符,例如:
java -cp test.jar;hello.jar
就是指";"
File.separator才是用来分隔同一个路径字符串中的目录,例如:
E:\Android\androidProject
就是指"\"