Android 輕鬆實現後臺搭建+APP版本更新

(本文講解了在Android中實現APP版本更新,文末附有源碼。)

看完本文,您可以學到:


1.版本更新的方法

2.與後臺的交互

3.Android中Handler的使用

4.Android中ProgressDialog的使用

話不多說,先來看看效果圖:





一、大致思路闡述


首先,我們要有一個可以被手機訪問的後臺。

這裏有兩種方法,在調試的時候我們可以利用手機和筆記本連到同一個局域網的方式,在電腦上開啓個類似PHP或者JAVAEE一樣樣的後臺服務。

但是對於沒有相關後臺開發經驗的朋友,這裏有一種更好的方式:利用Github等免費空間來實現。詳細請戳我的另一篇博文利用Github建立你的個人網站 。

OK,有了存放資源的後臺,我們要放點什麼東西呢?很簡單,一個包含最新版本信息的update.txt文件和一個.apk文件足矣!


txt文件裏寫啥?看下我的例子:

XXX&1.3&這裏寫點描述&http://192.168.1.100:8080/PersonalHomePage/new.apk

解釋一下: &是分隔符,用於手機端獲取到信息後的分割。1.3代表着最新版本號,之後的是新版本的描述,最後的是新版本APK的下載地址(這 裏我用了局域網)。一開始的是啥呢?我當時在試驗的時候,在開頭並沒有加額外信息,即以1.3開頭,實驗之後,發現手機端獲取到TXT文本信息後不能正確 解析,原因我覺得是因爲TXT文件的開頭包含有一些自帶的字符,手機解析時會有問題。(感興趣的朋友可以去深究,還望不吝賜教!)


OK,有了新版本的信息,我們要怎麼做?

我們要獲取到最新的版本號,然後與當前APP的版本號進行對比。如果低於最新版本,就到下載地址中去下載。


二、詳細代碼解釋


首先,新建一個UpdateInfo類,用來與update.txt的內容對應,這個很簡單:


[java] view plaincopy

  1. package com.example.appupdatedemo;  

  2.   

  3. public class UpdateInfo  

  4. {  

  5.         private String version;  

  6.         private String description;  

  7.         private String url;  

  8.           

  9.         public String getVersion()  

  10.         {  

  11.                 return version;  

  12.         }  

  13.         public void setVersion(String version)  

  14.         {  

  15.                 this.version = version;  

  16.         }  

  17.         public String getDescription()  

  18.         {  

  19.                 return description;  

  20.         }  

  21.         public void setDescription(String description)  

  22.         {  

  23.                 this.description = description;  

  24.         }  

  25.         public String getUrl()  

  26.         {  

  27.                 return url;  

  28.         }  

  29.         public void setUrl(String url)  

  30.         {  

  31.                 this.url = url;  

  32.         }  

  33.           

  34. }  



然後,寫一個類去獲取更新的信息,即我們的update.txt文件:


UpdateInfoService:


[java] view plaincopy

  1. package com.example.appupdatedemo;  

  2.   

  3. import java.io.BufferedReader;  

  4. import java.io.InputStreamReader;  

  5. import java.net.HttpURLConnection;  

  6. import java.net.URL;  

  7.   

  8. import android.content.Context;  

  9.   

  10. public class UpdateInfoService {  

  11.     public UpdateInfoService(Context context) {  

  12.     }  

  13.   

  14.     public UpdateInfo getUpDateInfo() throws Exception {  

  15.         String path = GetServerUrl.getUrl() + "/update.txt";  

  16.         StringBuffer sb = new StringBuffer();  

  17.         String line = null;  

  18.         BufferedReader reader = null;  

  19.         try {  

  20.             // 創建一個url對象  

  21.             URL url = new URL(path);  

  22.             // 通過url對象,創建一個HttpURLConnection對象(連接)  

  23.             HttpURLConnection urlConnection = (HttpURLConnection) url  

  24.                     .openConnection();  

  25.             // 通過HttpURLConnection對象,得到InputStream  

  26.             reader = new BufferedReader(new InputStreamReader(  

  27.                     urlConnection.getInputStream()));  

  28.             // 使用io流讀取文件  

  29.             while ((line = reader.readLine()) != null) {  

  30.                 sb.append(line);  

  31.             }  

  32.         } catch (Exception e) {  

  33.             e.printStackTrace();  

  34.         } finally {  

  35.             try {  

  36.                 if (reader != null) {  

  37.                     reader.close();  

  38.                 }  

  39.             } catch (Exception e) {  

  40.                 e.printStackTrace();  

  41.             }  

  42.         }  

  43.         String info = sb.toString();  

  44.         UpdateInfo updateInfo = new UpdateInfo();  

  45.         updateInfo.setVersion(info.split("&")[1]);  

  46.         updateInfo.setDescription(info.split("&")[2]);  

  47.         updateInfo.setUrl(info.split("&")[3]);  

  48.         return updateInfo;  

  49.     }  

  50.   

  51. }  


這裏獲取文件的方法是先創建一個HttpURLConnection,再獲取輸入流。細心的朋友可能注意到其中有個類,叫GetServerUrl,這個類是用來存放後臺地址信息的:


[java] view plaincopy

  1. package com.example.appupdatedemo;  

  2.   

  3. /** 

  4.  * 獲取服務器IP地址 

  5.  */  

  6.   

  7. public class GetServerUrl{  

  8.     static String url="http://192.168.1.100:8080/PersonalHomePage";   //沒錯,我這裏用的是本地的JAVAEE工程,各位根據實際情況修改。  

  9.               

  10.     public static String getUrl() {  

  11.         return url;  

  12.     }  

  13. }  


OK,到了這一步,準備工作都做完了,接下來只剩一個類了!即我們的MainActicity,一共一百多行,我們分幾部分來講。


第一部分代碼,做的工作是獲取版本更新信息。


[java] view plaincopy

  1. public class MainActivity extends Activity {  

  2.   

  3.     // 更新版本要用到的一些信息  

  4.     private UpdateInfo info;  

  5.     private ProgressDialog pBar;  

  6.   

  7.     @Override  

  8.     protected void onCreate(Bundle savedInstanceState) {  

  9.         super.onCreate(savedInstanceState);  

  10.         setContentView(R.layout.activity_main);  

  11.   

  12.         Toast.makeText(MainActivity.this"正在檢查版本更新..", Toast.LENGTH_SHORT).show();  

  13.         // 自動檢查有沒有新版本 如果有新版本就提示更新  

  14.         new Thread() {  

  15.             public void run() {  

  16.                 try {  

  17.                     UpdateInfoService updateInfoService = new UpdateInfoService(  

  18.                             MainActivity.this);  

  19.                     info = updateInfoService.getUpDateInfo();  

  20.                     handler1.sendEmptyMessage(0);  

  21.                 } catch (Exception e) {  

  22.                     e.printStackTrace();  

  23.                 }  

  24.             };  

  25.         }.start();  

  26.     }  

  27.   

  28.     @SuppressLint("HandlerLeak")  

  29.     private Handler handler1 = new Handler() {  

  30.         public void handleMessage(Message msg) {  

  31.             // 如果有更新就提示  

  32.             if (isNeedUpdate()) {   //在下面的代碼段  

  33.                 showUpdateDialog();  //下面的代碼段  

  34.             }  

  35.         };  

  36.     };  


這裏我們用到了new Thread+ Handler的方式去進行異步加載版本信息,主要是因爲在安卓中要把耗時任務放在非主線程中執行,否則會造成阻塞,拋出無響應異常。還有另外的實現方式是安卓封裝的AsyncTask,具體可以參考這篇博文:Android AsyncTask詳解



第二部分,判斷是否是最新版本,如果不是,跳出對話框選擇是否更新:


[java] view plaincopy

  1. private void showUpdateDialog() {  

  2.     AlertDialog.Builder builder = new AlertDialog.Builder(this);  

  3.     builder.setIcon(android.R.drawable.ic_dialog_info);  

  4.     builder.setTitle("請升級APP至版本" + info.getVersion());  

  5.     builder.setMessage(info.getDescription());  

  6.     builder.setCancelable(false);  

  7.   

  8.     builder.setPositiveButton("確定"new DialogInterface.OnClickListener() {  

  9.   

  10.         @Override  

  11.         public void onClick(DialogInterface dialog, int which) {  

  12.             if (Environment.getExternalStorageState().equals(  

  13.                     Environment.MEDIA_MOUNTED)) {  

  14.                 downFile(info.getUrl());     //在下面的代碼段  

  15.             } else {  

  16.                 Toast.makeText(MainActivity.this"SD卡不可用,請插入SD卡",  

  17.                         Toast.LENGTH_SHORT).show();  

  18.             }  

  19.         }  

  20.     });  

  21.     builder.setNegativeButton("取消"new DialogInterface.OnClickListener() {  

  22.   

  23.         @Override  

  24.         public void onClick(DialogInterface dialog, int which) {  

  25.         }  

  26.   

  27.     });  

  28.     builder.create().show();  

  29. }  

  30.   

  31. private boolean isNeedUpdate() {  

  32.       

  33.     String v = info.getVersion(); // 最新版本的版本號  

  34.     Log.i("update",v);  

  35.     Toast.makeText(MainActivity.this, v, Toast.LENGTH_SHORT).show();  

  36.     if (v.equals(getVersion())) {  

  37.         return false;  

  38.     } else {  

  39.         return true;  

  40.     }  

  41. }  

  42.   

  43. // 獲取當前版本的版本號  

  44. private String getVersion() {  

  45.     try {  

  46.         PackageManager packageManager = getPackageManager();  

  47.         PackageInfo packageInfo = packageManager.getPackageInfo(  

  48.                 getPackageName(), 0);  

  49.         return packageInfo.versionName;  

  50.     } catch (NameNotFoundException e) {  

  51.         e.printStackTrace();  

  52.         return "版本號未知";  

  53.     }  

  54. }  

這段裏面要注意的是怎麼獲取當前版本,方法是使用PackageManager提供的getPackageInfo方法,返回的是manifest文件中的版本號。其他的代碼挺簡單,註釋也挺全的。如果有問題,歡迎留言。


接下來是最後一部分,下載文件。


[java] view plaincopy

  1.     void downFile(final String url) {   

  2.         pBar = new ProgressDialog(MainActivity.this);    //進度條,在下載的時候實時更新進度,提高用戶友好度  

  3.         pBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);  

  4.         pBar.setTitle("正在下載");  

  5.         pBar.setMessage("請稍候...");  

  6.         pBar.setProgress(0);  

  7.         pBar.show();  

  8.         new Thread() {  

  9.             public void run() {          

  10.                 HttpClient client = new DefaultHttpClient();  

  11.                 HttpGet get = new HttpGet(url);  

  12.                 HttpResponse response;  

  13.                 try {  

  14.                     response = client.execute(get);  

  15.                     HttpEntity entity = response.getEntity();  

  16.                     int length = (int) entity.getContentLength();   //獲取文件大小  

  17.                                         pBar.setMax(length);                            //設置進度條的總長度  

  18.                     InputStream is = entity.getContent();  

  19.                     FileOutputStream fileOutputStream = null;  

  20.                     if (is != null) {  

  21.                         File file = new File(  

  22.                                 Environment.getExternalStorageDirectory(),  

  23.                                 "Test.apk");  

  24.                         fileOutputStream = new FileOutputStream(file);  

  25.                         byte[] buf = new byte[10];   //這個是緩衝區,即一次讀取10個比特,我弄的小了點,因爲在本地,所以數值太大一 下就下載完了,看不出progressbar的效果。  

  26.                         int ch = -1;  

  27.                         int process = 0;  

  28.                         while ((ch = is.read(buf)) != -1) {         

  29.                             fileOutputStream.write(buf, 0, ch);  

  30.                             process += ch;  

  31.                             pBar.setProgress(process);       //這裏就是關鍵的實時更新進度了!  

  32.                         }  

  33.   

  34.                     }  

  35.                     fileOutputStream.flush();  

  36.                     if (fileOutputStream != null) {  

  37.                         fileOutputStream.close();  

  38.                     }  

  39.                     down();  

  40.                 } catch (ClientProtocolException e) {  

  41.                     e.printStackTrace();  

  42.                 } catch (IOException e) {  

  43.                     e.printStackTrace();  

  44.                 }  

  45.             }  

  46.   

  47.         }.start();  

  48.     }  

  49.   

  50.     void down() {  

  51.         handler1.post(new Runnable() {  

  52.             public void run() {  

  53.                 pBar.cancel();  

  54.                 update();  

  55.             }  

  56.         });  

  57.     }  

  58. //安裝文件,一般固定寫法  

  59.     void update() {                      

  60.         Intent intent = new Intent(Intent.ACTION_VIEW);  

  61.         intent.setDataAndType(Uri.fromFile(new File(Environment  

  62.                 .getExternalStorageDirectory(), "Test.apk")),  

  63.                 "application/vnd.android.package-archive");  

  64.         startActivity(intent);  

  65.     }  

這 一段主要是利用progressdialog在下載的時候實時更新進度,主要利用的是一個字節數組的緩衝區。即每次獲取到的內容填滿緩衝區後就寫入到本地 本件中。這裏我把緩衝區的大小設置爲10個字節(1024會比較好),理由是因爲在同一個局域網中速度特別快,刷一下就下載完了,看不出進度條效果,緩衝 區調小點就OK了。


========================================

寫在後面:

源代碼已上傳到我的Github,或者到CSDN下載區下載。

任何問題,歡迎留言交流!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章