一个从服务器端下载apk 的小例子。下载过程中会实时的刷新进度条。这里使用了两种方法,一种是利用第三方的框架xutils中的HttpUtils来进行下载的,另一种是自己写的一个单线程下载的方法。
注意:
1、自己开子线程下载时不要在子线程中操作和UI有关的事情,否则会报错。这里利用发handler来对UI操作,保证在主线程(UI线程中)来操作刷新UI;
2、获取下载apk包大小的时候也要注意HttpURLConnection.getContentLength()获取的size跟下载下来的file的legth有可能不相等,这个和服务器端有关,也有可能getContentLength()返回的结果是-1.原因是:HttpURLConnection跟服务交互采用了"gzip"压缩。所以下载的fileLegth>HttpURLConnection.getContentLength().
api上也不推荐是用该方法来验证文件的完整性。可目前项目有不能修改服务器。通过继续研究api发现这种gzip压缩方式是可以取消的。取消办法这http
request的head中设置如下参数即可:urlConnection.setRequestProperty("Accept-Encoding", "identity");
至此基本上面诡异的问题修复。2.2以上的版本默认都是采用压缩优化希望大家注意。
主要的代码如下:
package cn.hjking.mydownloadeapkdemo;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DecimalFormat;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import cn.hjking.mydownloadapkdemo.R;
import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.HttpHandler;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;
public class MainActivity extends Activity {
private Activity act;
private HttpHandler httpHandler;
private AlertDialog downloadDialog;//正在下载的对话框
private TextView tvCur;//当前下载的百分比
private ProgressBar pb;//下载的进度条
private TextView tvCanCel;//停止下载
private TextView tvHidden;//隐藏对话框的按钮
private int contentLength;//要下载文件的大小
private boolean isDownloading = false;//是否正在下载
private boolean isCancel = false;//是否取消升级
private DecimalFormat df = new DecimalFormat("###.00");//设置结果保留两位小数
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
act = this;
}
/**
* 打开下载对话框的按钮单击事件
* @param v
*/
public void showDialog(View v){
if(isDownloading){
//如果正在下载,显示正在下载的对话框
if(!downloadDialog.isShowing()){
downloadDialog.show();
}
return;
}
isCancel = false;//设置当前没有进行取消下载
AlertDialog.Builder adb = new AlertDialog.Builder(this);
final AlertDialog alertDialog = adb.create();
View view = View.inflate(this, R.layout.dialog_layout, null);
alertDialog.setView(view, 0, 0, 0, 0);
TextView tvCancle = (TextView) view.findViewById(R.id.cancle_tv);
TextView tvOk = (TextView) view.findViewById(R.id.ok_tv);
tvCancle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
alertDialog.dismiss();
}
});
tvOk.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 开始下载
//downLoadApk();//利用第三方框架下载
MyDownload();//自己写的单线程下载方法
alertDialog.dismiss();
}
});
alertDialog.show();
}
/**
*
* 下载apk利用第三方框架xutils下载
*/
protected void downLoadApk() {
//判断sd卡是否存在。下载的文件存放至sd卡中
if(!isSDcardExist()){
Toast.makeText(this, "SD卡不存在,下载失败", 1).show();
return;
}
HttpUtils httpUtils = new HttpUtils();
String downloadUrl = "http://www.online-cmcc.com/gfms/app/apk/4GTraffic2MM.apk";
final String target = getDowloadPath() + File.separator +"4GTraffic2MM.apk";
httpHandler = httpUtils.download(downloadUrl, target, new RequestCallBack() {
@Override
public void onSuccess(ResponseInfo arg0) {
Toast.makeText(act, "下载成功", 1).show();
isDownloading = false;
if(downloadDialog.isShowing()){
downloadDialog.dismiss();
}
InstallAPK(target);
}
@Override
public void onFailure(HttpException arg0, String arg1) {
Toast.makeText(act, "访问服务器失败", 1).show();
if(downloadDialog.isShowing()){
downloadDialog.dismiss();
}
isDownloading = false;
}
@Override
public void onStart() {
super.onStart();
showDownloadDialog();
tvCanCel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//停止下载
//httpHandler.stop();
httpHandler.cancel();
if(downloadDialog.isShowing()){
downloadDialog.dismiss();
}
isDownloading = false;
}
});
tvHidden.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(downloadDialog.isShowing()){
downloadDialog.dismiss();
}
}
});
System.out.println("onStart");
}
@Override
public void onLoading(long total, long current, boolean isUploading) {
super.onLoading(total, current, isUploading);
isDownloading = true;
pb.setMax((int)total);
pb.setProgress((int)current);
tvCur.setText(df.format((float)current/(float)total * 100) + "%");
if(current == total){
pb.setProgress(pb.getMax());
}
}
});
}
/**
* 自己写的一个单线程下载
* 如果想要刷新要发handler(保证在UI线程中),
* 利用状态isDownloading或isCancel来判断当前是否停止下载等操作
*/
private void MyDownload(){
final String downloadUrl = "http://www.online-cmcc.com/gfms/app/apk/4GTraffic2MM.apk";
if(!isSDcardExist()){
Toast.makeText(act, "SD卡不可用", 1).show();
return;
}
final String filePath = getDowloadPath() + File.separator +"4GTraffic2MM.apk";
//自己开线程下载
new Thread(){
private InputStream inputStream;
private FileOutputStream fos;
public void run() {
try {
URL url = new URL(downloadUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(30000);
//http请求不要gzip压缩,否则获取的文件大小可以小于文件的实际大小
conn .setRequestProperty("Accept-Encoding", "identity");
int responseCode = conn.getResponseCode();
if(responseCode == 200){
inputStream = conn.getInputStream();
File file = new File(filePath);
fos = new FileOutputStream(file);
contentLength = conn.getContentLength();
System.out.println("文件的大小::" + contentLength);
int fileLengthFromHeader = Integer.parseInt(conn.getHeaderField("Content-Length"));
System.out.println("根据头文件获取文件的大小::" + fileLengthFromHeader);
//子线程不能显示和刷新UI
Message msg = Message.obtain();
msg.what = SHOWDOWNLOADDIALOG;
handler.sendMessage(msg);
byte[] buffer = new byte[1024];
int len = 0;
while(((len = inputStream.read(buffer)) != -1) && !isCancel ){
isDownloading = true;
fos.write(buffer, 0, len);
int curlength = (int) file.length();
Message updateMsg = Message.obtain();
updateMsg.what = UPDATEDOWNLOADDIALOG;
updateMsg.obj = curlength;
handler.sendMessage(updateMsg);
System.out.println("file.length()::" + curlength);
}
if(file.length() == contentLength){
//下载完成
Message finishedMsg = Message.obtain();
finishedMsg.what = DOWNLOADFINISHED;
finishedMsg.obj = filePath;
handler.sendMessage(finishedMsg);
}
}else{
Toast.makeText(act, "访问服务器失败", 1).show();
if(downloadDialog.isShowing()){
downloadDialog.dismiss();
}
isDownloading = false;
}
} catch (MalformedURLException e) {
e.printStackTrace();
System.out.println("MalformedURLException:" + e.getMessage());
} catch (IOException e2) {
e2.printStackTrace();
System.out.println("IOException:" + e2.getMessage());
}finally{
try {
if(inputStream != null){
inputStream.close();
}
if(fos != null){
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("IOException:" + e.getMessage());
}
}
};
}.start();
}
/**
* 显示正在下载的对话框
*
*/
private void showDownloadDialog(){
AlertDialog.Builder adb = new AlertDialog.Builder(act);
downloadDialog = adb.create();
View view = View.inflate(act, R.layout.download_dialog_layout, null);
downloadDialog.setView(view, 0, 0, 0, 0);
tvCur = (TextView) view.findViewById(R.id.tv_cursize);
tvCanCel = (TextView) view.findViewById(R.id.tv_cancel);
tvHidden = (TextView) view.findViewById(R.id.tv_hidden);
pb = (ProgressBar) view.findViewById(R.id.download_pb);
downloadDialog.show();
}
private final int SHOWDOWNLOADDIALOG = 88;
private final int UPDATEDOWNLOADDIALOG = 99;
private final int DOWNLOADFINISHED = 66;
/**
* 子线程不能刷新UI需要在这里处理
*/
private Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case SHOWDOWNLOADDIALOG://显示正在下载的对话框
showDownloadDialog();
pb.setMax(contentLength);
tvCanCel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//取消下载
isCancel = true;
isDownloading = false;
if(downloadDialog.isShowing()){
downloadDialog.dismiss();
}
}
});
tvHidden.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(downloadDialog.isShowing()){
downloadDialog.dismiss();
}
}
});
break;
case UPDATEDOWNLOADDIALOG://刷新正在下载对话框的内容
int curSize = (int)msg.obj;
pb.setProgress(curSize);
tvCur.setText(df.format((float)curSize / (float)contentLength * 100) + "%");
break;
case DOWNLOADFINISHED://下载完成后进行的操作
Toast.makeText(act, "下载成功", 1).show();
isDownloading = false;
if(downloadDialog.isShowing()){
downloadDialog.dismiss();
}
InstallAPK((String) msg.obj);
break;
default:
break;
}
};
};
/**
*
* 判断sd卡是否存在
* @return
*/
private boolean isSDcardExist(){
return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
}
private String getDowloadPath(){
return Environment.getExternalStorageDirectory().getAbsolutePath();
}
/**
* 安装apk
* @param filePath
*/
private void InstallAPK(String filePath){
Intent i = new Intent(Intent.ACTION_VIEW);
i.setDataAndType(Uri.parse("file://" + filePath),"application/vnd.android.package-archive");
startActivity(i);
}
}