今天我们学习如何实现Android应用的自动更新版本功能,这是在各种语言编写的应用中都会经常遇到的情景。当我们的应用检测到网络上有新版本发布时,系统会提示是否下载新版本应用,当新版本应用下载完毕后,系统会自动安装下载的新版本应用(或跳转到相关安装页面询问)。我们将下载的应用存放在sdcard中,由于整个流程涉及对sdcard的读写操作,所以要赋给我们应用读写外存的权限。下面给出该场景的案例:
一、案例技术要点
1.程序清单文件中需要配置如下权限:
访问网络
读取sdcard
写入sdcard
2.创建一个HttpURLConnection连接,从网络下载新版本应用到本地
3.创建一个ProgressBar下载进度条,实时显示下载应用的进度
4.应用下载完毕后,构建Intent跳转至其安装页面,该Intent的配置如下:
Action:Intent.ACTION_VIEW
DataAndType:Uri.parse("file://" + appFile.toString()),"application/vnd.android.package-archive"
二、案例代码陈列
AndroidManifest.xml
strings.xml
Android实现应用自动更新
main.xml
下载进度条布局文件:progressBar.xml
AutoUpdateMainActivity.java
package cn.lynn.autoupdate;import android.app.Activity;import android.os.Bundle;public class AutoUpdateMainActivity extends Activity { private UpdateAppManager updateManager; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); updateManager = new UpdateAppManager(this); updateManager.checkUpdateInfo(); }}
UpdateAppManager.java
package cn.lynn.autoupdate;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.URL;import android.app.AlertDialog;import android.app.Dialog;import android.content.Context;import android.content.DialogInterface;import android.content.DialogInterface.OnClickListener;import android.content.Intent;import android.net.Uri;import android.os.Environment;import android.os.Handler;import android.os.Message;import android.view.LayoutInflater;import android.view.View;import android.widget.ProgressBar;public class UpdateAppManager { // 文件分隔符 private static final String FILE_SEPARATOR = "/"; // 外存sdcard存放路径 private static final String FILE_PATH = Environment.getExternalStorageDirectory() + FILE_SEPARATOR +"autoupdate" + FILE_SEPARATOR; // 下载应用存放全路径 private static final String FILE_NAME = FILE_PATH + "autoupdate.apk"; // 更新应用版本标记 private static final int UPDARE_TOKEN = 0x29; // 准备安装新版本应用标记 private static final int INSTALL_TOKEN = 0x31; private Context context; private String message = "检测到本程序有新版本发布,建议您更新!"; // 以华为天天聊hotalk.apk为例 private String spec = "http://222.42.1.209:81/1Q2W3E4R5T6Y7U8I9O0P1Z2X3C4V5B/mt.hotalk.com:8080/release/hotalk1.9.17.0088.apk"; // 下载应用的对话框 private Dialog dialog; // 下载应用的进度条 private ProgressBar progressBar; // 进度条的当前刻度值 private int curProgress; // 用户是否取消下载 private boolean isCancel; public UpdateAppManager(Context context) { this.context = context; } private final Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what) { case UPDARE_TOKEN: progressBar.setProgress(curProgress); break; case INSTALL_TOKEN: installApp(); break; } } }; /** * 检测应用更新信息 */ public void checkUpdateInfo() { showNoticeDialog(); } /** * 显示提示更新对话框 */ private void showNoticeDialog() { new AlertDialog.Builder(context) .setTitle("软件版本更新") .setMessage(message) .setPositiveButton("下载", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); showDownloadDialog(); } }).setNegativeButton("以后再说", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }).create().show(); } /** * 显示下载进度对话框 */ private void showDownloadDialog() { View view = LayoutInflater.from(context).inflate(R.layout.progressbar, null); progressBar = (ProgressBar) view.findViewById(R.id.progressBar); AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle("软件版本更新"); builder.setView(view); builder.setNegativeButton("取消", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); isCancel = true; } }); dialog = builder.create(); dialog.show(); downloadApp(); } /** * 下载新版本应用 */ private void downloadApp() { new Thread(new Runnable() { @Override public void run() { URL url = null; InputStream in = null; FileOutputStream out = null; HttpURLConnection conn = null; try { url = new URL(spec); conn = (HttpURLConnection) url.openConnection(); conn.connect(); long fileLength = conn.getContentLength(); in = conn.getInputStream(); File filePath = new File(FILE_PATH); if(!filePath.exists()) { filePath.mkdir(); } out = new FileOutputStream(new File(FILE_NAME)); byte[] buffer = new byte[1024]; int len = 0; long readedLength = 0l; while((len = in.read(buffer)) != -1) { // 用户点击“取消”按钮,下载中断 if(isCancel) { break; } out.write(buffer, 0, len); readedLength += len; curProgress = (int) (((float) readedLength / fileLength) * 100); handler.sendEmptyMessage(UPDARE_TOKEN); if(readedLength >= fileLength) { dialog.dismiss(); // 下载完毕,通知安装 handler.sendEmptyMessage(INSTALL_TOKEN); break; } } out.flush(); } catch (Exception e) { e.printStackTrace(); } finally { if(out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } if(in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if(conn != null) { conn.disconnect(); } } } }).start(); } /** * 安装新版本应用 */ private void installApp() { File appFile = new File(FILE_NAME); if(!appFile.exists()) { return; } // 跳转到新版本应用安装页面 Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.parse("file://" + appFile.toString()), "application/vnd.android.package-archive"); context.startActivity(intent); }}
三、案例效果展示
新版本应用下载后,sdcard相关存放目录如下: