开发中对版本进行检查并更新的需求基本是所有利用必须有的功能,可是在实际开发中有些朋友就容易疏忽1些细节。
1般是将本地版本告知服务器,服务器经过相干处理睬返回客户端相干信息,告知客户端需不需要更新,如果需要更新是强迫更新还是非强迫更新。客户端得到服务器返回的相干信息后再进1步做逻辑处理。
1般的处理就是进入利用就弹窗通知用户有版本更新,弹窗可以没有取消按钮其实不能取消。这样用户就只能选择更新或关闭利用了,固然也能够添加取消按钮,但是如果用户选择取消则直接退出利用。
1般的处理是在利用的设置中添加版本检查的操作,如果用户主动检查版本则弹窗告知用户有版本更新。这时候用户可以取消或更新。
功能实际是比较简单清晰的,但之所以写这篇文章,是由于在我们公司的1个项目中,我把这个模块分给了1个有着4年工作经验的哥们编写,最后这哥们花了2个小时做完了。我还想这哥们写得挺快,效力很高嘛,结果1测试发现问题很多:
1. 进入首页前关闭网络,进入后刷新界面发现强迫更新提示没有弹窗
2. 再进入其它界面也没有任何更新提示
3. 在正常更新时点击肯定更新,没有判断网络状态(wifi,移动网络)直接下载apk文件,如果用户在移动网络下将耗费非常多的流量,直接影响用户体验
4. 下载进程在利用内没有进度条提示,通知栏也没有进度提示
5. apk文件下载进程中,如果强迫结束利用,下载被中断
6. apk如果正常下载下来,弹出了安装界面,这时候如果用户取消了安装回到利用,在需要强迫更新的情况下并没有再次弹窗禁止用户进行任何其它操作,失去了强迫更新的意义
首先声明下,我这丝毫没有吐槽的意思哟,只是想说作为1个合格的程序员大家最最少需要做到思惟严谨这点,在有能力的情况下对用户体验能提点建议最好。自己写的代码1定要经过严格测试再交付,不要期望测试人员帮你测试再去修改,你要知道现在很多公司是没有专业的测试人员乃至是没有测试人员的哟。
解决方案
每一个界面都需要检查更新,固然我们不能在每一个Activity中都复制粘贴1样的代码。这时候定义1个BaseActivity,所有其它Activity都从它继承就显得很有价值了。可以把检查更新的操作放到BaseActivity的相干方法中,比如放在onResume中,这样每当显示1个界面时都将履行检查更新的操作
对5问题,如果把下载的操作放在了Activity中进行,如果利用意外终止或强迫退出利用,则下载线程也将被终止
解决方案
可以将下载任务放到Service中履行,这样即便利用被终止Service1样有保活机制(startForeground)让Service的任务有很大的机会继续得以履行
对6问题,如果检查更新的操作没有在Activity的resume时再次履行,则回到Activity自然也就没有检查更新并弹窗了
解决方案
在Activity的onResume中继续检查更新,如果是强迫更新则弹窗禁止用户进行其它操作
对3,4问题,我倒是觉得不是程序问题而是态度问题,实际加入非wifi和进度显示的功能非常简单
核心代码以下:
public class VersionUpdateService extends Service {
private LocalBinder binder = new LocalBinder();
private DownLoadListener downLoadListener;//下载任务监听回调接口
private boolean downLoading;
private int progress;
private NotificationManager mNotificationManager;
private NotificationUpdaterThread notificationUpdaterThread;
private Notification.Builder notificationBuilder;
private final int NOTIFICATION_ID = 100;
private VersionUpdateModel versionUpdateModel;
private CheckVersionCallBack checkVersionCallBack;//检查结果监听回调接口
public interface DownLoadListener {
void begain();
void inProgress(float progress, long total);
void downLoadLatestSuccess(File file);
void downLoadLatestFailed();
}
public interface CheckVersionCallBack {
void onSuccess();
void onError();
}
...
private class NotificationUpdaterThread extends Thread {
@Override
public void run() {
while (true) {
notificationBuilder.setContentTitle("正在下载更新" + progress + "%"); // the label of the entry
notificationBuilder.setProgress(100, progress, false);
...
}
}
}
private void starDownLoadForground() {
//创建通知栏
notificationBuilder = new Notification.Builder(this);
...
Notification notification = notificationBuilder.getNotification();
startForeground(NOTIFICATION_ID, notification);
}
private void stopDownLoadForground() {
stopForeground(true);
}
//履行版本检查任务
public void doCheckUpdateTask() {
//获得本定版本号
final int currentBuild = AppUtil.getVersionCode(this);
//调用版本检查接口
ApiManager.getInstance().versionApi.upgradeRecords(currentBuild, new RequestCallBack() {
@Override
public void onSuccess(Headers headers, String response) {
versionUpdateModel = JSON.parseObject(response, VersionUpdateModel.class);
...
if (checkVersionCallBack != null)
checkVersionCallBack.onSuccess();
}
@Override
public void onError(int code, String response) {
...
}
});
}
public void doDownLoadTask() {
starDownLoadForground();
//启动通知栏进度更新线程
notificationUpdaterThread = new NotificationUpdaterThread();
notificationUpdaterThread.start();
//文件下载寄存路径
final File fileDir = FolderUtil.getDownloadCacheFolder();
...
downLoading = true;
if (downLoadListener != null) {
downLoadListener.begain();
}
NetManager.getInstance().download(url, fileDir.getAbsolutePath(), new DownloadCallBack() {
@Override
public void inProgress(float progress_, long total) {
...
//履行进度更新
if (downLoadListener != null)
downLoadListener.inProgress(progress_, total);
}
@Override
public void onSuccess(Headers headers, String response) {
//履行成功回调
...
installApk(destFile, VersionUpdateService.this);
}
@Override
public void onError(int code, String response) {
...
//履行失败回调
}
});
}
//安装apk
public void installApk(File file, Context context) {
...
}
}
public class VersionUpdateHelper implements ServiceConnection {
private Context context;
private VersionUpdateService service;
private AlertDialog waitForUpdateDialog;
private ProgressDialog progressDialog;
private static boolean isCanceled;
private boolean showDialogOnStart;
public static final int NEED_UPDATE = 2;
public static final int DONOT_NEED_UPDATE = 1;
public static final int CHECK_FAILD = -1;
public static final int USER_CANCELED = 0;
private CheckCallBack checkCallBack;
public interface CheckCallBack{
void callBack(int code);
}
public VersionUpdateHelper(Context context) {
this.context = context;
}
public void startUpdateVersion() {
if (isCanceled)
return;
if (isWaitForUpdate() || isWaitForDownload()) {
return;
}
if (service == null && context != null) {
context.bindService(new Intent(context, VersionUpdateService.class), this, Context.BIND_AUTO_CREATE);
}
}
public void stopUpdateVersion() {
unBindService();
}
private void cancel() {
isCanceled = true;
unBindService();
}
private void unBindService() {
if (isWaitForUpdate() || isWaitForDownload()) {
return;
}
if (service != null && !service.isDownLoading()) {
context.unbindService(this);
service = null;
}
}
...
private void showNotWifiDownloadDialog() {
final AlertDialog.Builder builer = new AlertDialog.Builder(context);
builer.setTitle("下载新版本");
builer.setMessage("检查到您的网络处于非wifi状态,下载新版本将消耗1定的流量,是不是继续下载?");
builer.setNegativeButton("以后再说", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
...
//如果是强迫更新 exit app
if (mustUpdate) {
MainApplication.getInstance().exitApp();
}
}
});
builer.setPositiveButton("继续下载", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
service.doDownLoadTask();
}
});
...
}
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
service = ((VersionUpdateService.LocalBinder) binder).getService();
service.setCheckVersionCallBack(new VersionUpdateService.CheckVersionCallBack() {
@Override
public void onSuccess() {
VersionUpdateModel versionUpdateModel = service.getVersionUpdateModel();
//EventBus控制更新红点提示
EventBus.getDefault().postSticky(versionUpdateEvent);
if (!versionUpdateModel.isNeedUpgrade()) {
if(checkCallBack != null){
checkCallBack.callBack(DONOT_NEED_UPDATE);
}
cancel();
return;
}
if (!versionUpdateModel.isMustUpgrade() && !showDialogOnStart) {
cancel();
return;
}
if(checkCallBack != null){
checkCallBack.callBack(NEED_UPDATE);
}
final AlertDialog.Builder builer = ...//更新提示对话框
builer.setPositiveButton("立即更新", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
if (NetUtil.isWifi(context)) {
service.doDownLoadTask();
} else {
showNotWifiDownloadDialog();
}
}
});
//当点取消按钮时进行登录
if (!versionUpdateModel.isMustUpgrade()) {
builer.setNegativeButton("稍后更新", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
cancel();
if(checkCallBack != null){
checkCallBack.callBack(USER_CANCELED);
}
}
});
}
builer.setCancelable(false);
waitForUpdateDialog = builer.create();
waitForUpdateDialog.show();
}
@Override
public void onError() {
unBindService();
...
}
});
service.setDownLoadListener(new VersionUpdateService.DownLoadListener() {
@Override
public void begain() {
VersionUpdateModel versionUpdateModel = service.getVersionUpdateModel();
if (versionUpdateModel.isMustUpgrade()) {
progressDialog = ...//生成进度条对话框
}
}
@Override
public void inProgress(float progress, long total) {
...//更新进度条
}
@Override
public void downLoadLatestSuccess(File file) {
...//履行成功处理
unBindService();
}
@Override
public void downLoadLatestFailed() {
...//履行失败处理
unBindService();
}
});
service.doCheckUpdateTask();
}
...
}
最后,使用方式还是非常简单的。在BaseActivity中使用:
private VersionUpdateHelper versionUpdateHelper;
@Override
protected void onResume() {
super.onResume();
if(versionUpdateHelper == null)
versionUpdateHelper = new VersionUpdateHelper(this);
versionUpdateHelper.startUpdateVersion();
}
@Override
protected void onPause() {
super.onPause();
if(versionUpdateHelper != null)
versionUpdateHelper.stopUpdateVersion();
}
保证在每进入1个界面和离开界面时都将检查更新(bindService)和取消检查(unBindService)。这时候有些朋友可能认为这样做会不会浪费资源呢?没有!
1,如果利用是强迫更新,那末在网络正常情况下进入利用就可以检查出有新版本,这时候弹窗后用户不能进入任何操作,没有机会进入别的界面,所有无进行重复检查;如果进入利用主页由于网络问题,检查失败,这时候虽然不会弹窗提示更新,但是如果用户的网络恢复落后入任何其它界面都将得到正常的版本更新检查并弹窗提示
2,如果利用是非强迫更新时,在Helper代码里进行了以下的判断:
if (!versionUpdateModel.isMustUpgrade() && !showDialogOnStart) {
cancel();
return;
}
这里的showDialogOnStart默许为false,也就是说如果不是强迫更新则检查成功后就当“取消”处理,并在cancel方法中将变量isCanceled修改成true,这样如果有新的要求想要履行startUpdateVersion()都将被疏忽,注意isCanceled是static全局的。
如果想实现在设置中由用户手动检查更新,则只需履行类似以下代码:
SettingActivity.java
private VersionUpdateHelper versionUpdateHelper;
@OnClick(R.id.rl_version_update)
public void onClickVersionUpdate(View view) {
if(updateTips.getVisibility() == View.VISIBLE){
return;
}
VersionUpdateHelper.resetCancelFlag();//重置cancel标记
if (versionUpdateHelper == null) {
versionUpdateHelper = new VersionUpdateHelper(this);
versionUpdateHelper.setShowDialogOnStart(true);
versionUpdateHelper.setCheckCallBack(new VersionUpdateHelper.CheckCallBack() {
@Override
public void callBack(int code) {
//EventBus发送消息通知红点消失
VersionUpdateEvent versionUpdateEvent = new VersionUpdateEvent();
versionUpdateEvent.setShowTips(false);
EventBus.getDefault().postSticky(versionUpdateEvent);
}
});
}
versionUpdateHelper.startUpdateVersion();
}
由于代码较多,且多数代码和ui相干,所以在文章中很多ui相干或getter和setter方法等非核心代码并没有列出。演示代码中用了EventBus和OkHttp开源控件,具体使用方法望大家自己找相干资料学习。本人打算有空的时候写个EventBus系列文章,望大家多多关注。
文件下载也是使用的okHttp实现的,大家可以换成任何你熟习的下载框架。VersionUpdateService.java和VersionUpdateHelper.java的完全代码可以到我的github上下载,由于时间关系并没有相干用法的完全案例还望见谅,等有时间1定奉上。
如果有任何问题可以在评论中加以发问,谢谢~~