转载请标明出处:1片枫叶的专栏
上1篇文章中我们讲授了android app中的轮询操作,讲授的内容主要包括:我们在App中使用轮询操作的情形,作用和实现方式等。1般而言我们使用轮询操作都是通过定时任务的情势要求服务器并更新用户界面,轮询操作都有1定的使用生命周期,即在1定的页面中启动轮询操作,然后在特定的情况下关闭轮询操作,这点需要我们尤其注意,我们还介绍了使用Timer和Handler实现轮询操作的实例,更多关于App中轮询操作的信息,可参考我的:android产品研发(103)–>App轮询操作
本文将讲授app的升级与更新。1般而言用户使用App的时候升级提示有两种方式取得:
1种是通过App Store获得
1种是打开利用以后提示用户更新升级
而更新操作1般是在用户点击了升级按钮以后开始履行的,这里的升级操作也分为两种情势:
1般升级
强迫升级
app升级操作:
在App Store中升级需要为App Store上传新版App,我们在新版本完成以后都会上传到App Store中,不同的利用市场审核的时间不同,1般除第1次上传时间较长以外,其余的审核都是挺快的,1般不会超过半天(不排除例外情况奥),在审核完成以后就相当于完成了这个利用市场的发布了,也就是发布上线了。这时候候如果用户安装了这个利用市场,那末就可以看到我们的App有新版本的升级提示了。
除可以在利用市场升级,我们还可以在利用内升级,在利用内升级主要是通过调用服务器端接口获得利用的升级信息,然后通过获得的服务器升级利用信息与本地的App版本比对,若服务器下发的最新的App版本高于本地的版本号,则说明有新版本发布,那末我们就能够履行更新操作了,否则疏忽掉便可。
利用内升级其实已有好多第3方的SDK了,常见的友盟,百度App开发工具包都已集成了升级的功能,部份SDK厂商还提供增量更新的功能。增量更新的内容不是我们这里的讨论重点,想了解更多增量更新的内容可参考:浅谈Android增量升级
这里我们先简单介绍1下友盟的App升级功能,友盟其实已有了App升级的API,我们只需要简单的调用便可。
/**
* 要求友盟更新API,判断是不是弹出更新弹窗
*/
public static void updateVersion(final Activity mContext, final MainActivity.UpdateCallback updateCallback, final boolean isShow) {
UmengUpdateAgent.setUpdateListener(new UmengUpdateListener() {
@Override
public void onUpdateReturned(int updateStatus, UpdateResponse updateInfo) {
switch (updateStatus) {
//判断是不是有新版本需要更新
case UpdateStatus.Yes: // has update
try {
//在线读取更新参数
String value = MobclickAgent.getConfigParams(mContext, "FORCE_UPDATE_MIXVERSION");
if (value != null && !value.trim().equals("")) {
int versionCode = Config.changeVersionNameToCode(value);
if (versionCode != 0) {
String localVersionName = getVersionName(mContext);
int localVersionCode = Config.changeVersionNameToCode(localVersionName);
//判断当前版本号于友盟中的最低版本号,若当前版本号小于最低版本号,则强迫更新,否则非强迫更新
if (localVersionCode <= versionCode) {
// 弹窗更新弹窗
updateCallback.onUpdateSuccess(updateInfo);
} else {
UmengUpdateAgent.setUpdateAutoPopup(true);
UmengUpdateAgent.showUpdateDialog(mContext, updateInfo);
}
} else {
UmengUpdateAgent.setUpdateAutoPopup(true);
UmengUpdateAgent.showUpdateDialog(mContext, updateInfo);
}
} else {
UmengUpdateAgent.setUpdateAutoPopup(true);
UmengUpdateAgent.showUpdateDialog(mContext, updateInfo);
}
} catch (Exception e) {
e.printStackTrace();
}
break;
case UpdateStatus.No: // has no update
if (isShow) {
Config.showToast(mContext, "您当前使用的友友用车已经是最新版本");
}
break;
}
}
});
UmengUpdateAgent.setUpdateAutoPopup(false);
UmengUpdateAgent.forceUpdate(mContext);
UmengUpdateAgent.setChannel(ChannelUtil.getChannel(mContext));
}
以上是友盟的升级API,在调用之前需要先继承友盟的SDK,这样经过调用以后我们就能够通过友盟实现更新接口的提示功能了,默许的友盟提供了静默安装,更新提示弹窗,强迫更新等几种,可以根据本身App的需求来肯定更新的方式。
如果不喜欢使用第3方的更新方式,我们也能够通过调用服务器接口的方式实现自己的更新弹窗提示,主要的逻辑也是通过判断服务器下发的最新App版本号与本地版本号对照,若服务器真个App版本号大于本地的App版本号,则说明当前App不是最新的版本,需要升级,这里我们简单看1下友友用车中自定义的更新接口实现:
/**
* 检测App是不是需要更新
*
* @param mContext
* @param isShow 若不需要更新是不是需要弹出文案
*/
public static void queryAppBaseVersionInfo(final Activity mContext, final boolean isOneUpdate, final boolean isShow) {
try {
// 若当前网络异常,则直接return
if (!Config.isNetworkConnected(mContext)) {
// 关闭进度条
dismissProgress(isShow);
return;
}
// 控制变量,App更新接口进程生命周期中只会调用1次
if (isQueryAppUpdated && isOneUpdate) {
return;
}
L.i("开始调用要求是不是需要版本更新的接口....");
ExtInterface.QueryAppBaseVersionInfoNL.Request.Builder request = ExtInterface.QueryAppBaseVersionInfoNL.Request.newBuilder();
request.setClientChannel(CHANNEL_ANDROID);
// 查询最新的版本信息,不需要传入版本号
// request.setVersionCode(VersionUtils.getVersionName(mContext));
NetworkTask task = new NetworkTask(Cmd.CmdCode.QueryAppBaseVersionInfo_VALUE);
task.setBusiData(request.build().toByteArray());
NetworkUtils.executeNetwork(task, new HttpResponse.NetWorkResponse<UUResponseData>() {
@Override
public void onSuccessResponse(UUResponseData responseData) {
if (responseData.getRet() == 0) {
try {
isQueryAppUpdated = true;
ExtInterface.QueryAppBaseVersionInfoNL.Response response = ExtInterface.QueryAppBaseVersionInfoNL.Response.parseFrom(responseData.getBusiData());
if (response.getRet() == 0) {
L.i("要求检测App是不是更新接口成功,开始解析返回结果");
// 解析检测结果
parserUpdateResule(mContext, response, isShow);
} else {
if (isShow) {
showDefaultNetworkSnackBar(mContext);
}
}
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
if (isShow) {
showDefaultNetworkSnackBar(mContext);
}
}
}
}
@Override
public void onError(VolleyError errorResponse) {
L.e("要求检测更新接口失败....");
if (isShow) {
showDefaultNetworkSnackBar(mContext);
}
}
@Override
public void networkFinish() {
L.i("要求检测更新接口完成....");
// 关闭进度条
dismissProgress(isShow);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
该接口只会在App打开时调用1次,判断App是不是需要更新,然后在要求服务器成功以后,会解析要求结果,我们继续看1下我们的解析逻辑:
/**
* 解析更新检查结果
*
* @param response
*/
private static void parserUpdateResule(Activity mContext, ExtInterface.QueryAppBaseVersionInfoNL.Response response, boolean isShow) {
if (mContext == null) {
return;
}
// 判断是不是需要更新
ExtInterface.AppBaseVersionInfo appBaseVersionInfo = response.getAppBaseVersionInfo();
// 若当前更新是不是有效
if (appBaseVersionInfo.getIsDel() == ENEFFECT) {
return;
}
String updateVersionCode = appBaseVersionInfo.getVersionCode();
int updateCode = changeVersionNameToCode(updateVersionCode);
int localCode = changeVersionNameToCode(VersionUtils.getVersionName(mContext));
// 本地利用版本号小于更新的利用版本号,则需要更新
L.i("本地版本号:" + localCode + " " + VersionUtils.getVersionName(mContext) + " 远程版本号:" + updateCode
+ " " + updateVersionCode);
if (localCode < updateCode) {
// 显示更新文案
L.i("开始显示更新弹窗...");
showUpdateDialog(mContext, appBaseVersionInfo);
}
// 不需要更新
else {
if (isShow) {
Config.showToast(mContext, mContext.getResources().getString(R.string.about_new));
}
}
}
解析更新接口信息的时候,会判断App的更新操作是普通更新还是强迫更新,若是强迫更新的话,则没有取消按钮,并且更新弹窗不可关闭。若是普通的更新的话则有暂不更新按钮,点击暂不更新更新弹窗会取消,但是当下次打开App的时候,弹窗提示还是会弹窗。
普通更新包括暂不更新和立即更新两个按钮操作:
强迫更新只有立即更新按钮,弹窗不可取消:
app更新操作:
app的更新操作就是下载App并安装了,下面我们还是分两部份看,利用市场的更新与利用内更新
在利用市场中更新App很简单就是履行简单的下载操作,然后顺着App的提示,1步步安装便可,这里没有甚么需要注意的地方。
利用内更新操作主要是当用户点击了更新按钮以后履行的,下载,安装等逻辑,下面我们看1下友友用车利用内更新的实践。
利用内更新主要包括了:普通更新和强迫更新两种,其中普通更新弹窗可以选择更新也能够选择疏忽,而强迫更新只能选择更新,并且更新弹窗不可取消。
下面的代码是履行下载操作的核心逻辑:
/**
* 开始履行下载动作
*/
private static void doDownLoad(final Activity mContext, String downloadUrl, final String actionButtonMsg, final boolean isFocusUpdate) {
// 强迫更新
if (isFocusUpdate) {
DownLoadDialog.updateRela.setVisibility(View.VISIBLE);
DownLoadDialog.progressBar.setProgress(0);
DownLoadDialog.progressBar.start();
DownLoadDialog.updatePercent.setText("0%");
DownLoadDialog.materialDialog.getPositiveButton().setEnabled(false);
DownLoadDialog.materialDialog.getPositiveButton().setText("下载中");
}
Config.showToast(mContext, "开始下载安装包.......");
// 删除下载的apk文件
doDeleteDownApk(mContext);
L.i("安装包下载地址:" + downloadUrl);
DownloadManager.getInstance().cancelAll();
DownloadManager.downloadId = DownloadManager.getInstance().add(DownloadManager.getDownLoadRequest(mContext, downloadUrl, new DownloadStatusListenerV1() {
@Override
public void onDownloadComplete(DownloadRequest downloadRequest) {
L.i("onDownloadComplete_____...");
// 设置按钮是不是可点击
showPositiveText(false, actionButtonMsg);
if (isFocusUpdate) {
// 更新进度条显示
DownLoadDialog.updatePercent.setText("100%");
DownLoadDialog.progressBar.stop();
} else {
String title = "正在下载友友用车...";
String content = "下载成功";
DownloadNotification.showNotification(mContext, title, content, DownloadNotification.notofyId);
// 关闭通知栏消息
UUApp.notificationManager.cancel(DownloadNotification.notofyId);
}
// 下载完成,履行安装逻辑
doInstallApk(mContext);
// 退出App
UUApp.getInstance().exit();
}
@Override
public void onDownloadFailed(DownloadRequest downloadRequest, int errorCode, String errorMessage) {
L.i("onDownloadFiled______...");
L.i("errorMessage:" + errorMessage);
// 设置按钮是不是可点击
showPositiveText(false, actionButtonMsg);
if (isFocusUpdate) {
// DownLoadDialog.progressBar.stop();
DownLoadDialog.updatePercent.setText("更新失败");
} else {
String title = "正在下载友友用车...";
String content = "下载失败";
DownloadNotification.showNotification(mContext, title, content, DownloadNotification.notofyId);
}
}
@Override
public void onProgress(DownloadRequest downloadRequest, long totalBytes, long downloadedBytes, int progress) {
if (lastProgress != progress) {
lastProgress = progress;
L.i("onProgress_____progress:" + progress + " totalBytes:" + totalBytes + " downloadedBytes:" + downloadedBytes);
// 设置按钮是不是可点击
showPositiveText(true, actionButtonMsg);
// 强迫更新则更新进度条
if (isFocusUpdate) {
String content = downloadedBytes * 100 / totalBytes + "%";
float result = progress / (float)100.00;
DownLoadDialog.progressBar.setProgress(result);
DownLoadDialog.updatePercent.setText(content);
} else {
String title = "正在下载友友用车...";
String content = downloadedBytes * 100 / totalBytes + "%";
DownloadNotification.showNotification(mContext, title, content, DownloadNotification.notofyId);
}
}
}
}));
}
这里的下载操作包括了3个回调方法:
onDownloadComplete()
onDownloadFailed()
onProgress()
其中onDownlaodComplete方法在下载完成时回调,onDownloadFailed方法在下载失败是回调,而onProgress方法则用于刷新下载进程,我们在onProcess方法中更新通知栏下载进度,具体我们可以看1下更新通知栏消息的方法:
/**
* 更新通知栏显示
* @param title
* @param content
* @param notifyId
*/
public static void showNotification(Activity mContext, String title, String content, int notifyId) {
NotificationCompat.Builder mNotifyBuilder = new NotificationCompat.Builder(mContext)
.setSmallIcon(R.mipmap.icon)
.setContentTitle(title)
.setContentText(content)
.setSmallIcon(android.R.drawable.stat_sys_download);
Notification notification = mNotifyBuilder.build();
// notification.flags = Notification.FLAG_NO_CLEAR;
UUApp.notificationManager.notify(notifyId, notification);
}
而在onDownloadFailed方法中,履行的代码逻辑是提示用户下载失败,
而在onDownloadComplete方法中,履行安装下载apk文件的操作,我们可以继续看1下我们是如何履行安装逻辑的。
/**
* 履行安装apk文件
*/
private static void doInstallApk(Activity mContext) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.fromFile(new File(DownloadManager.getApkPath(mContext))),
"application/vnd.android.package-archive");
mContext.startActivity(intent);
}
这段代码会调用android的安装apk程序,这样我们就履行了下载文件的安装操作,不同的手机安装程序及展现界面略有不同。
总结:
App升级操作分为两种,在利用市场提示升级和在利用内提示升级,而在利用内提示升级可以继承第3方升级API(如:友盟),也能够自己实现;
利用升级的提示主要逻辑是根据服务器真个APK版本号与本地的利用版本号对照,若服务器真个利用版本号高于本地版本号,则说明利用需要升级;
利用升级可以分为普通升级和强迫升级两种,1般不太建议使用强迫升级(用户体验很差),除非是1些严重的线上bug;
App的更新操作包括下载与安装两部份,下载操作时可以选择继承第3方服务,也能够自己实现。
另外对产品研发技术,技能,实践方面感兴趣的同学可以参考我的:
android产品研发(1)–>实用开发规范
android产品研发(2)–>启动页优化
android产品研发(3)–>基类Activity
android产品研发(4)–>减小Apk大小
android产品研发(5)–>多渠道打包
android产品研发(6)–>Apk混淆
android产品研发(7)–>Apk热修复
android产品研发(8)–>App数据统计
android产品研发(9)–>App网络传输协议
android产品研发(10)–>不使用静态变量保存数据
android产品研发(101)–>利用内跳转scheme协议
android产品研发(102)–>App长连接实现
android产品研发(103)–>App轮询操作
本文以同步至github中:https://github.com/yipianfengye/androidProject,欢迎star和follow
上一篇 二叉搜索树的第k个结点