Android下载apk并安装,实时刷新进度条

一个从服务器端下载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);
	}
}

 
   
如果需要源码可以在此下载:
点击下载源码

你可能感兴趣的:(Android项目开发小知识)