見證過博客園的屢次升級,你也但願你的軟件經過更新發布新特性通知用戶吧,是的。
這篇文章是android開發人員的必備知識,是我特別爲你們整理和總結的,不求完美,可是有用。 html
1.設計思路,使用VersionCode定義爲版本升級參數。
android爲咱們定義版本提供了2個屬性:java
1
2
3
4
|
<
manifest
package
=
"com.cnblogs.tianxia.subway"
android:versionCode
=
"1"
<!--Integer類型,系統不顯示給用戶-->
android:versionName="1.0"
<!--String類型,系統顯示用戶-->
></
manifest
>
|
谷歌建議咱們使用versionCode自增來代表版本升級,不管是大的改動仍是小的改動,而versionName是顯示用戶看的軟件版本,做爲顯示使用。因此咱們選擇了VersionCode做爲咱們定義版本升級的參數。android
2.工程目錄
爲了對真實項目或者企業運用有實戰指導做用,我模擬一個獨立的項目,工程目錄設置的合理嚴謹一些,而不是僅僅一個demo。
假設咱們以上海地鐵爲項目,命名爲"Subway",工程結構以下,
服務器
3.版本初始化和版本號的對比。
首先定義在全局文件Global.java中定義變量localVersion和serverVersion分別存放本地版本號和服務器版本號。app
1
2
3
4
5
6
|
public
class
Global {
//版本信息
public
static
int
localVersion =
0
;
public
static
int
serverVersion =
0
;
public
static
String downloadDir =
"app/download/"
;
}
|
由於本文只是重點說明升級更新,爲了防止其餘太多無關代碼冗餘其中,我直接在SubwayApplication中定義方法initGlobal()方法。ide
1
2
3
4
5
6
7
8
9
10
11
12
|
/**
* 初始化全局變量
* 實際工做中這個方法中serverVersion從服務器端獲取,最好在啓動畫面的activity中執行
*/
public
void
initGlobal(){
try
{
Global.localVersion = getPackageManager().getPackageInfo(getPackageName(),
0
).versionCode;
//設置本地版本號
Global.serverVersion =
1
;
//假定服務器版本爲2,本地版本默認是1
}
catch
(Exception ex){
ex.printStackTrace();
}
}
|
若是檢測到新版本發佈,提示用戶是否更新,我在SubwayActivity中定義了checkVersion()方法:模塊化
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
|
/**
* 檢查更新版本
*/
public
void
checkVersion(){
if
(Global.localVersion < Global.serverVersion){
//發現新版本,提示用戶更新
AlertDialog.Builder alert =
new
AlertDialog.Builder(
this
);
alert.setTitle(
"軟件升級"
)
.setMessage(
"發現新版本,建議當即更新使用."
)
.setPositiveButton(
"更新"
,
new
DialogInterface.OnClickListener() {
public
void
onClick(DialogInterface dialog,
int
which) {
//開啓更新服務UpdateService
//這裏爲了把update更好模塊化,能夠傳一些updateService依賴的值
//如佈局ID,資源ID,動態獲取的標題,這裏以app_name爲例
Intent updateIntent =
new
Intent(SubwayActivity.
this
, UpdateService.
class
);
updateIntent.putExtra(
"titleId"
,R.string.app_name);
startService(updateIntent);
}
})
.setNegativeButton(
"取消"
,
new
DialogInterface.OnClickListener(){
public
void
onClick(DialogInterface dialog,
int
which) {
dialog.dismiss();
}
});
alert.create().show();
}
else
{
//清理工做,略去
//cheanUpdateFile(),文章後面我會附上代碼
}
}
|
以下圖:
好,咱們如今把這些東西串一下:
第一步在SubwayApplication的onCreate()方法中執行initGlobal()初始化版本變量。函數
1
2
3
4
|
public
void
onCreate() {
super
.onCreate();
initGlobal();
}
|
第二步在SubwayActivity的onCreate()方法中檢測版本更新checkVersion()。佈局
1
2
3
4
5
|
public
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.main);
checkVersion();
}
|
如今入口已經打開,在checkVersion方法的第18行代碼中看出,當用戶點擊更新,咱們開啓更新服務,從服務器上下載最新版本。動畫
4.使用Service在後臺從服務器端下載,完成後提示用戶下載完成,並關閉服務。
定義一個服務UpdateService.java,首先定義與下載和通知相關的變量:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//標題
private
int
titleId =
0
;
//文件存儲
private
File updateDir =
null
;
private
File updateFile =
null
;
//通知欄
private
NotificationManager updateNotificationManager =
null
;
private
Notification updateNotification =
null
;
//通知欄跳轉Intent
private
Intent updateIntent =
null
;
private
PendingIntent updatePendingIntent =
null
;
|
在onStartCommand()方法中準備相關的下載工做:
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
|
@Override
public
int
onStartCommand(Intent intent,
int
flags,
int
startId) {
//獲取傳值
titleId = intent.getIntExtra(
"titleId"
,
0
);
//建立文件
if
(android.os.Environment.MEDIA_MOUNTED.equals(android.os.Environment.getExternalStorageState())){
updateDir =
new
File(Environment.getExternalStorageDirectory(),Global.downloadDir);
updateFile =
new
File(updateDir.getPath(),getResources().getString(titleId)+
".apk"
);
}
this
.updateNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
this
.updateNotification =
new
Notification();
//設置下載過程當中,點擊通知欄,回到主界面
updateIntent =
new
Intent(
this
, SubwayActivity.
class
);
updatePendingIntent = PendingIntent.getActivity(
this
,
0
,updateIntent,
0
);
//設置通知欄顯示內容
updateNotification.icon = R.drawable.arrow_down_float;
updateNotification.tickerText =
"開始下載"
;
updateNotification.setLatestEventInfo(
this
,
"上海地鐵"
,
"0%"
,updatePendingIntent);
//發出通知
updateNotificationManager.notify(
0
,updateNotification);
//開啓一個新的線程下載,若是使用Service同步下載,會致使ANR問題,Service自己也會阻塞
new
Thread(
new
updateRunnable()).start();
//這個是下載的重點,是下載的過程
return
super
.onStartCommand(intent, flags, startId);
}
|
上面都是準備工做,如圖:
從代碼中能夠看出來,updateRunnable類纔是真正下載的類,出於用戶體驗的考慮,這個類是咱們單獨一個線程後臺去執行的。
下載的過程有兩個工做:1.從服務器上下載數據;2.通知用戶下載的進度。
線程通知,咱們先定義一個空的updateHandler。
1
2
3
4
5
6
|
private
Handler updateHandler =
new
Handler(){
@Override
public
void
handleMessage(Message msg) {
}
};
|
再來建立updateRunnable類的真正實現:
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
|
class
updateRunnable
implements
Runnable {
Message message = updateHandler.obtainMessage();
public
void
run() {
message.what = DOWNLOAD_COMPLETE;
try
{
//增長權限;
if
(!updateDir.exists()){
updateDir.mkdirs();
}
if
(!updateFile.exists()){
updateFile.createNewFile();
}
//下載函數,以QQ爲例子
//增長權限;
long
downloadSize = downloadUpdateFile(
"http://softfile.3g.qq.com:8080/msoft/179/1105/10753/MobileQQ1.0(Android)_Build0198.apk"
,updateFile);
if
(downloadSize>
0
){
//下載成功
updateHandler.sendMessage(message);
}
}
catch
(Exception ex){
ex.printStackTrace();
message.what = DOWNLOAD_FAIL;
//下載失敗
updateHandler.sendMessage(message);
}
}
}
|
下載函數的實現有不少,我這裏把代碼貼出來,並且咱們要在下載的時候通知用戶下載進度:
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
|
public
long
downloadUpdateFile(String downloadUrl, File saveFile)
throws
Exception {
//這樣的下載代碼不少,我就不作過多的說明
int
downloadCount =
0
;
int
currentSize =
0
;
long
totalSize =
0
;
int
updateTotalSize =
0
;
HttpURLConnection httpConnection =
null
;
InputStream is =
null
;
FileOutputStream fos =
null
;
try
{
URL url =
new
URL(downloadUrl);
httpConnection = (HttpURLConnection)url.openConnection();
httpConnection.setRequestProperty(
"User-Agent"
,
"PacificHttpClient"
);
if
(currentSize >
0
) {
httpConnection.setRequestProperty(
"RANGE"
,
"bytes="
+ currentSize +
"-"
);
}
httpConnection.setConnectTimeout(
10000
);
httpConnection.setReadTimeout(
20000
);
updateTotalSize = httpConnection.getContentLength();
if
(httpConnection.getResponseCode() ==
404
) {
throw
new
Exception(
"fail!"
);
}
is = httpConnection.getInputStream();
fos =
new
FileOutputStream(saveFile,
false
);
byte
buffer[] =
new
byte
[
4096
];
int
readsize =
0
;
while
((readsize = is.read(buffer)) >
0
){
fos.write(buffer,
0
, readsize);
totalSize += readsize;
//爲了防止頻繁的通知致使應用吃緊,百分比增長10才通知一次
if
((downloadCount ==
0
)||(
int
) (totalSize*
100
/updateTotalSize)-
10
>downloadCount){
downloadCount +=
10
;
updateNotification.setLatestEventInfo(UpdateService.
this
,
"正在下載"
, (
int
)totalSize*
100
/updateTotalSize+
"%"
, updatePendingIntent);
updateNotificationManager.notify(
0
, updateNotification);
}
}
}
finally
{
if
(httpConnection !=
null
) {
httpConnection.disconnect();
}
if
(is !=
null
) {
is.close();
}
if
(fos !=
null
) {
fos.close();
}
}
return
totalSize;
}
|
顯示下載進度,如圖:
下載完成後,咱們提示用戶下載完成,而且能夠點擊安裝,那麼咱們來補全前面的Handler吧。
先在UpdateService.java定義2個常量來表示下載狀態:
1
2
3
|
//下載狀態
private
final
static
int
DOWNLOAD_COMPLETE =
0
;
private
final
static
int
DOWNLOAD_FAIL =
1
;
|
根據下載狀態處理主線程:
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
|
private
Handler updateHandler =
new
Handler(){
@Override
public
void
handleMessage(Message msg) {
switch
(msg.what){
case
DOWNLOAD_COMPLETE:
//點擊安裝PendingIntent
Uri uri = Uri.fromFile(updateFile);
Intent installIntent =
new
Intent(Intent.ACTION_VIEW);
installIntent.setDataAndType(uri,
"application/vnd.android.package-archive"
);
updatePendingIntent = PendingIntent.getActivity(UpdateService.
this
,
0
, installIntent,
0
);
updateNotification.defaults = Notification.DEFAULT_SOUND;
//鈴聲提醒
updateNotification.setLatestEventInfo(UpdateService.
this
,
"上海地鐵"
,
"下載完成,點擊安裝。"
, updatePendingIntent);
updateNotificationManager.notify(
0
, updateNotification);
//中止服務
stopSelf();
break
;
case
DOWNLOAD_FAIL:
//下載失敗
updateNotification.setLatestEventInfo(UpdateService.
this
,
"上海地鐵"
,
"下載完成,點擊安裝。"
, updatePendingIntent);
updateNotificationManager.notify(
0
, updateNotification);
break
;
default
:
stopSelf();
}
}
};
|
下載完成,如圖:
至此,文件下載而且在通知欄通知進度。
發現本人廢話不少,其實幾句話的事情,來來回回寫了這麼多,囉嗦了,後面博文我會朝着精簡方面努力。
PS:前面說要附上cheanUpdateFile()的代碼
1
2
3
4
5
|
File updateFile =
new
File(Global.downloadDir,getResources().getString(R.string.app_name)+
".apk"
);
if
(updateFile.exists()){
//當不須要的時候,清除以前的下載文件,避免浪費用戶空間
updateFile.delete();
}
|
謝謝你們!!!!