一、自动更新实现流程
从服务器端获取最新应用的版本信息
从本地配置文件中读取本地应用的版本信息
比较服务端版本信息和本地版本信息,若服务器端的版本号大于本地的版本号,则转4,否则结束
弹框提示用户应用程序有更新的版本,询问是否需要下载,若是则转5,否则弹框消失,结束
从服务器端获取最新版应用程序,存于本地,并进行安装
二、自动更新实现过程
新建一个Android工程,如我的工程文件目录为:
在工程文件中新建一个activity,用于呈现自动更新提示对话框。如上图工程中的AutoUpdateActivity,该activity代码如下:
//一个用于呈现提示用于进行更新的页面public class AutoUpdateActivity extends Activity { private static final String TAG = "AutoUpdateActivity"; //日志打印标志 private static int localVersionCode, serverVersionCode = 0; //本地和服务器端应用版本号 private String fileName, filePath; //应用程序保存文件名和文件路径 private Context context; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //设置当前activity的布局文件,任意布局文件皆可 setContentView(R.layout.activity_auto_update); context = this; filePath = "Star"; } @Override protected void onResume() { super.onResume(); // 检查是否需要进行版本更新,该函数必须在onResume或者onStart中调用,否则可能报错 checkUpdate(); } // 检查是否有新的版本 private void checkUpdate() { // 获取本地应用程序版本号 try { localVersionCode = context.getPackageManager().getPackageInfo( context.getPackageName(), 0).versionCode; } catch (NameNotFoundException e) { e.printStackTrace(); } // 比较本地版本号与服务器端版本号,判断是否需要更新 if (getServerVersionCode() > localVersionCode) { fileName = "Star.apk"; showUpdateDialog(); } } // 获取服务器端应用程序版本号 private int getServerVersionCode() { RequestParams params = new RequestParams(); StringBuilder versionCode = new StringBuilder(); versionCode.append("{\"getVersionCode\"}"); params.put("param", versionCode.toString()); AsyncHttpClient client = new AsyncHttpClient(); //向服务器请求最新版本号的URL地址 String urlString = utils.Constants.SERVER_BASE_URL + utils.Constants.GET_VERSION_CODE; client.post(urlString, params, new AsyncHttpResponseHandler() { @Override public void onSuccess(String response) { serverVersionCode = Integer.parseInt(JsonUtil .getResult(response)); } @Override public void onFailure(Throwable error, String content) { serverVersionCode = 0; Log.d(TAG, content + " "); } }); return serverVersionCode; } // 显示应用程序更新对话框 private void showUpdateDialog() { //创建一个对话框 AlertDialog.Builder builder = new AlertDialog.Builder(context); //设置对话框标题、显示信息等 builder.setTitle("版本更新").setMessage("Star有新版本啦,快下载体验吧~") .setIcon(com.star.R.drawable.ic_update_dialog); //设置对话框的响应按钮 builder.setPositiveButton("下载", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); //DownTask是一个我自己写的AsyncTask类,用于下载最新版的应用程序 DownTask downTask = new DownTask(context, filePath, fileName); downTask.execute(Constants.SERVER_BASE_URL + Constants.GET_NEW_VERSION); } }); builder.setNegativeButton("以后再说", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); //对话框消失 } }); builder.show(); //必须调用该函数,否则创建的对话框将不会显示 }}
上面代码中的checkUpdate()函数包含了上述原理一中的1、2、3步,当if条件判断为真时,显示应用更新提示对话框,即调用showUpdateDilaog()方法,用户选择“下载”,则下载该应用程序,并进行更新(这部分为调用DownTask进行处理);
DownTask类代码如下:
//DownTask是通过一部任务进行应用下载的,也可以开启一个新的工作线程进行下载public class DownTask extends AsyncTask{ private static final String TAG = "DownTask"; private Context mContext; private AlertDialog aDialog; private String filePath; private String fileName; /** * 构造函数 * * @param ctx * @param savePath * :下载资源保存路径 @param fileName * :下载资源保存文件名 */ public DownTask(Context ctx, String savePath, String fileName) { mContext = ctx; filePath = savePath; this.fileName = fileName; } @SuppressWarnings("unused") @Override protected String doInBackground(String... params) { //FileUtils是自己写的一个文件工具 FileUtils fileUtil = new FileUtils(); String result = null; File file; try { file = new File(fileUtil.getSDPATH() + filePath + "/" + fileName); if (file.exists()) { result = "File exist!"; Log.i(TAG, result); } else { fileUtil.createSDDir(filePath); file.createNewFile(); URL url = new URL(params[0]); HttpURLConnection urlConnection = (HttpURLConnection) url .openConnection(); fileUtil.write2SDFromInput(filePath, fileName, urlConnection.getInputStream()); if (file != null) { result = "success"; } else { result = "Fail"; } } } catch (IOException e1) { e1.printStackTrace(); } Log.i(TAG, result); return result; } @Override protected void onPostExecute(String result) { Toast.makeText(mContext, "最新版Star已经下载好啦,赶快试用吧!", Toast.LENGTH_LONG); Log.i(TAG, "Finish download"); aDialog.dismiss(); installApk(); } // 安装apk文件 private void installApk() { File apkfile = new File(filePath + "/" + fileName); if (!apkfile.exists()) { return; } //调用安装应用程序的Intent Intent i = new Intent(Intent.ACTION_VIEW); i.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive"); mContext.startActivity(i); } @Override protected void onPreExecute() { AlertDialog.Builder builder = new AlertDialog.Builder(mContext); builder.setTitle("下载Star").setMessage("新版本下载中...") .setIcon(R.drawable.ic_update_dialog).show(); aDialog = builder.create(); }}
FileUtils代码:
public class FileUtils { private String SDPATH; private int FILESIZE = 4 * 1024; public String getSDPATH() { return SDPATH; } public FileUtils() { // 得到当前外部存储设备的目录( /SDCARD ) SDPATH = Environment.getExternalStorageDirectory() + "/"; } /** * 在SD卡上创建文件 * * @param fileName * @return * @throws IOException */ public File createSDFile(String fileName) throws IOException { File file = new File(SDPATH + fileName); file.createNewFile(); return file; } /** * 在SD卡上创建目录 * * @param dirName * @return */ public File createSDDir(String dirName) { File dir = new File(SDPATH + dirName); dir.mkdir(); return dir; } /** * 判断SD卡上的文件夹是否存在 * * @param fileName * @return */ public boolean isFileExist(String fileName) { File file = new File(SDPATH + fileName); return file.exists(); } /** * 将一个InputStream里面的数据写入到SD卡中 * * @param path * @param fileName * @param input * @return */ public File write2SDFromInput(String path, String fileName, InputStream input) { File file = null; OutputStream output = null; try { createSDDir(path); file = createSDFile(path + fileName); output = new FileOutputStream(file); byte[] buffer = new byte[FILESIZE]; int length; while ((length = (input.read(buffer))) > 0) { output.write(buffer, 0, length); } output.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { output.close(); } catch (IOException e) { e.printStackTrace(); } } return file; }}
最后别忘了在manifest文件中添加相应的权限以及该activity
注意:Android不能在UI线程中进行比较耗时的操作,否则可能导致页面阻塞,甚至引发ANR错误(程序未响应),所以该示例中的下载操作放到一个异步任务中进行的,也可以采用工作线程的方式