前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >简单实现Android APP自动更新

简单实现Android APP自动更新

作者头像
开发者技术前线
发布2020-11-23 15:34:12
2.6K0
发布2020-11-23 15:34:12
举报
文章被收录于专栏:开发者技术前线

一般的安卓app都有自动更新功能,实现app的更新,以让用户体验新版本的功能,这里也是项目中用到的,今天就来总结一下,代码应该有点多,还请耐心点哈。安卓应用实现自动更新比较简单,这里跟大家介绍下:

一般的安卓app都有自动更新功能,实现app的更新,以让用户体验新版本的功能,这里也是项目中用到的,今天就来总结一下,代码应该有点多,还请耐心点哈。安卓应用实现自动更新比较简单,这里跟大家介绍下:

第一步 服务器端: 服务端提供一个借口,或者网址,我这里就用的服务器是tomcat,这里提供一个网址如下://也就是一个json数据接口public static final String UPDATE_URL = "http://192.168.1.103:8080/update.json";

我们来看下json数据参数:{//app名字appname: "爱新闻1.1",//服务器版本号serverVersion: "2",//服务器标志serverFlag: "1",//是否强制更新lastForce: "1",//apk下载地址,这里我已经下载了官方的apk,放到了服务器里面updateurl: "http://192.168.1.103:8080/36Kr.apk",//版本的更新的描述upgradeinfo: "V1.1版本更新,你想不想要试一下哈!!!"}

好了以上的是服务器端的信息,在这里不需要多说了,我们来看下客户端的吧。 第二步 客户端需要实现 首先我们要去解析服务端给的json,那么我们就要来创建一个model类了(代码过多,这里只有字段,getter和setter方法自己创建):

代码语言:javascript
复制
//app名字
   private String appname;
   //服务器版本
   private String serverVersion;
   //服务器标志
   private String serverFlag;
   //强制升级
   private String lastForce;
   //app最新版本地址
   private String updateurl;
   //升级信息
   private String upgradeinfo;

在这里使用了一个辅助类,基本和model字段差不多:

代码语言:javascript
复制
public class UpdateInformation {
   public static String appname = MyApplication.getInstance()
           .getResources().getString(R.string.app_name);
   public static int localVersion = 1;// 本地版本
   public static String versionName = ""; // 本地版本名
   public static int serverVersion = 1;// 服务器版本
   public static int serverFlag = 0;// 服务器标志
   public static int lastForce = 0;// 之前强制升级版本
   public static String updateurl = "";// 升级包获取地址
   public static String upgradeinfo = "";// 升级信息   public static String downloadDir = "wuyinlei";// 下载目录
}

我们知道,我们在进入app的时候,这个时候如果检测到服务器端有了新的版本,就回弹出提示框,提示我们更新。这个我们在MainActivity里面处理逻辑(onCreate()方法里面):

代码语言:javascript
复制
OkhttpManager.getAsync(Config.UPDATE_URL, new OkhttpManager.DataCallBack() {
           @Override
           public void requestFailure(Request request, Exception e) {           }
           @Override
           public void requestSuccess(String result) {
               try {
                   Log.d("wuyiunlei",result);
                   JSONObject object = new JSONObject(result);
                   UpdateInfoModel model = new UpdateInfoModel();
                   model.setAppname(object.getString("appname"));
                   model.setLastForce(object.getString("lastForce"));
                   model.setServerFlag(object.getString("serverFlag"));
                   model.setServerVersion(object.getString("serverVersion"));
                   model.setUpdateurl(object.getString("updateurl"));
                   model.setUpgradeinfo(object.getString("upgradeinfo"));
                   tmpMap.put(DeliverConsts.KEY_APP_UPDATE, model);
               } catch (JSONException e) {
                   e.printStackTrace();
               }
               //发送广播
               sendBroadcast(new Intent(UpdateReceiver.UPDATE_ACTION));
           }
       });

当然了,我们也要注册和结束广播:

代码语言:javascript
复制

   private void registerBroadcast() {
       mUpdateReceiver = new UpdateReceiver(false);
       mIntentFilter = new IntentFilter(UpdateReceiver.UPDATE_ACTION);
       this.registerReceiver(mUpdateReceiver, mIntentFilter);
   }   private void unRegisterBroadcast() {
       try {
           this.unregisterReceiver(mUpdateReceiver);
       } catch (Exception e) {
           e.printStackTrace();
       }
   }

好了,接下来我们看下我们自定义的广播接收者UpdateReceiver .java:

代码语言:javascript
复制
public class UpdateReceiver extends BroadcastReceiver {
   private AlertDialog.Builder mDialog;
   public static final String UPDATE_ACTION = "wuyinlei_aixinwen";
   private SharedPreferencesHelper mSharedPreferencesHelper;
   private boolean isShowDialog;   public UpdateReceiver() {
   }   public UpdateReceiver(boolean isShowDialog) {
       super();
       this.isShowDialog = isShowDialog;
   }   @Override
   public void onReceive(Context context, Intent intent) {
       mSharedPreferencesHelper = mSharedPreferencesHelper
               .getInstance(MyApplication.getInstance());
       //当然了,这里也可以直接new处hashmap
       HashMap<String, Object> tempMap = MyApplication.getInstance()
               .getTempMap();
       UpdateInfoModel model = (UpdateInfoModel) tempMap
               //就是一个标志
               .get(DeliverConsts.KEY_APP_UPDATE);
       try {         
           UpdateInformation.localVersion = MyApplication
                   .getInstance()
                   //包管理独享
                   .getPackageManager()
                   //包信息
                   .getPackageInfo(
                           MyApplication.getInstance()
                                   .getPackageName(), 0).versionCode;
           /**
            * 获取到当前的版本名字
            */
           UpdateInformation.versionName = MyApplication
                   .getInstance()
                   .getPackageManager()
                   .getPackageInfo(
                           MyApplication.getInstance()
                                   .getPackageName(), 0).versionName;
       } catch (Exception e) {
           e.printStackTrace();
       }
       //app名字
       UpdateInformation.appname = MyApplication.getInstance()
               .getResources().getString(R.string.app_name);
       //服务器版本
       UpdateInformation.serverVersion = Integer.parseInt(model
               .getServerVersion());
       //服务器标志
       UpdateInformation.serverFlag = Integer.parseInt(model.getServerFlag());
       //强制升级
       UpdateInformation.lastForce = Integer.parseInt(model.getLastForce());
       //升级地址
       UpdateInformation.updateurl = model.getUpdateurl();
       //升级信息
       UpdateInformation.upgradeinfo = model.getUpgradeinfo();       //检查版本
       checkVersion(context);   }   /**
    * 检查版本更新
    */
   public void checkVersion(Context context) {
       if (UpdateInformation.localVersion < UpdateInformation.serverVersion) {
           // 需要进行更新
           mSharedPreferencesHelper.putIntValue(
                   //有新版本
                   SharedPreferencesTag.IS_HAVE_NEW_VERSION, 1);
           //更新
           update(context);
       } else {
           mSharedPreferencesHelper.putIntValue(
                   SharedPreferencesTag.IS_HAVE_NEW_VERSION, 0);
           if (isShowDialog) {
               //没有最新版本,不用升级
               noNewVersion(context);
           }
           clearUpateFile(context);
       }
   }   /**
    * 进行升级
    */
   private void update(Context context) {
       if (UpdateInformation.serverFlag == 1) {
           // 官方推荐升级
           if (UpdateInformation.localVersion < UpdateInformation.lastForce) {
               //强制升级
               forceUpdate(context);
           } else {
               //正常升级
               normalUpdate(context);
           }       } else if (UpdateInformation.serverFlag == 2) {
           // 官方强制升级
           forceUpdate(context);
       }
   }   /**
    * 没有新版本
    */
   private void noNewVersion(final Context context) {
       mDialog = new AlertDialog.Builder(context);
       mDialog.setTitle("版本更新");
       mDialog.setMessage("当前为最新版本");
       mDialog.setNegativeButton("确定", new DialogInterface.OnClickListener() {           @Override
           public void onClick(DialogInterface dialog, int which) {
               dialog.dismiss();
           }
       }).create().show();
   }   /**
    * 强制升级 ,如果不点击确定升级,直接退出应用
    */
   private void forceUpdate(final Context context) {
       mDialog = new AlertDialog.Builder(context);
       mDialog.setTitle("版本更新");
       mDialog.setMessage(UpdateInformation.upgradeinfo);       mDialog.setPositiveButton("确定", new DialogInterface.OnClickListener() {
           @Override
           public void onClick(DialogInterface dialog, int which) {
               Intent mIntent = new Intent(context, UpdateService.class);
               mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
               mIntent.putExtra("appname", UpdateInformation.appname);
               mIntent.putExtra("appurl", UpdateInformation.updateurl);
               //启动服务
               context.startService(mIntent);
           }
       }).setNegativeButton("退出", new DialogInterface.OnClickListener() {           @Override
           public void onClick(DialogInterface dialog, int which) {
               // 直接退出应用
               //ManagerActivity.getInstance().finishActivity();
               System.exit(0);
           }
       }).setCancelable(false).create().show();
   }   /**
    * 正常升级,用户可以选择是否取消升级
    */
   private void normalUpdate(final Context context) {
       //这里是显示dialog,确定点击升级 ,取消不升级
   }   /**
    * 清理升级文件
    */
   private void clearUpateFile(final Context context) {
       File updateDir;
       File updateFile;
       if (Environment.MEDIA_MOUNTED.equals(Environment
               .getExternalStorageState())) {
           updateDir = new File(Environment.getExternalStorageDirectory(),
                   UpdateInformation.downloadDir);
       } else {
           updateDir = context.getFilesDir();
       }
       updateFile = new File(updateDir.getPath(), context.getResources()
               .getString(R.string.app_name) + ".apk");
       if (updateFile.exists()) {
           Log.d("update", "升级包存在,删除升级包");
           updateFile.delete();
       } else {
           Log.d("update", "升级包不存在,不用删除升级包");
       }
   }
}

最后我们来看下服务吧UpdateService .java:

代码语言:javascript
复制
/**
* 不要忘记注册,在mainfest文件中
*/
public class UpdateService extends Service {
   // BT字节参考量
   private static final float SIZE_BT = 1024L;
   // KB字节参考量
   private static final float SIZE_KB = SIZE_BT * 1024.0f;
   // MB字节参考量
   private static final float SIZE_MB = SIZE_KB * 1024.0f;   private final static int DOWNLOAD_COMPLETE = 1;// 完成
   private final static int DOWNLOAD_NOMEMORY = -1;// 内存异常
   private final static int DOWNLOAD_FAIL = -2;// 失败   private String appName = null;// 应用名字
   private String appUrl = null;// 应用升级地址
   private File updateDir = null;// 文件目录
   private File updateFile = null;// 升级文件   // 通知栏
   private NotificationManager updateNotificationManager = null;
   private Notification updateNotification = null;   private Intent updateIntent = null;// 下载完成
   private PendingIntent updatePendingIntent = null;// 在下载的时候   @Override
   public IBinder onBind(Intent arg0) {
       return null;
   }   @Override
   public void onStart(Intent intent, int startId) {
       super.onStart(intent, startId);
       appName = intent.getStringExtra("appname");
       appUrl = intent.getStringExtra("appurl");
       updateNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
       updateNotification = new Notification();
       //通知图标
       updateNotification.icon = R.mipmap.head;
       //通知信息描述
       updateNotification.tickerText = "正在下载 " + appName;
       updateNotification.when = System.currentTimeMillis();
       updateIntent = new Intent(this, MyApplication.class);
       updatePendingIntent = PendingIntent.getActivity(this, 0, updateIntent,
               0);
       updateNotification.contentIntent = updatePendingIntent;
       updateNotification.contentIntent.cancel();
       updateNotification.contentView = new RemoteViews(getPackageName(),
               //这个布局很简单,就是一个图片和两个textview,分别是正在下载和下载进度
               R.layout.download_notification);
       updateNotification.contentView.setTextViewText(
               R.id.download_notice_name_tv, appName + " 正在下载");
       updateNotification.contentView.setTextViewText(
               R.id.download_notice_speed_tv, "0MB (0%)");
       updateNotificationManager.notify(0, updateNotification);
       new UpdateThread().execute();
   }   /**
    * 在这里使用了asynctask异步任务来下载
    */
   class UpdateThread extends AsyncTask<Void, Void, Integer> {
       @Override
       protected Integer doInBackground(Void... params) {
           return downloadUpdateFile(appUrl);
       }       @Override
       protected void onPostExecute(Integer result) {
           super.onPostExecute(result);           if (result == DOWNLOAD_COMPLETE) {
               Log.d("update", "下载成功");
               String cmd = "chmod 777 " + updateFile.getPath();
               try {
                   Runtime.getRuntime().exec(cmd);
               } catch (IOException e) {
                   e.printStackTrace();
               }
               Uri uri = Uri.fromFile(updateFile);
               //安装程序
               Intent installIntent = new Intent(Intent.ACTION_VIEW);
               installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
               installIntent.setDataAndType(uri,
                       "application/vnd.android.package-archive");
               updatePendingIntent = PendingIntent.getActivity(
                       UpdateService.this, 0, installIntent, 0);
               updateNotification.contentIntent = updatePendingIntent;
               updateNotification.contentView.setTextViewText(
                       R.id.download_notice_speed_tv,
                       getString(R.string.update_notice_finish));
               updateNotification.tickerText = appName + "下载完成";
               updateNotification.when = System.currentTimeMillis();
               updateNotification.defaults = Notification.DEFAULT_SOUND;
               updateNotification.flags |= Notification.FLAG_AUTO_CANCEL;
               updateNotificationManager.notify(0, updateNotification);
               //启动安装程序
               UpdateService.this.startActivity(installIntent);
               stopSelf();
           } else if (result == DOWNLOAD_NOMEMORY) {
               //如果内存有问题
               updateNotification.tickerText = appName + "下载失败";
               updateNotification.when = System.currentTimeMillis();
               updateNotification.contentView.setTextViewText(
                       R.id.download_notice_speed_tv,
                       getString(R.string.update_notice_nomemory));
               updateNotification.flags |= Notification.FLAG_AUTO_CANCEL;
               updateNotification.defaults = Notification.DEFAULT_SOUND;
               updateNotificationManager.notify(0, updateNotification);
               stopSelf();
           } else if (result == DOWNLOAD_FAIL) {
               //下载失败
               updateNotification.tickerText = appName + "下载失败";
               updateNotification.when = System.currentTimeMillis();
               updateNotification.contentView.setTextViewText(
                       R.id.download_notice_speed_tv,
                       getString(R.string.update_notice_error));
               updateNotification.flags |= Notification.FLAG_AUTO_CANCEL;
               updateNotification.defaults = Notification.DEFAULT_SOUND;
               updateNotificationManager.notify(0, updateNotification);
               stopSelf();
           }
       }   }   /**
    * 下载更新程序文件
    * @param downloadUrl   下载地址
    * @return
    */
   private int downloadUpdateFile(String downloadUrl) {
       int count = 0;
       long totalSize = 0;   //总大小
       long downloadSize = 0;   //下载的大小
         
         retrun OktttpUtil.down(url)// 省略
        
   }   /**
    * 可用内存大小
    */
   private boolean MemoryAvailable(long fileSize) {
       fileSize += (1024 << 10);
       if (MemoryStatus.externalMemoryAvailable()) {
           if ((MemoryStatus.getAvailableExternalMemorySize() <= fileSize)) {
               if ((MemoryStatus.getAvailableInternalMemorySize() > fileSize)) {
                   createFile(false);
                   return true;
               } else {
                   return false;
               }
           } else {
               createFile(true);
               return true;
           }
       } else {
           if (MemoryStatus.getAvailableInternalMemorySize() <= fileSize) {
               return false;
           } else {
               createFile(false);
               return true;
           }
       }
   }   /**
    * 获取下载进度
    */
   public static String getMsgSpeed(long downSize, long allSize) {
       StringBuffer sBuf = new StringBuffer();
       sBuf.append(getSize(downSize));
       sBuf.append("/");
       sBuf.append(getSize(allSize));
       sBuf.append(" ");
       sBuf.append(getPercentSize(downSize, allSize));
       return sBuf.toString();
   } 
   /**
    * 获取到当前的下载百分比
    */
   public static String getPercentSize(long downSize, long allSize) {
       String percent = (allSize == 0 ? "0.0" : new DecimalFormat("0.0")
               .format((double) downSize / (double) allSize * 100));
       return "(" + percent + "%)";
   }   /**
    * 创建file文件
    */
   private void createFile(boolean sd_available) {
       if (sd_available) {
           updateDir = new File(Environment.getExternalStorageDirectory(),
                   UpdateInformation.downloadDir);
       } else {
           updateDir = getFilesDir();
       }
       updateFile = new File(updateDir.getPath(), appName + ".apk");
       if (!updateDir.exists()) {
           updateDir.mkdirs();
       }
       if (!updateFile.exists()) {
           try {
               updateFile.createNewFile();
           } catch (IOException e) {
               e.printStackTrace();
           }
       } else {
           updateFile.delete();
           try {
               updateFile.createNewFile();
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }
}

这个时候,可能看到服务怎么这么多代码啊,我头都大了,不要着急,我们一步一步说明一下,这里逻辑很简单,就是在通知栏中,用到了通知,这个时候我们有三种情况,造成了我们好多代码的重复,(你也可以不必考虑那么多情况),还有,里面有了几个工具类,没有提取出来,分别是获取sdcard大小是否可用(创建文件夹),获取当前下载进度,获取应用大小,下载文件,这里也可以使用第三方框架来下载。里面的重要的地方都有注释,如果有疑问,可用互相讨论一下。

这里我们就简单的上几张图看看吧:提示更新图:

更新下载通知:

下载完成后安装图:

最新版应用主界面图(这里我下载的是36kr官方的app,我在应用中心下载好的,嘿嘿):

当然了哈,这里我写的还是有点问题的,每次进入都会提示,如果有必要,也可以实现是否要自动更新,用服务,也就是点击是否自动更新,如果不是自动更新,就不会去触发服务端接口信息,如果是自动更新,就去触发,来获取最新的app版本。

作者:若兰明月

链接:http://www.jianshu.com/p/544eb3291825

來源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关推荐

沉浸式状态栏的三种实现方式

技术 - 思维 - 感悟

END

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-08-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开发者技术前线 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档