App在开发过程中,随着业务场景的不断增多,功能的不断完善,早期下载App的用户便无法体验最新的功能,为了能让用户更及时的体验App最新版本,在App开发过程加入App自动更新功能便显得尤为重要。更新apk主要分为二类: (1)用户点击更新后,前台进行下载,下载过程中用户无法操作 (2)后台进行下载,下载完成后,service回调进行apk的安装,下载过程中用户可操作,本来讲解第一种

本文demo下载:http://www.wisdomdd.cn/Wisdom/resource/articleDetail.htm?resourceId=121

1. 运行前准备

本文附带实例: 实例中会下载一个应用程序(本公司的另一个产品:优鲜派 无病毒,请安心测试), 如果用户需要体验自动下载其它的产品,请自行配置apk下载路径:  将下图中的url路径换成自己的apk路径即可, apk的路径可以到360或者应用宝的官方网站上获取

2. 本实例运行效果图: 

2.1 启动应用程序

2.2 点击 更新 操作, 开始下载最新版本apk,进度条显示下载进度

2.3 apk下载完成之后,自动安装下载的apk包, 注意下图的箭头处的意思

3.代码讲解

      3.1 程序启动后, 调用后台接口获取最新apk版本信息(包括版本号与版本需要升级功能信息), 将最新版本号与当前apk版本号进行比较,如果版本号不一致,则弹出提示框,提示用户新版本中包含哪些最新信息, 用户根据自动需要选择是否更新

获得当前apk版本号:

  1. public static int getVersion(Context context) {
  2. try {
  3. PackageManager manager = context.getPackageManager();
  4. PackageInfo info = manager.getPackageInfo(context.getPackageName(),
  5. 0);
  6. String version = info.versionName;
  7. int versioncode = info.versionCode;
  8. return versioncode;
  9. } catch (Exception e) {
  10. e.printStackTrace();
  11. }
  12. return 0;
  13. }

由于无法模拟与后台的交互,这里将网络版本号写死,比当前apk版本号要大,模拟更新的场景

弹出对话框,提示新版本新功能

  1. private void getVersion(final int vision) {
  2. // {"data":{"content":"其他bug修复。","id":"2","api_key":"android",
  3. // // "version":"2.1"},"msg":"获取成功","status":1}
  4. String data = "";
  5. //网络请求获取当前版本号和下载链接
  6. //实际操作是从服务器获取
  7. //demo写死了
  8.  
  9. String newversion = "2.1";//网络版本号
  10. String content = "\n" +
  11. "就不告诉你我们更新了什么-。-\n" +
  12. "\n" +
  13. "----------万能的分割线-----------\n" +
  14. "\n" +
  15. "(ㄒoㄒ) 被老板打了一顿,还是来告诉你吧:\n" +
  16.  
  17. "1.下架商品误买了?恩。。。我搞了点小动作就不会出现了\n" +
  18. "2.侧边栏、弹框优化 —— 这个你自己去探索吧,总得留点悬念嘛-。-\n";//更新内容
  19.  
  20. double newversioncode = Double
  21. .parseDouble(newversion);
  22. int cc = (int) (newversioncode);
  23.  
  24. System.out.println(newversion + "v" + vision + ",,"
  25. + cc);
  26. if (cc != vision) {
  27. if (vision < cc) {
  28. System.out.println(newversion + "v"
  29. + vision);
  30. // 版本号不同
  31. ShowDialog(vision, newversion, content, url);
  32. }
  33. }
  34. }

  

3.2 在弹出的对话框,用户点击"更新"与"取消"操作

更新时构建进度条页面

  1. private void ShowDialog(int vision, String newversion, String content, final String url) {
  2. new android.app.AlertDialog.Builder(this)
  3. .setTitle("版本更新")
  4. .setMessage(content)
  5. .setPositiveButton("更新", new DialogInterface.OnClickListener() {
  6. @Override
  7. public void onClick(DialogInterface dialog, int which) {
  8. dialog.dismiss();
  9. pBar = new CommonProgressDialog(MainActivity.this);
  10. pBar.setCanceledOnTouchOutside(false);
  11. pBar.setTitle("正在下载");
  12. pBar.setCustomTitle(LayoutInflater.from(
  13. MainActivity.this).inflate(
  14. R.layout.title_dialog, null));
  15. pBar.setMessage("正在下载");
  16. pBar.setIndeterminate(true);
  17. pBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
  18. pBar.setCancelable(true);
  19. // downFile(URLData.DOWNLOAD_URL);
  20. final DownloadTask downloadTask = new DownloadTask(
  21. MainActivity.this);
  22. downloadTask.execute(url);
  23. pBar.setOnCancelListener(new DialogInterface.OnCancelListener() {
  24. @Override
  25. public void onCancel(DialogInterface dialog) {
  26. downloadTask.cancel(true);
  27. }
  28. });
  29. }
  30. })
  31. .setNegativeButton("取消", new DialogInterface.OnClickListener() {
  32. @Override
  33. public void onClick(DialogInterface dialog, int which) {
  34. dialog.dismiss();
  35. }
  36. })
  37. .show();
  38. }

  

3.3 开启线程类下载应用程序,将下载进度显示到进度条上

线程启动前调用:   onPreExecute

线程下载apk过程:  onPostExecute

进度条更新:       onProgressUpdate

下载apk完成回调:  onPostExecute

  1. class DownloadTask extends AsyncTask<String, Integer, String> {
  2.  
  3. private Context context;
  4. private PowerManager.WakeLock mWakeLock;
  5.  
  6. public DownloadTask(Context context) {
  7. this.context = context;
  8. }
  9.  
  10. @Override
  11. protected String doInBackground(String... sUrl) {
  12. InputStream input = null;
  13. OutputStream output = null;
  14. HttpURLConnection connection = null;
  15. File file = null;
  16. try {
  17. URL url = new URL(sUrl[0]);
  18. connection = (HttpURLConnection) url.openConnection();
  19. connection.connect();
  20. if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
  21. return "Server returned HTTP "
  22. + connection.getResponseCode() + " "
  23. + connection.getResponseMessage();
  24. }
  25.  
  26. int fileLength = connection.getContentLength();
  27. if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
  28. file = Environment.getExternalStorageDirectory();
  29. if (!file.exists()) {
  30. // 判断父文件夹是否存在
  31. if (!file.getParentFile().exists()) {
  32. file.getParentFile().mkdirs();
  33. }
  34. }
  35. file = new File(file, DOWNLOAD_NAME);
  36. } else {
  37. Toast.makeText(MainActivity.this, "sd卡未挂载", Toast.LENGTH_LONG).show();
  38. }
  39. input = connection.getInputStream();
  40. output = new FileOutputStream(file);
  41. byte data[] = new byte[4096];
  42. long total = 0;
  43. int count;
  44. while ((count = input.read(data)) != -1) {
  45. // allow canceling with back button
  46. if (isCancelled()) {
  47. input.close();
  48. return null;
  49. }
  50. total += count;
  51. // publishing the progress....
  52. if (fileLength > 0) // only if total length is known
  53. publishProgress((int) (total * 100 / fileLength));
  54. output.write(data, 0, count);
  55.  
  56. }
  57. } catch (Exception e) {
  58. System.out.println(e.toString());
  59. return e.toString();
  60.  
  61. } finally {
  62. try {
  63. if (output != null)
  64. output.close();
  65. if (input != null)
  66. input.close();
  67. } catch (IOException ignored) {
  68. ignored.toString();
  69. }
  70. if (connection != null)
  71. connection.disconnect();
  72. }
  73. return null;
  74. }
  75.  
  76. @Override
  77. protected void onPreExecute() {
  78. super.onPreExecute();
  79. // take CPU lock to prevent CPU from going off if the user
  80. // presses the power button during download
  81. PowerManager pm = (PowerManager) context
  82. .getSystemService(Context.POWER_SERVICE);
  83. mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
  84. getClass().getName());
  85. mWakeLock.acquire();
  86. pBar.show();
  87. }
  88.  
  89. @Override
  90. protected void onProgressUpdate(Integer... progress) {
  91. super.onProgressUpdate(progress);
  92. // if we get here, length is known, now set indeterminate to false
  93. pBar.setIndeterminate(false);
  94. pBar.setMax(100);
  95. pBar.setProgress(progress[0]);
  96. }
  97.  
  98. @Override
  99. protected void onPostExecute(String result) {
  100. mWakeLock.release();
  101. pBar.dismiss();
  102. if (result != null) {
  103. // 申请多个权限。
  104. AndPermission.with(MainActivity.this)
  105. .requestCode(REQUEST_CODE_PERMISSION_SD)
  106. .permission(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
  107. // rationale作用是:用户拒绝一次权限,再次申请时先征求用户同意,再打开授权对话框,避免用户勾选不再提示。
  108. .rationale(rationaleListener
  109. ).send();
  110. Toast.makeText(context, "您未打开SD卡权限" + result, Toast.LENGTH_LONG).show();
  111. } else {
  112. update(context);
  113. }
  114.  
  115. }
  116. }

  4. 安装下载完的apk包

  1. private void update(Context context) {
  2. //安装应用
  3. Log.i("lxl update->","update");
  4.  
  5. File apkFile = new File(Environment.getExternalStorageDirectory(), DOWNLOAD_NAME);
  6.  
  7. Intent intent =new Intent(Intent.ACTION_VIEW);
  8.  
  9. if (apkFile == null || context == null) {
  10. Log.i("lxl update->","null.");
  11. throw new NullPointerException();
  12. }
  13.  
  14. if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.N) {
  15.  
  16. Uri contentUri = FileProvider.getUriForFile(context.getApplicationContext(),
  17. BuildConfig.APPLICATION_ID + ".fileProvider",apkFile);
  18.  
  19. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  20.  
  21. intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
  22.  
  23. intent.setDataAndType(contentUri,"application/vnd.android.package-archive");
  24. Log.i("lxl update->",">>>==Build.VERSION_CODES.N");
  25.  
  26. }else{
  27.  
  28. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  29.  
  30. intent.setDataAndType(Uri.fromFile(apkFile),"application/vnd.android.package-archive");
  31. Log.i("lxl update->","<<<<< Build.VERSION_CODES.N");
  32. }
  33.  
  34. startActivity(intent);
  35.  
  36. }

本文demo下载:http://www.wisdomdd.cn/Wisdom/resource/articleDetail.htm?resourceId=121

Android App版本自动更新的更多相关文章

  1. Android(2)—Mono For Android App版本自动更新

    0.前言 公司Android查询的项目已经开始,整体采用Java后台+App前台来实现,由于项目小,App这块就我一个人开发,首先需要研究的是:Android版本的更新升级问题:本人经过近一周的学习整 ...

  2. Android(3)—Mono For Android App版本自动更新(2)

    0.前言 这篇博文是上一篇的延续,主要是修改上一个版中的BUG和优化一些待完善的项,也算是结贴,当然还有需要完善的,等日后项目中用到的时候再单独写出来吧,本篇主要写升级改进的部分: 改进1.修复[BU ...

  3. Android实现App版本自动更新

    现在很多的App中都会有一个检查版本的功能.例如斗鱼TV App的设置界面下: 当我们点击检查更新的时候,就会向服务器发起版本检测的请求.一般的处理方式是:服务器返回的App版本与当前手机安装的版本号 ...

  4. 如何实现已发布app的自动更新

    要实现app的自动更新,做两件事情就可以搞定 1.获取当前手机中的app版本号 我们可以通过查询mainbundle中的获取CFBundleVersion NSDictionary *infoDict ...

  5. C#.Net版本自动更新程序及3种策略实现

    C#.Net版本自动更新程序及3种策略实现 C/S程序是基于客户端和服务器的,在客户机编译新版本后将文件发布在更新服务器上,然后建立一个XML文件,该文件列举最新程序文件的版本号及最后修改日期.如程序 ...

  6. 获取 Android APP 版本信息工具类(转载)

    获取 Android APP 版本信息工具类 获取手机APP版本信息工具类 1.获取版本名称 2.获取版本号 3.获取App的名称 package com.mingyue.nanshuibeidiao ...

  7. Android 版本自动更新

    截图如下: 代码实现如下: package com.update.apk; import java.io.BufferedReader; import java.io.File; import jav ...

  8. 【Android 应用开发】Android应用的自动更新模块

    . 作者 :万境绝尘  转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/18964835 . 软件的自动更新一般都与Splash界 ...

  9. 安卓程序代写 网上程序代写[原]Android应用的自动更新模块

    软件的自动更新一般都与Splash界面绑定在一起, 由于需要维护的软件界面很复杂, 一个Activity中嵌入ViewPager, 并且逻辑比较复杂, 索性重新写一个Activity, 现在的软件都很 ...

随机推荐

  1. jemeter工作台设置

    工作台的设置 1.创建一个线程组 创建一个http代理服务器:工作台-->添加-->非测试元件-->http代理服务器 设置参照下图,要录制的时候点击启动 2.设置IE浏览器 IE- ...

  2. Angular 报错 Can't bind to 'formGroup' since it isn't a known property of 'form'

    错误描述 当form表单加FormGroup属性时报错 Can't bind to 'formGroup' since it isn't a known property of 'form' < ...

  3. 深谈auto变量

    1.c++中有一个关键字auto,c语言也有这么一个关键字,但是两者的意义大不相同. 2.c++中用auto定义的变量自动匹配赋值号右边的值的类型,具有自动匹配类型的作用,而c语言中auto只是声明一 ...

  4. ArcGIS API for JavaScript 4.2学习笔记[12] View的弹窗(Popup)

    看本文前最好对第二章(Mapping and Views)中的Map和View类有理解. 视图类有一个属性是Popup类型的popup,查阅API知道这个就是视图的弹窗,每一个View的实例都有一个p ...

  5. bzoj 2733: [HNOI2012]永无乡

    Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以 ...

  6. 如何去除本地文件与svn服务器的关联

    1.每个目录逐个去删除.svn文件夹 .svn属于隐藏文件夹,可通过操纵Windows文件资源管理器使隐藏文件可视,删除该文件,即可. 2.首先建立一个新文件,文件命名为remove-svn-fold ...

  7. css scroll bug

    滚动区域不能设置overflow var doc = $(document), win = $(window), h = $("#head"), b = $("#body ...

  8. Asp.net IIS Express 无法启动 解决办法

    http://www.mamicode.com/info-detail-1893424.html 一 .其他项目都可以,就这么一个不行 用记事本或者其他什么文本编辑器,打开项目的.csproj文件,定 ...

  9. popupwindow那些坑

    1. new PopupWindow(vw, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 如果 ...

  10. asp.net core webapi 服务端配置跨域

    在前后端分离开发中服务端仅仅只为前端提供api接口,并且前后端往往单独部署,此时就会出现浏览器跨域问题.asp.net core提供了简单优雅的解决方案. 在startup文件的Configure添加 ...