1. 运行前准备
本文附带实例: 实例中会下载一个应用程序(本公司的另一个产品:优鲜派 无病毒,请安心测试), 如果用户需要体验自动下载其它的产品,请自行配置apk下载路径: 将下图中的url路径换成自己的apk路径即可, apk的路径可以到360或者应用宝的官方网站上获取
2. 本实例运行效果图:
2.1 启动应用程序
2.2 点击 更新 操作, 开始下载最新版本apk,进度条显示下载进度
2.3 apk下载完成之后,自动安装下载的apk包, 注意下图的箭头处的意思
3.代码讲解
3.1 程序启动后, 调用后台接口获取最新apk版本信息(包括版本号与版本需要升级功能信息), 将最新版本号与当前apk版本号进行比较,如果版本号不一致,则弹出提示框,提示用户新版本中包含哪些最新信息, 用户根据自动需要选择是否更新
获得当前apk版本号:
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; }
由于无法模拟与后台的交互,这里将网络版本号写死,比当前apk版本号要大,模拟更新的场景
弹出对话框,提示新版本新功能
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";//更新内容 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.2 在弹出的对话框,用户点击"更新"与"取消"操作
更新时构建进度条页面
private void ShowDialog(int vision, String newversion, String content, final String url) { 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(); }
3.3 开启线程类下载应用程序,将下载进度显示到进度条上
线程启动前调用: onPreExecute
线程下载apk过程: onPostExecute
进度条更新: onProgressUpdate
下载apk完成回调: onPostExecute
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(); if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { return "Server returned HTTP " + connection.getResponseCode() + " " + connection.getResponseMessage(); } int fileLength = connection.getContentLength(); if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { file = Environment.getExternalStorageDirectory(); if (!file.exists()) { // 判断父文件夹是否存在 if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } } file = new File(file, DOWNLOAD_NAME); } 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) { ignored.toString(); } 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_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 { update(context); } } }
4. 安装下载完的apk包
private void update(Context context) { //安装应用 Log.i("lxl update->","update"); File apkFile = new File(Environment.getExternalStorageDirectory(), DOWNLOAD_NAME); Intent intent =new Intent(Intent.ACTION_VIEW); if (apkFile == null || context == null) { Log.i("lxl update->","null."); throw new NullPointerException(); } if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.N) { Uri contentUri = FileProvider.getUriForFile(context.getApplicationContext(), BuildConfig.APPLICATION_ID + ".fileProvider",apkFile); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.setDataAndType(contentUri,"application/vnd.android.package-archive"); Log.i("lxl update->",">>>==Build.VERSION_CODES.N"); }else{ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setDataAndType(Uri.fromFile(apkFile),"application/vnd.android.package-archive"); Log.i("lxl update->","<<<<< Build.VERSION_CODES.N"); } startActivity(intent); }