在这篇文章中(参见 android中如何下载文件并显示下载进度 )我们讲到了如何下载文件的问题,今天我介绍如何实现应用的自动更新,其中下载apk模块用到了前一篇文章中的知识。当然这只是一个实现的框架,你需要根据自己的需求是改变一些细节。
自动更新的原理
其实就是客户端将自己的版本号与服务端的版本号进行比对,版本号小于服务端则意味着有新版本,当然服务端的版本号是需要人工放上去的。
先看看效果:
为了代码的简洁,我这里用原生的alert对话框。
点击下载之后:
下载完成安装的界面我就不张贴了。
使用方法:
很简单,在需要检查更新的地方加入如下代码:
1
2
|
updateChecker.checkForUpdates();
|
其中http://jcodecraeer.com/update.php返回的是服务器段存放的版本信息。服务端的版本信息分为三部分:
1.版本号;
2.版本描述;
3.存放apk的url(告诉客户端,在哪里下载新版本的apk);
以http://jcodecraeer.com/update.php返回的结果为例,返回的字符串具体是这样的:
1
|
{
"url"
:
"http://www.jcodecraeer.com/***.apk"
,
"versionCode"
:
"2"
,
"updateMessage"
:
"1.修改了app图标 2.设备详情的显示方式"
}
|
这是一个json格式的字符串。
实现
有三个类:
其中
AppVersion是版本信息的模型类,基本上和服务端返回的东西是相对应的。
DownloadService是下载模块。
UpdateChecker是检查更新,调用下载模块,下载完安装的工具类。
AppVersion
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
|
package com.jcodecraeer.jcode.update;
public class AppVersion {
private String updateMessage;
private String apkUrl;
private int apkCode;
public static final String APK_DOWNLOAD_URL =
"url"
;
public static final String APK_UPDATE_CONTENT =
"updateMessage"
;
public static final String APK_VERSION_CODE =
"versionCode"
;
public void setUpdateMessage(String updateMessage) {
this
.updateMessage = updateMessage;
}
public String getUpdateMessage() {
return
updateMessage;
}
public void setApkUrl(String apkUrl) {
this
.apkUrl = apkUrl;
}
public String getApkUrl() {
return
apkUrl;
}
public void setApkCode(int apkCode) {
this
.apkCode = apkCode;
}
public int getApkCode() {
return
apkCode;
}
}
|
DownloadService
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
package com.jcodecraeer.jcode.update;
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import com.jcodecraeer.PullToRefreshListView;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.IntentService;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ResultReceiver;
import android.provider.ContactsContract;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Images.ImageColumns;
import android.support.v4.app.Fragment;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.GridView;
import android.widget.ShareActionProvider;
import android.widget.TextView;
import android.widget.Toast;
import android.view.ActionMode;
public class DownloadService extends IntentService {
public static final int UPDATE_PROGRESS = 8344;
public DownloadService() {
super
(
"DownloadService"
);
}
@Override
protected void onHandleIntent(Intent intent) {
String urlToDownload = intent.getStringExtra(
"url"
);
String fileDestination = intent.getStringExtra(
"dest"
);
ResultReceiver receiver = (ResultReceiver) intent.getParcelableExtra(
"receiver"
);
try
{
URL url =
new
URL(urlToDownload);
URLConnection connection = url.openConnection();
connection.connect();
// this will be useful so that you can show a typical 0-100% progress bar
int fileLength = connection.getContentLength();
// download the file
InputStream input =
new
BufferedInputStream(connection.getInputStream());
OutputStream output =
new
FileOutputStream(fileDestination);
byte data[] =
new
byte[100];
long total = 0;
int count;
while
((count = input.read(data)) != -1) {
total += count;
// publishing the progress....
Bundle resultData =
new
Bundle();
resultData.putInt(
"progress"
,(int) (total * 100 / fileLength));
receiver.send(UPDATE_PROGRESS, resultData);
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
}
catch
(IOException e) {
e.printStackTrace();
}
Bundle resultData =
new
Bundle();
resultData.putInt(
"progress"
,100);
receiver.send(UPDATE_PROGRESS, resultData);
}
}
|
UpdateChecker
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
|
package com.jcodecraeer.jcode.update;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.zip.GZIPInputStream;
import org.json.JSONException;
import org.json.JSONObject;
import com.jcodecraeer.jcode.Code;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.ResultReceiver;
import android.util.Log;
import android.widget.Toast;
public class UpdateChecker{
private static final String TAG =
"UpdateChecker"
;
private Context mContext;
//检查版本信息的线程
private Thread mThread;
//版本对比地址
private String mCheckUrl;
private AppVersion mAppVersion;
//下载apk的对话框
private ProgressDialog mProgressDialog;
private File apkFile;
public void setCheckUrl(String url) {
mCheckUrl = url;
}
public UpdateChecker(Context context) {
mContext = context;
// instantiate it within the onCreate method
mProgressDialog =
new
ProgressDialog(context);
mProgressDialog.setMessage(
"正在下载"
);
mProgressDialog.setIndeterminate(
false
);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(
true
);
mProgressDialog.setOnCancelListener(
new
DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
}
});
mProgressDialog.setOnDismissListener(
new
DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
// TODO Auto-generated method stub
}
});
}
public void checkForUpdates() {
if
(mCheckUrl ==
null
) {
//throw new Exception("checkUrl can not be null");
return
;
}
final Handler handler =
new
Handler(){
public void handleMessage(Message msg) {
if
(msg.what == 1) {
mAppVersion = (AppVersion) msg.obj;
try
{
int versionCode = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionCode;
if
(mAppVersion.getApkCode() > versionCode) {
showUpdateDialog();
}
else
{
//Toast.makeText(mContext, "已经是最新版本", Toast.LENGTH_SHORT).show();
}
}
catch
(PackageManager.NameNotFoundException ignored) {
//
}
}
}
};
mThread =
new
Thread() {
@Override
public void run() {
//if (isNetworkAvailable(mContext)) {
Message msg =
new
Message();
String json = sendPost();
Log.i(
"jianghejie"
,
"json = "
+json);
if
(json!=
null
){
AppVersion appVersion = parseJson(json);
msg.what = 1;
msg.obj = appVersion;
handler.sendMessage(msg);
}
else
{
Log.e(TAG,
"can't get app update json"
);
}
}
};
mThread.start();
}
protected String sendPost() {
HttpURLConnection uRLConnection =
null
;
InputStream is =
null
;
BufferedReader buffer =
null
;
String result =
null
;
try
{
URL url =
new
URL(mCheckUrl);
uRLConnection = (HttpURLConnection) url.openConnection();
uRLConnection.setDoInput(
true
);
uRLConnection.setDoOutput(
true
);
uRLConnection.setRequestMethod(
"POST"
);
uRLConnection.setUseCaches(
false
);
uRLConnection.setConnectTimeout(10 * 1000);
uRLConnection.setReadTimeout(10 * 1000);
uRLConnection.setInstanceFollowRedirects(
false
);
uRLConnection.setRequestProperty(
"Connection"
,
"Keep-Alive"
);
uRLConnection.setRequestProperty(
"Charset"
,
"UTF-8"
);
uRLConnection.setRequestProperty(
"Accept-Encoding"
,
"gzip, deflate"
);
uRLConnection.setRequestProperty(
"Content-Type"
,
"application/json"
);
uRLConnection.connect();
is = uRLConnection.getInputStream();
String content_encode = uRLConnection.getContentEncoding();
if
(
null
!= content_encode && !
""
.equals(content_encode) && content_encode.equals(
"gzip"
)) {
is =
new
GZIPInputStream(is);
}
buffer =
new
BufferedReader(
new
InputStreamReader(is));
StringBuilder strBuilder =
new
StringBuilder();
String line;
while
((line = buffer.readLine()) !=
null
) {
strBuilder.append(line);
}
result = strBuilder.toString();
}
catch
(Exception e) {
Log.e(TAG,
"http post error"
, e);
} finally {
if
(buffer!=
null
){
try
{
buffer.close();
}
catch
(IOException e) {
e.printStackTrace();
}
}
if
(is!=
null
){
try
{
is.close();
}
catch
(IOException e) {
e.printStackTrace();
}
}
if
(uRLConnection!=
null
){
uRLConnection.disconnect();
}
}
return
result;
}
private AppVersion parseJson(String json) {
AppVersion appVersion =
new
AppVersion();
try
{
JSONObject obj =
new
JSONObject(json);
String updateMessage = obj.getString(AppVersion.APK_UPDATE_CONTENT);
String apkUrl = obj.getString(AppVersion.APK_DOWNLOAD_URL);
int apkCode = obj.getInt(AppVersion.APK_VERSION_CODE);
appVersion.setApkCode(apkCode);
appVersion.setApkUrl(apkUrl);
appVersion.setUpdateMessage(updateMessage);
}
catch
(JSONException e) {
Log.e(TAG,
"parse json error"
, e);
}
return
appVersion;
}
public void showUpdateDialog() {
AlertDialog.Builder builder =
new
AlertDialog.Builder(mContext);
//builder.setIcon(R.drawable.icon);
builder.setTitle(
"有新版本"
);
builder.setMessage(mAppVersion.getUpdateMessage());
builder.setPositiveButton(
"下载"
,
new
DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
downLoadApk();
}
});
builder.setNegativeButton(
"忽略"
,
new
DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
}
});
builder.show();
}
public void downLoadApk() {
String apkUrl = mAppVersion.getApkUrl();
String dir = mContext.getExternalFilesDir(
"apk"
).getAbsolutePath();
File folder = Environment.getExternalStoragePublicDirectory(dir);
if
(folder.exists() && folder.isDirectory()) {
//do nothing
}
else
{
folder.mkdirs();
}
String filename = apkUrl.substring(apkUrl.lastIndexOf(
"/"
),apkUrl.length());
String destinationFilePath = dir +
"/"
+ filename;
apkFile =
new
File(destinationFilePath);
mProgressDialog.show();
Intent intent =
new
Intent(mContext, DownloadService.class);
intent.putExtra(
"url"
, apkUrl);
intent.putExtra(
"dest"
, destinationFilePath);
intent.putExtra(
"receiver"
,
new
DownloadReceiver(
new
Handler()));
mContext.startService(intent);
}
private class DownloadReceiver extends ResultReceiver{
public DownloadReceiver(Handler handler) {
super
(handler);
}
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super
.onReceiveResult(resultCode, resultData);
if
(resultCode == DownloadService.UPDATE_PROGRESS) {
int progress = resultData.getInt(
"progress"
);
mProgressDialog.setProgress(progress);
if
(progress == 100) {
mProgressDialog.dismiss();
//如果没有设置SDCard写权限,或者没有sdcard,apk文件保存在内存中,需要授予权限才能安装
String[] command = {
"chmod"
,
"777"
,apkFile.toString()};
try
{
ProcessBuilder builder =
new
ProcessBuilder(command);
builder.start();
Intent intent =
new
Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(apkFile),
"application/vnd.android.package-archive"
);
mContext.startActivity(intent);
}
catch
(Exception e){
}
}
}
}
}
}
|
所有代码都已经贴出来了。
这个代码是完全可以用的,但是有些细节问题需要改进。