android的开发有时候让人很蛋疼,比如说app的更新,像ios的更新都被app store包办了方便快捷,但是国内的安卓市场却五花八门,途径各种各样。不要太期望像苹果那样把更新全权交个某夹或者某0,app内都要自己继承一个自动更新的功能。那我们就要自己实现一个。

效果图如下:

android app的自动更新 安卓app自动更新功能开发_ide

android app的自动更新 安卓app自动更新功能开发_ide_02

更新分成2种,一种是普通更新(如上图1)用户可以选择不更新,一种是强制更新(如上图2),新版本已经不再向下兼容,更新时会锁定屏幕,用户要么更新要么退出。

这里我把代码分享一下,更新功能都集成到了一个library中方便调用,下面先说下使用方法:

使用方法:

首先下载更新功能库library-version,导入工程后关联到自己项目:

library-version下载链接
然后在自己项目中需要检查更新的地方添加如下代码:
UpdateHelper updateHelper = new UpdateHelper.Builder(this)
.checkUrl(url)
.isAutoInstall(false) //设置为false需在下载完手动点击安装,默认值为true,下载后自动安装。
.build();
updateHelper.check();
如果你有复杂的需求,需要在更新过程中加入自己的处理逻辑,那么可以使用下面的接口:
UpdateHelper updateHelper = new UpdateHelper.Builder(this)
.checkUrl(url)
.isAutoInstall(false) //设置为false需在下载完手动点击安装,默认值为true,下载后自动安装。
.build();
updateHelper.check(new OnUpdateListener() {
@Override
public void onStartCheck() {
}
@Override
public void onFinishCheck(UpdateInfo info) {
}
@Override
public void onStartDownload() {
}
@Override
public void onInstallApk() {
}
@Override
public void onFinshDownload() {
}
@Override
public void onDownloading(int progress) {
}
});
有几点需要注意:
第一点:其中isAutoInstall(false)只适用于普通更新,强制更新一律自动安装。
第二点:其中url是你的apk的下载接口,你需要提供一个如下的接口:
{
"status":1,
"isForce":1,
"apkUrl":"http://dl.m.duoku.com/game/49000/49554/20150206145821_DuoKu.apk",
"versionCode":14,
"updateTips":"update1.1.7.2",
"versionName":"1.1.7.2",
"changeLog":"1.新增了用户信息的采集
2.修复了路径规划节点超过1000个异常崩溃的bug","size":"25M"}
参数说明:
参数名
类型
说明
是否必选
status
int
请求是否成功(1成功其他否)
是
isForce
int
是否强制更新(1是0否)
是
apkUrl
String
下载url
是
versionCode
int
版本号
是
updateTips
String
更新标题
否
versionName
String
版本名
是
changeLog
String
更新说明
是
size
String
包大小
是
其中版本号versionCode是判断是否需要更新的关键,它被设置在你的app的AndroidManifest.xml文件中:
package="com.ccrv.cber"
android:versionCode="13"
android:versionName="1.1.7.1" >
而版本名versionName只是用于给用户作展示用,它并不参与到逻辑中。这是android的版本管理规范。
提供了以上的接口功能集成就完成了,很方便吧。
下面解释下主要代码:不看实现原理的可以跳过了
先看下目录结构:
解释下每个包中的类是用来干嘛的:
UpdateHelper:更新功能的主流程,程序的入口
VersionDialog:复用的几种的弹出对话
OnUpdateListener:更新过程的回调接口,如果有回调需要则需要自己实现
UpdateInfo:版本信息的实体类
com.shelwee.update.utils包下则都是些网络相关的工具类
library的主要处理流程都集中在UpdateHelper类中,在检查更新的时候首先调用了check方法:
public void check(OnUpdateListener listener) {
initButtonReceiver();
if (listener != null) {
this.updateListener = listener;
}
if (mContext == null) {
Log.e("NullPointerException", "The context must not be null.");
return;
}
AsyncCheck asyncCheck = new AsyncCheck();
asyncCheck.execute(checkUrl);
}
在check方法内部启动异步任务AsyncCheck去我们提供的接口获取服务器目前app信息,
通过JSONHandler类来获取返回信息并封装成UpdateInfo实体对象:
@Override
protected UpdateInfo doInBackground(String... params) {
...
updateInfo = JSONHandler.toUpdateInfo(HttpRequest.get(url));
...
return updateInfo;
}
在onPostExecute回调方法中,比较服务器最新版本和本地版本号,如果不是最新,则调用showUpdateUI(updateInfo)方法:
protected void onPostExecute(UpdateInfo updateInfo) {
if (mContext != null && updateInfo != null) {
if (Integer.parseInt(updateInfo.getVersionCode()) > getPackageInfo().versionCode) {
showUpdateUI(updateInfo);
...
} else {
Toast.makeText(mContext, "当前已是最新版", Toast.LENGTH_LONG).show();
...
}
}
}
在showUpdateUI方法中判断是否是强制更新来弹出不同的窗口和处理事件,并在点击事件中判断用户当前是否使用流量,如果是则调用showNetDialog(updateInfo)提示,如果不是则启动下载异步任务asyncDownLoad.execute(updateInfo):
/** * 弹出提示更新窗口 */
private void showUpdateUI(final UpdateInfo updateInfo) {
if (updateInfo.getIsForce()==0) {
//普通更新
final VersionDialog dialog = new VersionDialog(...,STYLE_UPDATE_NOMAL);
dialog.setOnPositiveListener(new OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
NetWorkUtils netWorkUtils = new NetWorkUtils(mContext);
int type = netWorkUtils.getNetType();
if (type != 1) {
showNetDialog(updateInfo);
}else {
AsyncDownLoad asyncDownLoad = new AsyncDownLoad();
asyncDownLoad.execute(updateInfo);
}
}
});
dialog.show();
}else {
//强制更新
final VersionDialog dialog = new VersionDialog(...,STYLE_UPDATE_FORCE);
...
dialog.show();
}
}
下载任务中,每5%调用handler发送一次UPDATE_NOTIFICATION_PROGRESS消息来更新UI中的进度条;
并在下载完成的回调函数onPostExecute中给handler发送COMPLETE_DOWNLOAD_APK来通知安装:
@Override
protected Boolean doInBackground(UpdateInfo... params) {
...
FileOutputStream fos = new FileOutputStream(apkFile);
while ((length = inputStream.read(buf)) != -1) {
...
int progress = (int) ((count / (float) total) * 100);
if (progress % 5 == 0) {
handler.obtainMessage(UPDATE_NOTIFICATION_PROGRESS,progress, -1, params[0]).sendToTarget();
}
...
}
inputStream.close();
fos.close();
...
}
@Override
protected void onPostExecute(Boolean flag) {
...
handler.obtainMessage(COMPLETE_DOWNLOAD_APK).sendToTarget();
...
}
在handler的处理函数中,UPDATE_NOTIFICATION_PROGRESS消息调用showDownloadNotificationUI()来更新UI;
COMPLETE_DOWNLOAD_APK消息中判断是否自动安装来调用installApk()自动安装:
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_NOTIFICATION_PROGRESS:
showDownloadNotificationUI((UpdateInfo) msg.obj, msg.arg1);
break;
case COMPLETE_DOWNLOAD_APK:
if (UpdateHelper.this.isAutoInstall) {
...
//自动安装
installApk(Uri.parse("file://" + cache.get(APK_PATH)));
} else {
//点击通知栏消息进行手动安装
...
}
}
...
}
}
showDownloadNotificationUI判断是否是强制更新来决定是通知栏提示进度或者对话框提示进度:
private void showDownloadNotificationUI(UpdateInfo updateInfo,final int progress) {
if (mContext != null) {
if (updateInfo.getIsForce()==0) {
// 普通更新,通知栏提示
...
notificationManager.notify(DOWNLOAD_NOTIFICATION_ID,ntfBuilder.build());
}else {
// 强制更新,对话框提示
...
pd.show();
}
}
}

下面是安装apk的方法:

private void installApk(Uri data) {
...
Intent i = new Intent(Intent.ACTION_VIEW);
i.setDataAndType(data, "application/vnd.android.package-archive");
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(i);
...
}

以上插入的代码都省略过,为了方便讲解只贴出了重点代码行,具体代码请查看源码。