一个成熟的商业APP必须不断的退出新的版本。那么,不可能让用户自己去应用市场去下载新 版本的应用,我们应该在应用内部提供自动升级的功能。自动升级其实包含两个层面,一个是整个APP的升级,也就是下载新版本的APP,然后安装替换掉现有 的。还有一种升级是模块升级,这种升级一般采用静默升级,就是用户完全不知道。这个在我大迅雷里面经常做的,拿各个渠道去试错,对于一个互联网公司而言是 再普通不过的了。而这些模块,肯定是诸如,解析库,下载库,播放库,这些后台库。。
不过今天,我要说的是APP的自动升级,模块的静默升级我们下次有时间再说。
APP升级流程
盗用别人的图,来说明一下升级APP的整体流程。对,这个图是盗用别人的。因为,这就是APP升级的一般流程。 所以,我们知道了,第一步,我们需要去服务器查询版本信息,确定是不是要进行升级。
到服务器查询版本信息
具体见代码,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
private
static
final
class
UpgradeLoader
extends
AsyncTask<
void
,
void
,=
""
updateinfo=
""
> {
private
WeakReference mContext;
private
boolean
mAuto;
private
IUpgradeCheck mUpgradeCheckListener;
protected
boolean
mChecked;
public
UpgradeLoader(Activity context,
boolean
auto, IUpgradeCheck listener) {
mContext =
new
WeakReference(context);
mAuto = auto;
mUpgradeCheckListener = listener;
}
@Override
protected
void
onPreExecute() {
if
(mUpgradeCheckListener !=
null
) {
mUpgradeCheckListener.beforeCheck();
}
}
@Override
protected
UpdateInfo doInBackground(Void... params) {
if
(mUpgradeCheckListener !=
null
) {
mUpgradeCheckListener.onCheck();
}
Context ctx = mContext.get();
String channel =
""
;
if
(ctx !=
null
) {
channel = Util.getChannelID(ctx);
}
return
DataProxy.getInstance().getUpdateInfo(Util.getVersionName(MyApplication.sInstance),
Util.getOSVersion(), channel);
//去服务器查询版本信息,这个是我们对http的一个封装,在这里,每个公司可以自己定义一些自己的地址和参数,非常简单的。
}
@Override
protected
void
onPostExecute(UpdateInfo result) {
//从服务器查询回来,决定要不要升级
if
(mUpgradeCheckListener !=
null
) {
mUpgradeCheckListener.afterCheck();
}
if
(!isCancelled()) {
if
(result ==
null
) {
if
(!mAuto) {
UIHelper.showToast(MyApplication.sInstance,
"检查更新失败"
, Toast.LENGTH_SHORT);
}
}
else
{
switch
(result.type) {
case
UpdateInfo.NO_UPGRADE:
if
(!mAuto) {
UIHelper.showToast(KankanApplication.sInstance,
"已经是最新版本,没有更新"
, Toast.LENGTH_SHORT);
}
break
;
case
UpdateInfo.UPDATE_NOT_TIPS:
if
(!mAuto) {
buildDialog(result);
}
else
{
downloadApk(KankanApplication.sInstance, result.latestUrl);
}
break
;
case
UpdateInfo.UPDATE_TIPS:
case
UpdateInfo.UPDATE_FOURCE:
if
(!(mAuto && PreferenceManager.instance(mContext.get()).isVerionSkiped(result.latestVersion))) {
buildDialog(result);
}
default
:
break
;
}
}
}
}</activity></activity></
void
,>
|
下载APK
在网上,很多人提供了采用 httpclient 去下载APK的方法,我这里给出另外一种思路。两种方法都可以,建议采用我们的提供的,因为,这个是针对下载较大文件的系统方法。详见代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
private
static
void
downloadApk(Context context, String url) {
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
Uri uri = Uri.parse(url);
String scheme = uri.getScheme();
if
(scheme ==
null
|| (!scheme.equals(
"http"
) && !scheme.equals(
"https"
))) {
LOG.error(
"only supports http/https url={}"
, url);
}
else
{
try
{
DownloadManager.Request down =
new
DownloadManager.Request(uri);
down.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE
| DownloadManager.Request.NETWORK_WIFI);
down.setShowRunningNotification(
true
);
down.setVisibleInDownloadsUi(
true
);
down.setDestinationInExternalFilesDir(context,
null
, APK_NAME);
manager.enqueue(down);
//开始下载
}
catch
(Exception e) {
e.printStackTrace();
UIHelper.showToast(context,
"下载应用失败,请您去应用市场升级"
, Toast.LENGTH_LONG);
}
}
}
|
下载完成了,接下来就是要安装了。我们需要一个事件去触发安装。做法是,我们应该监听下载完成这件事情,下载完成之后,会有个广播发生,当监听到这个广播的时候我们就开始安装。
安装APK
监听下载完成,并安装。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
void
onReceive(Context context, Intent intent) {
if
(intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
//监听下载完成
long
id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -
1
);
if
(id > -
1
) {
File file = queryFile(context, id);
if
(file !=
null
&& file.exists()) {
Intent startInent =
new
Intent();
startInent.setAction(Intent.ACTION_VIEW);
startInent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startInent.setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive"
);
context.startActivity(startInent);
//安装APK
}
}
}
}
|
至此,一个APK的自动升级就完成了。其实还有一些善后工作需要做的是,你需要删除APK,删除APK的方法应该是在安装完成之后删除,网上有说通过广播监听的方法来删除,就是监听到安装完成之后删除。这个大家去搜索一下就可以。
删除APK文件
这里,我还是给出点示例: 首先是要获取应用的安装状态,通过广播的形式以下是和应用程序相关的Broadcast Action
ACTION_PACKAGE_ADDED 一个新应用包已经安装在设备上,数据包括包名(最新安装的包程序不能接收到这个广播)
ACTION_PACKAGE_REPLACED 一个新版本的应用安装到设备,替换之前已经存在的版本
ACTION_PACKAGE_CHANGED 一个已存在的应用程序包已经改变,包括包名
ACTION_PACKAGE_REMOVED 一个已存在的应用程序包已经从设备上移除,包括包名(正在被安装的包程序不能接收到这个广播)
ACTION_PACKAGE_RESTARTED 用户重新开始一个包,包的所有进程将被杀死,所有与其联系的运行时间状态应该被移除,包括包名(重新开始包程序不能接收到这个广播)
ACTION_PACKAGE_DATA_CLEARED 用户已经清楚一个包的数据,包括包名(清除包程序不能接收到这个广播)
代码实现
在 AndroidManifest.xml 中定义广播
1
2
3
4
5
6
7
8
|
<receiver android:name=
".AppInstallReceiver"
android:label=
"@string/app_name"
>
<intent-filter>
<data android:scheme=
"package"
>
</data></action></action></action></intent-filter>
</receiver>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
class
AppInstallReceiver
extends
BroadcastReceiver {
@Override
public
void
onReceive(Context context, Intent intent) {
PackageManager manager = context.getPackageManager();
if
(intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) {
String packageName = intent.getData().getSchemeSpecificPart();
Toast.makeText(context,
"安装成功"
+packageName, Toast.LENGTH_LONG).show();
}
if
(intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
String packageName = intent.getData().getSchemeSpecificPart();
Toast.makeText(context,
"卸载成功"
+packageName, Toast.LENGTH_LONG).show();
}
if
(intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {
String packageName = intent.getData().getSchemeSpecificPart();
Toast.makeText(context,
"替换成功"
+packageName, Toast.LENGTH_LONG).show();
|
1
2
3
4
5
|
<span style=
"white-space:pre"
> </span>doanloadApk.delete();
//从SD中删除刚刚下载的APK文件
}
}
}
|
好。APK自动升级就说道这里。
有时间,给大家说一下 模块的静默升级 的实现。