Android应用更新之自动检测版本及自动升级
步骤:
1.检测当前版本的信息AndroidManifest.xml?C>manifest?C>[Android]
2.从服务器获取版本号(版本号存在于xml文件中)并与当前检测到的版本进行匹配,如果不匹配,提示用户进行升级,如果匹配则进入程序主界面。(demo中假设需要更新)
3.当提示用户进行版本升级时,如果用户点击了“更新”,系统将自动从服务器上下载安装包并进行自动升级,如果点击取消将进入程序主界面。
效果图如下:
下面介绍一下代码的实现:
1.获取应用的当前版本号,我是封装了一个工具类来获取
// 获取本版本号,是否更新 int vision = Tools.getVersion(this);
获取当前版本号工具类:
public class Tools { /** * 检查是否存在SDCard * * @return */ public static boolean hasSdcard() { String state = Environment.getExternalStorageState(); if (state.equals(Environment.MEDIA_MOUNTED)) { return true; } else { return false; } } /** * 2 * 获取版本号 3 * @return 当前应用的版本号 4 */ public static int getVersion(Context context) { try { PackageManager manager = context.getPackageManager(); PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0); String version = info.versionName; int versioncode = info.versionCode; return versioncode; } catch (Exception e) { e.printStackTrace(); } return 0; } }
2.获取服务器版本号,是否要更新(此处就是简单的网络请求拿到需要的数据即可,我是写了固定值)
// 获取更新版本号 private void getVersion(final int vision) { // {"data":{"content":"其他bug修复。","id":"2","api_key":"android", // // "version":"2.1"},"msg":"获取成功","status":1} String data = ""; //网络请求获取当前版本号和下载链接 //实际操作是从服务器获取 //demo写死了 String newversion = "2.1";//更新新的版本号 String content = "\n" + "就不告诉你我们更新了什么-。-\n" + "\n" + "----------万能的分割线-----------\n" + "\n" + "(ㄒoㄒ) 被老板打了一顿,还是来告诉你吧:\n" + "1.下架商品误买了?恩。。。我搞了点小动作就不会出现了\n" + "2.侧边栏、弹框优化 ―― 这个你自己去探索吧,总得留点悬念嘛-。-\n";//更新内容 String url = "http://openbox.mobilem.360.cn/index/d/sid/3429345";//安装包下载地址 double newversioncode = Double .parseDouble(newversion); int cc = (int) (newversioncode); System.out.println(newversion + "v" + vision + ",," + cc); if (cc != vision) { if (vision < cc) { System.out.println(newversion + "v" + vision); // 版本号不同 ShowDialog(vision, newversion, content, url); } } }
3.接下来就是下载文件了
(1) 显示下载
此处用的是自定义按钮:
/** * 升级系统 * * @param content * @param url */ private void ShowDialog(int vision, String newversion, String content, final String url) { final MaterialDialog dialog = new MaterialDialog(this); dialog.content(content).btnText("取消", "更新").title("版本更新 ") .titleTextSize(15f).show(); dialog.setCanceledOnTouchOutside(false); dialog.setOnBtnClickL(new OnBtnClickL() {// left btn click listener @Override public void onBtnClick() { dialog.dismiss(); } }, new OnBtnClickL() {// right btn click listener @Override public void onBtnClick() { dialog.dismiss(); // pBar = new ProgressDialog(MainActivity.this, // R.style.dialog); pBar = new CommonProgressDialog(MainActivity.this); pBar.setCanceledOnTouchOutside(false); pBar.setTitle("正在下载"); pBar.setCustomTitle(LayoutInflater.from( MainActivity.this).inflate( R.layout.title_dialog, null)); pBar.setMessage("正在下载"); pBar.setIndeterminate(true); pBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pBar.setCancelable(true); // downFile(URLData.DOWNLOAD_URL); final DownloadTask downloadTask = new DownloadTask( MainActivity.this); downloadTask.execute(url); pBar.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { downloadTask.cancel(true); } }); } }); }
原生的按钮:
new android.app.AlertDialog.Builder(this) .setTitle("版本更新") .setMessage(content) .setPositiveButton("更新", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); pBar = new CommonProgressDialog(MainActivity.this); pBar.setCanceledOnTouchOutside(false); pBar.setTitle("正在下载"); pBar.setCustomTitle(LayoutInflater.from( MainActivity.this).inflate( R.layout.title_dialog, null)); pBar.setMessage("正在下载"); pBar.setIndeterminate(true); pBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pBar.setCancelable(true); // downFile(URLData.DOWNLOAD_URL); final DownloadTask downloadTask = new DownloadTask( MainActivity.this); downloadTask.execute(url); pBar.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { downloadTask.cancel(true); } }); } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }) .show();
(2)通过异步任务实现进度++
/** * 下载应用 * * @author Administrator */ class DownloadTask extends AsyncTask<String, Integer, String> { private Context context; private PowerManager.WakeLock mWakeLock; public DownloadTask(Context context) { this.context = context; } @Override protected String doInBackground(String... sUrl) { InputStream input = null; OutputStream output = null; HttpURLConnection connection = null; File file = null; try { URL url = new URL(sUrl[0]); connection = (HttpURLConnection) url.openConnection(); connection.connect(); // expect HTTP 200 OK, so we don't mistakenly save error // report // instead of the file if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { return "Server returned HTTP " + connection.getResponseCode() + " " + connection.getResponseMessage(); } // this will be useful to display download percentage // might be -1: server did not report the length int fileLength = connection.getContentLength(); if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { file = new File(Environment.getExternalStorageDirectory(), DOWNLOAD_NAME); if (!file.exists()) { // 判断父文件夹是否存在 if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } } } else { Toast.makeText(MainActivity.this, "sd卡未挂载", Toast.LENGTH_LONG).show(); } input = connection.getInputStream(); output = new FileOutputStream(file); byte data[] = new byte[4096]; long total = 0; int count; while ((count = input.read(data)) != -1) { // allow canceling with back button if (isCancelled()) { input.close(); return null; } total += count; // publishing the progress.... if (fileLength > 0) // only if total length is known publishProgress((int) (total * 100 / fileLength)); output.write(data, 0, count); } } catch (Exception e) { System.out.println(e.toString()); return e.toString(); } finally { try { if (output != null) output.close(); if (input != null) input.close(); } catch (IOException ignored) { } if (connection != null) connection.disconnect(); } return null; } @Override protected void onPreExecute() { super.onPreExecute(); // take CPU lock to prevent CPU from going off if the user // presses the power button during download PowerManager pm = (PowerManager) context .getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName()); mWakeLock.acquire(); pBar.show(); } @Override protected void onProgressUpdate(Integer... progress) { super.onProgressUpdate(progress); // if we get here, length is known, now set indeterminate to false pBar.setIndeterminate(false); pBar.setMax(100); pBar.setProgress(progress[0]); } @Override protected void onPostExecute(String result) { mWakeLock.release(); pBar.dismiss(); if (result != null) { // // 申请多个权限。大神的界面 // AndPermission.with(MainActivity.this) // .requestCode(REQUEST_CODE_PERMISSION_OTHER) // .permission(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE) // // rationale作用是:用户拒绝一次权限,再次申请时先征求用户同意,再打开授权对话框,避免用户勾选不再提示。 // .rationale(new RationaleListener() { // @Override // public void showRequestPermissionRationale(int requestCode, Rationale rationale) { // // 这里的对话框可以自定义,只要调用rationale.resume()就可以继续申请。 // AndPermission.rationaleDialog(MainActivity.this, rationale).show(); // } // } // ) // .send(); // 申请多个权限。 AndPermission.with(MainActivity.this) .requestCode(REQUEST_CODE_PERMISSION_SD) .permission(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE) // rationale作用是:用户拒绝一次权限,再次申请时先征求用户同意,再打开授权对话框,避免用户勾选不再提示。 .rationale(rationaleListener ) .send(); Toast.makeText(context, "您未打开SD卡权限" + result, Toast.LENGTH_LONG).show(); } else { // Toast.makeText(context, "File downloaded", // Toast.LENGTH_SHORT) // .show(); update(); } } }
此处下载apk文件,需要获取SD的读写权限(用的是严大的权限库)
权限库GitHub
private static final int REQUEST_CODE_PERMISSION_SD = 101; private static final int REQUEST_CODE_SETTING = 300; private RationaleListener rationaleListener = new RationaleListener() { @Override public void showRequestPermissionRationale(int requestCode, final Rationale rationale) { // 这里使用自定义对话框,如果不想自定义,用AndPermission默认对话框: // AndPermission.rationaleDialog(Context, Rationale).show(); // 自定义对话框。 AlertDialog.build(MainActivity.this) .setTitle(R.string.title_dialog) .setMessage(R.string.message_permission_rationale) .setPositiveButton(R.string.btn_dialog_yes_permission, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); rationale.resume(); } }) .setNegativeButton(R.string.btn_dialog_no_permission, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); rationale.cancel(); } }) .show(); } }; //----------------------------------SD权限----------------------------------// @PermissionYes(REQUEST_CODE_PERMISSION_SD) private void getMultiYes(List<String> grantedPermissions) { Toast.makeText(this, R.string.message_post_succeed, Toast.LENGTH_SHORT).show(); } @PermissionNo(REQUEST_CODE_PERMISSION_SD) private void getMultiNo(List<String> deniedPermissions) { Toast.makeText(this, R.string.message_post_failed, Toast.LENGTH_SHORT).show(); // 用户否勾选了不再提示并且拒绝了权限,那么提示用户到设置中授权。 if (AndPermission.hasAlwaysDeniedPermission(this, deniedPermissions)) { AndPermission.defaultSettingDialog(this, REQUEST_CODE_SETTING) .setTitle(R.string.title_dialog) .setMessage(R.string.message_permission_failed) .setPositiveButton(R.string.btn_dialog_yes_permission) .setNegativeButton(R.string.btn_dialog_no_permission, null) .show(); // 更多自定dialog,请看上面。 } } //----------------------------------权限回调处理----------------------------------// @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); /** * 转给AndPermission分析结果。 * * @param object 要接受结果的Activity、Fragment。 * @param requestCode 请求码。 * @param permissions 权限数组,一个或者多个。 * @param grantResults 请求结果。 */ AndPermission.onRequestPermissionsResult(this, requestCode, permissions, grantResults); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case REQUEST_CODE_SETTING: { Toast.makeText(this, R.string.message_setting_back, Toast.LENGTH_LONG).show(); //设置成功,再次请求更新 getVersion(Tools.getVersion(MainActivity.this)); break; } } }
(3) 当apk文件下载完毕时,打开安装
private void update() { //安装应用 Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(new File(Environment .getExternalStorageDirectory(), DOWNLOAD_NAME)), "application/vnd.android.package-archive"); startActivity(intent); }
源码
此demo已经上传到GitHub,如有需要自行下载
GitHub: 链接地址
栏 目:Android代码
下一篇:Android开发在RecyclerView上面实现"拖放"和"滑动删除"-2
本文地址:http://www.codeinn.net/misctech/198503.html