App自動更新之通知欄下載

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

謝謝你們!!!!

相關文章
相關標籤/搜索