見證過博客園的屢次升級,你也但願你的軟件經過更新發布新特性通知用戶吧,是的。
這篇文章是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 updateRunnableimplements 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();
}
|
謝謝你們!!!!