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 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();
}

謝謝你們!!!!

相關文章
相關標籤/搜索