安卓app自动更新功能

xiaoxiao2021-02-28  75

app是我们在开发时候经常碰到的事情,一般解决一些bug,添加了需求,实现了新的功能,以让用户体验新版本的功能,这些都是项目中用到的,今天就来总结一下

第一种:引用jjdxmashljjdxm_update

GitHub地址:jjdxmashl/jjdxm_update

这是大神jjdxmashl的开源项目,下载地址见上方。有版本更新、手动更新、静默更新、自动更新4种情况。应用内更新,实现类是友盟自动更新sdk的模式,用户使用前只需要配置自己的服务器更新检查接口即可(必须接口),也可以扩展加入一个接口作为在线参数配置来实现,可以实现下面的4种2更新方式和是否强制更新组合使用,支持get、post方式请求网络,默认是get请求。

4种更新检查类型

手动更新:手动检测更新(所有网络类型环境检测并提示主要用于点击检测使用)自动更新:自动检测更新(所有网络类型环境检测并提示)仅WiFi自动检测更新(只有WiFi网络类型环境检测并提示)静默更新:仅WiFi自动检测下载(只有WiFi网络类型环境检测、下载完才提示)

2种强制更新方式

在更新检查返回后,直接设置update.setForce(true)配合在线参数使用,通过在线参数返回的数据设置UpdateHelper.getInstance().setForced(true)

上述4中更新检查 结合 2种强制更新,适用于:上一个app版本有重大漏洞,修改在线 参数统一控制所有的app用户,不更新就不可以使用app。 

主要原理:服务器上修改参数值,app端获取后进行判断,如果是强制更新,则在打开应用时就提示有新版本的app,更新完成后才可以使用该app;更新为完成,则提示框不消失,点击back键则退出应用。

第二种: 判断VersionCode,xUtils实现下载

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

第二步 客户端需要实现:

首先我们要去解析服务端给的json,那么我们就要来创建一个model类了(代码过多,这里只有字段,getter和setter方法自己创建): //app名字 private String appname; //服务器版本 private String serverVersion; //服务器标志 private String serverFlag; //强制升级 private String lastForce; //app最新版本地址 private String updateurl; //升级信息 private String upgradeinfo; //在这里使用了一个辅助类,基本和model字段差不多: 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()方法里面): 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)); } });

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

/** * 广播注册 */ 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:

/** * 版本更新升级 广播接受者 * */ 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); } /** * 检查版本更新 * * @param 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); } } /** * 进行升级 * * @param 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); } } /** * 没有新版本 * @param 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(); } /** * 强制升级 ,如果不点击确定升级,直接退出应用 * * @param context */ 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(); } /** * 正常升级,用户可以选择是否取消升级 * * @param context */ private void normalUpdate(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) { dialog.dismiss(); } }).create().show(); } /** * 清理升级文件 * * @param context */ 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:

/** * 不要忘记注册,在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; //下载的大小 URI uri = null; //这个已经舍弃了,要用的话,就要加上org.apache.http.legacy.jar这个jar包 HttpGet httpGet = null; try { uri = new URI(downloadUrl); httpGet = new HttpGet(uri); } catch (URISyntaxException e) { String encodedUrl = downloadUrl.replace(' ', '+'); httpGet = new HttpGet(encodedUrl); e.printStackTrace(); } HttpClient httpClient = new DefaultHttpClient(); HttpResponse httpResponse = null; FileOutputStream fos = null; InputStream is = null; try { httpResponse = httpClient.execute(httpGet); if (httpResponse != null) { int stateCode = httpResponse.getStatusLine().getStatusCode(); if (stateCode == HttpStatus.SC_OK) { HttpEntity entity = httpResponse.getEntity(); if (entity != null) { totalSize = entity.getContentLength(); //如果内存可用 if (MemoryAvailable(totalSize)) { is = entity.getContent(); if (is != null) { fos = new FileOutputStream(updateFile, false); byte buffer[] = new byte[4096]; int readsize = 0; while ((readsize = is.read(buffer)) > 0) { fos.write(buffer, 0, readsize); downloadSize += readsize; if ((count == 0) || (int) (downloadSize * 100 / totalSize) >= count) { count += 5; updateNotification.contentView .setTextViewText( R.id.download_notice_speed_tv, getMsgSpeed(downloadSize,totalSize)); updateNotificationManager.notify(0, updateNotification); } } fos.flush(); if (totalSize >= downloadSize) { return DOWNLOAD_COMPLETE; } else { return DOWNLOAD_FAIL; } } } else { if (httpGet != null) { httpGet.abort(); } return DOWNLOAD_NOMEMORY; } } } } } catch (Exception e) { e.printStackTrace(); } finally { try { if (fos != null) { fos.close(); } if (is != null) { is.close(); } } catch (IOException e) { e.printStackTrace(); } if (httpClient != null) { httpClient.getConnectionManager().shutdown(); } } return DOWNLOAD_FAIL; } /** * 可用内存大小 * @param fileSize * @return */ 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; } } } /** * 获取下载进度 * @param downSize * @param allSize * @return */ 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(); } /** * 获取大小 * @param size * @return */ public static String getSize(long size) { if (size >= 0 && size < SIZE_BT) { return (double) (Math.round(size * 10) / 10.0) + "B"; } else if (size >= SIZE_BT && size < SIZE_KB) { return (double) (Math.round((size / SIZE_BT) * 10) / 10.0) + "KB"; } else if (size >= SIZE_KB && size < SIZE_MB) { return (double) (Math.round((size / SIZE_KB) * 10) / 10.0) + "MB"; } return ""; } /** * 获取到当前的下载百分比 * @param downSize 下载大小 * @param allSize 总共大小 * @return */ 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文件 * @param sd_available sdcard是否可用 */ 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大小是否可用(创建文件夹),获取当前下载进度,获取应用大小,下载文件,这里也可以使用第三方框架来下载。 当然了哈,这里我写的还是有点问题的,每次进入都会提示,如果有必要,也可以实现是否要自动更新,用服务,也就是点击是否自动更新,如果不是自动更新,就不会去触发服务端接口信息,如果是自动更新,就去触发,来获取最新的app版本。
转载请注明原文地址: https://www.6miu.com/read-21643.html

最新回复(0)