Android應用的自動更新模塊


軟件的自動更新一般都與Splash界面綁定在一起, 由於需要維護的軟件界面很復雜, 一個Activity中嵌入ViewPager, 並且邏輯比較復雜, 索性重新寫一個Activity, 現在的軟件都很流行使用Splash界面, 正好與自動更新配套在一起;

 

在這個自動更新Splash中, 使用到了 動畫設置 ,SharedPerference ,pull解析 ,dialog對話框 ,http網絡編程 ,handler 等.

 

注意一個錯誤 : 已安裝具有該名稱和不同簽名的數據包 , 早上測試人員報告突然出現這個問題, 在開發的時候我直接將eclipse上編譯的版本放到了服務器上, 最后出現了這個問題, 開發的時候明明是好的啊, 怎么測試的時候出問題了呢.

編譯環境不同, 產生的簽名是不一樣的, 在eclipse上編譯生成 與 正式版本在linux下編譯 所產生的 數字簽名 是不一樣的.

 

一. 創建Activity

 

1. 創建Activity大概流程

a. 設置全屏顯示.

b. 設置布局, 並在布局中顯示當前版本號, 為Splash界面添加動畫.

c. 獲取當前時間.

d. 獲取SharedPerence配置文件.

e. 開啟檢查版本號線程, 后續的操作都在這個線程中執行.

 

2. 設置窗口樣式

 

(1) 設置全屏顯示

a. 代碼實現 : 由於是Splash界面, 這里需要設置成無標題, 並且全屏顯示, 注意下面的兩行代碼需要在setContentView()方法之前調用;

  

  1. //隱藏標題欄  
  2. requestWindowFeature(Window.FEATURE_NO_TITLE);  
  3. //隱藏狀態欄  
  4. getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,   
  5.         WindowManager.LayoutParams.FLAG_FULLSCREEN);  

 

 

b. 配置實現 : 

  

  1. AndroidManifest.xml  
  2. <activity   
  3.     android:name="myAcitivty"    
  4.     android:theme="@android:style/Theme.NoTitleBar.Fullscreen" />  

 

 

(2) 關於窗口的其它設置

  

  1. //①設置窗體始終點亮  
  2. getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);  

 

  

  1. //②設置窗體始終點亮  
  2. getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);  

 

 

設置窗體始終點亮的配置文件實現

  

  1. //③AndroidManifest.xml添加權限  
  2. <uses-permission android:name="android.permission.WAKE_LOCK" />  



 

  1. //設置窗體背景模糊  
  2. getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,WindowManager.LayoutParams.FLAG_BLUR_BEHIND);  

 

(3) 屏幕方向設置

 

a. 配置文件實現 

  1. //設置橫屏  
  2. <activity android:name="myAcitivty"  android:screenOrientation="landscape" />       
  3.   
  4. //設置豎屏  
  5. <activity android:name="myAcitivty"  android:screenOrientation="portrait" />   

 

 

b. 代碼實現 

  1. //設置橫屏  
  2. setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);  
  3.   
  4. //設置豎屏  
  5. setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);  

 

 

c. 獲取屏幕方向 

  1. //獲取橫屏方向  
  2. int orientation = this.getResources().getConfiguration().orientation;  

其中的orientation方向可以使 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE 或者 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE .

 

3. 設置動畫

 

為了更好的用戶體驗, 這里給Splash界面添加一個動畫, 這個動畫加給整個界面.

 

(1) 創建動畫 

  1. AlphaAnimation animation = new AlphaAnimation(0.0f, 1.0f);<span style="white-space:pre">    </span>//創建動畫  
  2. animation.setDuration(2000);<span style="white-space:pre">  </span>//設置漸變  
  3. splash_rl.setAnimation(animation);<span style="white-space:pre">    </span>//設置動畫載體  

創建動畫吧: 創建的這個動畫是透明度漸變動畫, 傳入浮點型參數, 0代表完全透明, 1代表不透明, 傳入參數代表透明度從完全透明到不透明.

 

設置時間 : 設置的duration是動畫漸變過程所消耗的時間.

設置動畫 : 最后使用setAnimation()方法將穿件的動畫設置給Splash界面.

 

(2) 動畫常用方法

 

a. 普通設置  

  1. alphaAnimation.setRepeatCount(5);//設置重復次數  
  2. alphaAnimation.setFillAfter(true);//動畫執行完是否停留在執行完的狀態  
  3. alphaAnimation.setStartOffset(1000);//動畫執行前等待的時間, 單位是毫秒  
  4. alphaAnimation.start();//開始動畫  

 

 


b. 設置監聽器

 

  1. alphaAnimation.setAnimationListener(new AnimationListener() {  
  2.             //動畫開始時回調  
  3.             @Override  
  4.             public void onAnimationStart(Animation animation) {  
  5.             }  
  6.             //動畫重復執行時回調  
  7.             @Override  
  8.             public void onAnimationRepeat(Animation animation) {  
  9.             }  
  10.             //動畫執行結束時回調  
  11.             @Override  
  12.             public void onAnimationEnd(Animation animation) {  
  13.             }  
  14.         });  

 

 

4. SharedPerference使用 

  1. //獲取SharedPerference  
  2. SharedPreferences sharedPreferences = getSharedPreferences("sp", Context.MODE_PRIVATE);  
  3.   
  4. Editor editor = sharedPreferences.edit();   //獲取Editor對象  
  5. editor.putBoolean("isUpdate"true);        //向sp中寫入數據  
  6. editor.commit();                            //提交  
  7.       
  8. sharedPreferences.getBoolean("isUpdate"true);//獲取sp中的變量  



 

5. onCreate()方法代碼  

  1. /** 
  2.      * 創建Activity時調用 
  3.      *  
  4.      * ① 設置全屏顯示, 由於是Splash界面, 因此不能有標題 
  5.      * ② 設置布局, 版本號, 執行動畫  
  6.      * ③ 設置當前時間 
  7.      * ④ 獲取SharedPerference配置文件 
  8.      * ⑤ 開啟檢查版本號線程, 后續操作都在改線程中操作 
  9.      *  
  10.      */  
  11.     @Override  
  12.     public void onCreate(Bundle savedInstanceState) {  
  13.         super.onCreate(savedInstanceState);  
  14.         //隱藏標題欄  
  15.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  16.         //隱藏狀態欄  
  17.         getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,   
  18.                 WindowManager.LayoutParams.FLAG_FULLSCREEN);  
  19.         //設置布局  
  20.         setContentView(R.layout.splash);  
  21.           
  22.         /* 
  23.          *  顯示當前軟件的版本號 
  24.          *  獲取布局中的TextView控件, 將版本號設置到這個TextView控件中 
  25.          */  
  26.         tv_version = (TextView) findViewById(R.id.tv_version);  
  27.         version =getString(R.string.current_version) + " " + getVersion();  
  28.         tv_version.setText(version);  
  29.           
  30.         /* 
  31.          *  在界面設置一個動畫, 用來表明正在運行 
  32.          *  a. 獲取布局 
  33.          *  b. 創建一個動畫對象 
  34.          *  c. 將動畫設置到布局中 
  35.          */  
  36.         splash_rl = (RelativeLayout) findViewById(R.id.splash_rl);  
  37.         AlphaAnimation animation = new AlphaAnimation(0.0f, 1.0f);  
  38.         animation.setDuration(2000);  
  39.         splash_rl.setAnimation(animation);  
  40.           
  41.         /* 
  42.          * 這個時間值是用來控制Splash界面顯示時間的 
  43.          * 記錄下這個值, 然后執行到下面, 如果時間差在3秒以內,  
  44.          * 就執行下面的操作, 如果時間差不足3秒, 就Thread.sleep時間差 
  45.          * 等夠3秒在執行下面的操作 
  46.          */  
  47.         time = System.currentTimeMillis();  
  48.   
  49.         //從SharedPreference中獲取一些配置  
  50.         sp = getSharedPreferences("config", Context.MODE_PRIVATE);  
  51.           
  52.         //開啟檢查版本號線程  
  53.         new Thread(new CheckVersionTask()).start();  
  54.     }  



 

 

二. 檢查版本號

 

1. 檢查版本號線程

流程 : 

a. 保持Splash持續時間 : 獲取當前時間與time進行比較, 如果不足3秒, 人為使Splash保持3秒時間;

b. 查看更新設置 : 從sp中獲取更新設置, 如果sp中自動更新為true, 那么就執行下面的更新流程, 如果sp中自動更新為false, 那么直接進入主界面.

c. 獲取信息 : 從網絡中獲取更新信息, 根據是否成功獲取信息執行不同的操作.

 

源碼 :  

  1. private final class CheckVersionTask implements Runnable{  
  2.     public void run() {  
  3.         try {  
  4.             /* 
  5.              * 獲取當前時間, 與onCreate方法中獲取的時間進行比較 
  6.              * 如果不足3秒, 在等待夠3秒之后在執行下面的操作 
  7.              */  
  8.             long temp = System.currentTimeMillis();  
  9.             if(temp - time < 3000){  
  10.                 SystemClock.sleep(temp - time);  
  11.             }  
  12.               
  13.             /* 
  14.              * 檢查配置文件中的設置, 是否設置了自動更新;  
  15.              * 如果設置了自動更新, 就執行下面的操作, 
  16.              * 如果沒有設置自動更新, 就直接進入主界面 
  17.              */  
  18.             boolean is_auto_update = sp.getBoolean("is_auto_update"true);  
  19.             if(!is_auto_update){  
  20.                 loadMainUI();  
  21.                 return;  
  22.             }  
  23.               
  24.             /* 
  25.              * 獲取更新信息 
  26.              * 如果信息不為null, 向handler發信息SUCESS_GET_UPDATEINOF, 執行后續操作 
  27.              * 如果信息為null, 向handler發信息ERROR_GET_UPDATEINOF, 執行后續操作 
  28.              * 如果出現異常, 向handler發信息ERROR_GET_UPDATEINOF, 執行后續操作 
  29.              */  
  30.             updateInfo = getUpdateInfo(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + XML_FILE_DIRECTORY);  
  31.             if(updateInfo != null){  
  32.                 Message msg = new Message();  
  33.                 msg.what = SUCESS_GET_UPDATEINOF;  
  34.                 mHandler.sendMessage(msg);  
  35.             }else{  
  36.                 Message msg = new Message();  
  37.                 msg.what = ERROR_GET_UPDATEINOF;  
  38.                 mHandler.sendMessage(msg);  
  39.             }  
  40.         } catch (Exception e) {  
  41.             e.printStackTrace();  
  42.             Message msg = new Message();  
  43.             msg.what = ERROR_GET_UPDATEINOF;  
  44.             mHandler.sendMessage(msg);  
  45.         }  
  46.     }  
  47.    }  



 

2. 獲取版本號方法

 

流程 : 

a. 創URL建對象;

b. 創建HttpURLConnection對象;

c. 設置超時時間;

d. 設置獲取方式;

e. 查看鏈接是否成功;

f. 解析輸入流信息;

 

源碼 :  

  1. /** 
  2.  * 獲取更新信息 
  3.  *      ① 根據字符串地址創建URL對象 
  4.  *      ② 根據URL對象創建HttpURLConnection鏈接對象 
  5.  *      ③ 設置鏈接對象5秒超時 
  6.  *      ④ 設置鏈接對象獲取的方式為get方式 
  7.  *      ⑤ 如果成功連接, conn.getRequestCode值就是200, 此時就可以獲取輸入流 
  8.  *      ⑥ 解析輸入流獲取更新信息 
  9.  *       
  10.  */  
  11. private UpdateInfo getUpdateInfo(String path){  
  12.     try {  
  13.         URL url = new URL(path);    //創建URL對象  
  14.         //創建連接對象  
  15.         HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
  16.         //設置鏈接超時  
  17.         conn.setConnectTimeout(5000);  
  18.         //設置獲取方式  
  19.         conn.setRequestMethod("GET");  
  20.         //如果連接成功, 獲取輸入流  
  21.         if(conn.getResponseCode() == 200){  
  22.             InputStream is = conn.getInputStream();  
  23.             //解析輸入流中的數據, 返回更新信息  
  24.             return parserUpdateInfo(is);  
  25.         }  
  26.     } catch (MalformedURLException e) {  
  27.         e.printStackTrace();  
  28.     } catch (ProtocolException e) {  
  29.         e.printStackTrace();  
  30.     } catch (IOException e) {  
  31.         e.printStackTrace();  
  32.     }  
  33.     return null;  
  34. }  

 

 

3. 更新信息對象

 

 

將從網上獲取的更新信息 包括 版本號, apk文件地址, 軟件描述等信息封裝在一個類中. 

  1. public class UpdateInfo {  
  2.     private String version; //當前軟件版本號  
  3.     private String url;     //獲取到的軟件地址  
  4.     private String description; //軟件描述  
  5.       
  6.     public String getVersion() {  
  7.         return version;  
  8.     }  
  9.     public void setVersion(String version) {  
  10.         this.version = version;  
  11.     }  
  12.     public String getUrl() {  
  13.         return url;  
  14.     }  
  15.     public void setUrl(String url) {  
  16.         this.url = url;  
  17.     }  
  18.     public String getDescription() {  
  19.         return description;  
  20.     }  
  21.     public void setDescription(String description) {  
  22.         this.description = description;  
  23.     }  
  24.     @Override  
  25.     public String toString() {  
  26.         return "UpdateInfo [version=" + version + ", url=" + url  
  27.                 + ", description=" + description + "]";  
  28.     }  
  29. }  

 

 

4. pull解析輸入流

 

(1) pull解析流程

 

a. 獲取pull解析器 : XmlPullParser parser = Xml.newPullParser();

b. 為pull解析器設置編碼 : parser.setInput(inputStream, "UTF-8");

c. 獲取pull解析器事件 : int eventType = parser.getEventType(), 之后的解析都要根據這個解析事件進行, 例如開始解析標簽的事件時 XmlPullParser.START_TAG, 文檔結束的事件時 XmlPullParser.END_DOCUMENT.

d. 解析流程控制 : 解析的時候, 如果沒有解析到文檔最后就一直解析, 這里使用while循環, eventType != XmlPullParser.END_DOCUMENT 就一直循環, 循環玩一個元素之后, 調用parser.next()遍歷下一個元素.

e. 獲取標簽名 : 在事件解析標簽的時候 ( eventType == XmlPullParser.START_TAG ) , 調用parser.getName()可以獲取這個標簽的標簽名, 如果我們想要獲取這個標簽下的文本元素, 可以使用parser.nextText()來獲取. 

 

(2) 更新xml文件 

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <updateInfo>  
  3.   <version>3.2</version>  
  4.   <url>http://127.0.0.1:8080/web/mobilesafe.apk</url>  
  5.   <description>客戶端更新</description>  
  6. </updateInfo>  

 

 

(3) 源碼 

  1. /** 
  2.  * 獲取更新信息 
  3.  *      ① 創建pull解析器 
  4.  *      ② 為解析器設置編碼格式 
  5.  *      ③ 獲取解析事件 
  6.  *      ④ 遍歷整個xml文件節點, 獲取標簽元素內容 
  7.  */  
  8. private UpdateInfo parserUpdateInfo(InputStream is){  
  9.     try {  
  10.         UpdateInfo updateInfo = null;  
  11.         //1. 創建pull解析解析器  
  12.         XmlPullParser parser = Xml.newPullParser();  
  13.         //2. 設置解析編碼  
  14.         parser.setInput(is, "UTF-8");  
  15.         //3. 獲取解析器解事件, 如解析到文檔開始 , 結尾, 標簽等  
  16.         int eventType = parser.getEventType();  
  17.         //4. 在文檔結束前一直解析  
  18.         while (eventType != XmlPullParser.END_DOCUMENT) {  
  19.             switch (eventType) {  
  20.             //只解析標簽  
  21.             case XmlPullParser.START_TAG:  
  22.                 if ("updateInfo".equals(parser.getName())) {  
  23.                     //當解析到updateInfo標簽的時候, 跟標簽開始, 創建一個UpdateInfo對象  
  24.                     updateInfo = new UpdateInfo();  
  25.                 } else if ("version".equals(parser.getName())) {  
  26.                     //解析版本號標簽  
  27.                     updateInfo.setVersion(parser.nextText());  
  28.                 } else if ("url".equals(parser.getName())) {  
  29.                     //解析url標簽  
  30.                     updateInfo.setUrl(parser.nextText());  
  31.                 } else if ("description".equals(parser.getName())) {  
  32.                     //解析描述標簽  
  33.                     updateInfo.setDescription(parser.nextText());  
  34.                 }  
  35.                 break;  
  36.             default:  
  37.                 break;  
  38.             }  
  39.             //每解析完一個元素, 就將解析標志位下移  
  40.             eventType = parser.next();  
  41.         }  
  42.         is.close();  
  43.         return updateInfo;  
  44.     } catch (XmlPullParserException e) {  
  45.         e.printStackTrace();  
  46.     } catch (IOException e) {  
  47.         e.printStackTrace();  
  48.     }  
  49.     return null;  
  50. }  

 

三. Handler對象

 

Handler對象用來控制整個更新過程的進行; 

  1. private Handler mHandler = new Handler(){  
  2.     public void handleMessage(android.os.Message msg) {  
  3.         switch (msg.what) {  
  4.         /* 
  5.          * 獲取更新信息錯誤 , 在斷網或者獲取信息出現異常執行 
  6.          * 提示一下, 之后進入主界面 
  7.          */  
  8.         case ERROR_GET_UPDATEINOF:  
  9.             ToastHint.getInstance().showHint(R.string.fail_to_get_updateinfo);  
  10.             loadMainUI();  
  11.             break;  
  12.         /* 
  13.          * 成功獲取更新信息, 一般在成功從網上獲取xml文件並解析出來 
  14.          * 如果版本號相同, 說明不用更新, 直接進入主界面 
  15.          * 如果版本號不同, 需要彈出更新對話框 
  16.          */  
  17.         case SUCESS_GET_UPDATEINOF:  
  18.             if(updateInfo.getVersion().equals(version)){  
  19.                 loadMainUI();  
  20.             }else{  
  21.                 showUpdateDialog();  
  22.             }  
  23.             break;  
  24.         /* 
  25.          * 下載apk文件出現錯誤, 中途斷網 出現異常等情況 
  26.          * 提示后進入主界面 
  27.          */  
  28.         case ERROR_DOWNLOAD_APK:  
  29.             mPb.dismiss();  
  30.             ToastHint.getInstance().showHint(R.string.fail_to_get_apk);  
  31.             loadMainUI();  
  32.             break;  
  33.         /* 
  34.          * 成功下載apk文件之后執行的操作 
  35.          * 取消進度條對話框, 之后安裝apk文件 
  36.          */  
  37.         case SUCCESS_DOWNLOAD_APK:  
  38.             mPb.dismiss();  
  39.             installApk();  
  40.             break;  
  41.         default:  
  42.             break;  
  43.         }  
  44.     };  
  45. };  

 

 

四. 下載安裝apk文件

 

 

1. 更新對話框

 

(1) 更新流程

 

先彈出更新對話框提示, 點擊確定就彈出進度條對話框, 下載apk文件 . 如果點擊取消, 直接進入主界面

 

更新對話框 : 這是一個AlertDialog , 先創建builder, 然后設置標題, 顯示內容, 設置積極消極按鈕, 創建對話框 之后顯示對話框;

進度條對話框 : 這是一個ProgressDialog, 直接使用new創建, 設置信息與顯示樣式, 最后顯示對話框.

 

(2) 創建對話框流程

 

創建一個對話框的流程 : 

a. 創建builder對象 : Builder builder = new Builder(context);

b. 設置標題 : builder.setTittle("");

c. 設置顯示信息 : builder.setMessage("");

d. 設置按鈕 : builder.setPositiveButton("", onClickListener);

e. 創建對話框 : Dialog dialog = builder.create();

f. 顯示對話框 : dialog.show();

 

創建進度條對話框流程 : 

a. 創建進度條對話框 : ProgressDialog progressDialog = new ProgressDialog(context);

b. 設置進度條對話框樣式 : progressDialog.setProgressStyle();

c. 設置顯示信息 : progressDialog.setMessage();

d. 顯示對話框 : progressDialog.show();

 

(3) 源碼  

  1. /** 
  2.  * 彈出更新對話框 
  3.  *  
  4.  * a. 創建builder對象 
  5.  * b. 設置標題 
  6.  * c. 設置對話框顯示信息 
  7.  * d. 設置該對話框不可回退, 如果回退的話就會卡在本界面 
  8.  * e. 設置確定按鈕 
  9.  * f. 設置取消按鈕 
  10.  * g. 創建對話框 
  11.  * h. 顯示對話框 
  12.  *  
  13.  * 確定按鈕按下顯示進度條對話框 
  14.  * a. 創建一個進度條對話框 
  15.  * b. 設置該對話框不能回退 
  16.  * c. 設置進度條樣式 
  17.  * d. 設置進度條的信息 
  18.  * e. 顯示進度條對話框 
  19.  * f. 開啟一個線程, 下載apk文件 
  20.  */  
  21. protected void showUpdateDialog() {  
  22.     //創建builder對象  
  23.     AlertDialog.Builder builder = new AlertDialog.Builder(this);  
  24.     //設置標題  
  25.     builder.setTitle(getString(R.string.update_dialog_tittle));  
  26.     //設置對話框信息  
  27.     builder.setMessage(updateInfo.getDescription());  
  28.     //設置不可回退  
  29.     builder.setCancelable(false);  
  30.     //設置確定按鈕  
  31.     builder.setPositiveButton(getString(R.string.confirm), new DialogInterface.OnClickListener() {  
  32.         public void onClick(DialogInterface dialog, int which) {  
  33.             //創建進度條對話框  
  34.             mPb = new ProgressDialog(SplashActivity.this);  
  35.             //設置進度條對話框不可回退  
  36.             mPb.setCancelable(false);  
  37.             //設置進度條對話框樣式  
  38.             mPb.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);  
  39.             //設置進度條對話框的信息  
  40.             mPb.setMessage(getString(R.string.update_dialog_messsage));  
  41.             //顯示進度條對話框  
  42.             mPb.show();  
  43.             //開啟顯示進度條對話框線程  
  44.             new Thread(new DownloadApkTask()).start();  
  45.         }  
  46.     });  
  47.     builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {  
  48.         public void onClick(DialogInterface dialog, int which) {  
  49.             loadMainUI();  
  50.         }  
  51.     });  
  52.     //創建更新信息提示對話框  
  53.     mUpdateInfoDialog = builder.create();  
  54.     //顯示更新信息提示對話框  
  55.     mUpdateInfoDialog.show();  
  56. }  

 

 

2. 下載apk線程 

  1. /** 
  2.  * 在這個線程中主要執行downloadApk方法, 這個方法傳入apk路徑和進度條對話框 
  3.  * 注意 : 下載的前提是sd卡的狀態是掛載的 
  4.  */  
  5. private final class DownloadApkTask implements Runnable{  
  6.     public void run() {  
  7.         if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){  
  8.             try {  
  9.                 SystemClock.sleep(2000);  
  10.                 apkFile = downloadApk(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + updateInfo.url,mPb);  
  11.                 Message msg = new Message();  
  12.                 msg.what = SUCCESS_DOWNLOAD_APK;  
  13.                 mHandler.sendMessage(msg);  
  14.             } catch (Exception e) {  
  15.                 e.printStackTrace();  
  16.                 Message msg = new Message();  
  17.                 msg.what = ERROR_DOWNLOAD_APK;  
  18.                 mHandler.sendMessage(msg);  
  19.             }  
  20.         }  
  21.     }  
  22.    }  

 

 

3. 下載apk核心方法

 

 

從網絡下載文件流程 : 

a. 創建URL對象 : 這個對象一般根據字符串地址創建, URL url = new URL(path);

b. 創建HttpURLConnection對象 : 這個對象根據URL對象創建, HttpURLConnection conn = (HttpURLConnection)url.openConnection();

c. 設置超時時間 : 單位是毫秒, conn.setConnectionTimeout(5000);

d. 設置請求方式 : conn.setRequestMethod("GET");

e. 成功連接 : 如果成功連接, 那么conn.getResponseCode()的值為200;

 

進度條對話框設置 : 

a. 設置進度條最大值 : mProgressDialog.setMax(int max);

b. 設置進度條當前值 : mProgressDialog.setProgress(int curr); 

  1. /** 
  2.  * 下載apk更新文件 
  3.  *   
  4.  * a. 根據SD卡路徑創建文件對象, 這個文件用來保存下載的文件 
  5.  * b. 創建URL對象 
  6.  * c. 創建HttpUrlConnection對象 
  7.  * d. 設置鏈接對象超時時間 
  8.  * e. 設置請求方式 get 
  9.  * f. 如果請求成功執行下面的操作 
  10.  *  
  11.  * g. 通過鏈接對象獲取網絡資源的大小 
  12.  * h. 將文件大小設置給進度條對話框 
  13.  * i. 獲取輸入流, 並且讀取輸入流信息 
  14.  * j. 根據讀取到的字節數, 將已經讀取的數據設置給進度條對話框 
  15.  */  
  16. public File downloadApk(String path,ProgressDialog pb) throws Exception{  
  17.     //創建本地文件對象  
  18.     File file = new File(Environment.getExternalStorageDirectory(), getFileName(path));  
  19.     //創建HttpURL連接  
  20.     URL url = new URL(path);  
  21.     HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
  22.     conn.setConnectTimeout(5000);  
  23.     conn.setRequestMethod("GET");  
  24.     if(conn.getResponseCode() == 200){  
  25.         int max = conn.getContentLength();  
  26.         //設置進度條對話框的最大值  
  27.         pb.setMax(max);  
  28.         int count = 0;  
  29.         InputStream is = conn.getInputStream();  
  30.         FileOutputStream fos = new FileOutputStream(file);  
  31.         byte[] buffer = new byte[1024];  
  32.         int len = 0;  
  33.         while((len = is.read(buffer)) != -1){  
  34.             fos.write(buffer, 0, len);  
  35.             //設置進度條對話框進度  
  36.             count = count + len;  
  37.             pb.setProgress(count);  
  38.         }  
  39.         is.close();  
  40.         fos.close();  
  41.     }  
  42.     return file;  
  43. }  

 

 

4. 安裝apk文件 

  1. /** 
  2.  * 安裝apk文件流程 
  3.  *  
  4.  * a. 設置Action : Intent.ACTION_VIEW. 
  5.  * b. 設置數據和類型 : 設置apk文件的uri 和 MIME類型 
  6.  * c. 開啟安裝文件的Activity. 
  7.  */  
  8. protected void installApk() {  
  9.     Intent intent = new Intent();  
  10.     intent.setAction(Intent.ACTION_VIEW);  
  11.     intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");  
  12.     startActivity(intent);  
  13. }  

 

五. 相關的源碼 

 

 

(1) 布局文件

splash.xml 

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:background="@drawable/ivt_splash"   
  6.     android:id="@+id/splash_rl">  
  7.   
  8.     <ProgressBar android:id="@+id/pb"  
  9.         android:layout_width="wrap_content"  
  10.         android:layout_height="wrap_content"  
  11.         android:layout_centerHorizontal="true"  
  12.         android:layout_alignParentBottom="true"  
  13.         android:layout_marginBottom="30dip"/>  
  14.       
  15.     <TextView android:id="@+id/tv_version"  
  16.        android:layout_width="wrap_content"  
  17.         android:layout_height="wrap_content"  
  18.         android:layout_centerHorizontal="true"  
  19.         android:layout_above="@id/pb"  
  20.         android:layout_marginBottom="60dip"  
  21.         android:textSize="30sp"  
  22.         android:textColor="#17A6E8"  
  23.         android:text="version"  
  24.         />  
  25. </RelativeLayout>  



 

 

(2) Activity頁面切換動畫

 

main_in.xml 

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <set xmlns:android="http://schemas.android.com/apk/res/android"  
  3.      >  
  4.     <translate  
  5.         android:fromXDelta="100%p"  
  6.         android:toXDelta="0"  
  7.         android:fromYDelta="0"  
  8.         android:toYDelta="0"   
  9.         android:duration="200"  
  10.         />  
  11. </set>  

 

 

splash_out.xml 

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <set xmlns:android="http://schemas.android.com/apk/res/android"  
  3.      >  
  4.     <translate  
  5.         android:fromXDelta="0"  
  6.         android:toXDelta="-100%p"  
  7.         android:fromYDelta="0"  
  8.         android:toYDelta="0"   
  9.         android:duration="200"  
  10.         />  
  11. </set>  

 

 

(3) SplashActivity源碼

 

SplashActivity.java 

    1. public class SplashActivity extends Activity {  
    2.   
    3.     private static final String TAG = "SplashActivity";  
    4.       
    5.     public static final int ERROR_GET_UPDATEINOF = 0;  
    6.     public static final int SUCESS_GET_UPDATEINOF = 1;  
    7.     public static final int ERROR_DOWNLOAD_APK = 2;  
    8.     public static final int SUCCESS_DOWNLOAD_APK = 3;  
    9.       
    10.     private static final String XML_FILE_DIRECTORY = "updateinfo.xml";  
    11.     private static final String UPDATE_FOLDER_DIRECTORY = "/webupdate/";  
    12.       
    13.     private TextView tv_version;  
    14.     private PackageManager pm;  
    15.     private String version;  
    16.     private UpdateInfo updateInfo;  
    17.       
    18.     private Dialog mUpdateInfoDialog;  
    19.     private ProgressDialog mPb;  
    20.     private File apkFile;  
    21.       
    22.     private RelativeLayout splash_rl;  
    23.     private long time;  
    24.     private SharedPreferences sp;  
    25.       
    26.     private Handler mHandler = new Handler(){  
    27.         public void handleMessage(android.os.Message msg) {  
    28.             switch (msg.what) {  
    29.             /* 
    30.              * 獲取更新信息錯誤 , 在斷網或者獲取信息出現異常執行 
    31.              * 提示一下, 之后進入主界面 
    32.              */  
    33.             case ERROR_GET_UPDATEINOF:  
    34.                 ToastHint.getInstance().showHint(R.string.fail_to_get_updateinfo);  
    35.                 loadMainUI();  
    36.                 break;  
    37.             /* 
    38.              * 成功獲取更新信息, 一般在成功從網上獲取xml文件並解析出來 
    39.              * 如果版本號相同, 說明不用更新, 直接進入主界面 
    40.              * 如果版本號不同, 需要彈出更新對話框 
    41.              */  
    42.             case SUCESS_GET_UPDATEINOF:  
    43.                 if(updateInfo.getVersion().equals(version)){  
    44.                     loadMainUI();  
    45.                 }else{  
    46.                     showUpdateDialog();  
    47.                 }  
    48.                 break;  
    49.             /* 
    50.              * 下載apk文件出現錯誤, 中途斷網 出現異常等情況 
    51.              * 提示后進入主界面 
    52.              */  
    53.             case ERROR_DOWNLOAD_APK:  
    54.                 mPb.dismiss();  
    55.                 ToastHint.getInstance().showHint(R.string.fail_to_get_apk);  
    56.                 loadMainUI();  
    57.                 break;  
    58.             /* 
    59.              * 成功下載apk文件之后執行的操作 
    60.              * 取消進度條對話框, 之后安裝apk文件 
    61.              */  
    62.             case SUCCESS_DOWNLOAD_APK:  
    63.                 mPb.dismiss();  
    64.                 installApk();  
    65.                 break;  
    66.             default:  
    67.                 break;  
    68.             }  
    69.         };  
    70.     };  
    71.       
    72.     /** 
    73.      * 創建Activity時調用 
    74.      *  
    75.      * ① 設置全屏顯示, 由於是Splash界面, 因此不能有標題 
    76.      * ② 設置布局, 版本號, 執行動畫  
    77.      * ③ 設置當前時間 
    78.      * ④ 獲取SharedPerference配置文件 
    79.      * ⑤ 開啟檢查版本號線程, 后續操作都在改線程中操作 
    80.      *  
    81.      */  
    82.     @Override  
    83.     public void onCreate(Bundle savedInstanceState) {  
    84.         super.onCreate(savedInstanceState);  
    85.         //隱藏標題欄  
    86.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
    87.         //隱藏狀態欄  
    88.         getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,   
    89.                 WindowManager.LayoutParams.FLAG_FULLSCREEN);  
    90.         //設置布局  
    91.         setContentView(R.layout.splash);  
    92.           
    93.         /* 
    94.          *  顯示當前軟件的版本號 
    95.          *  獲取布局中的TextView控件, 將版本號設置到這個TextView控件中 
    96.          */  
    97.         tv_version = (TextView) findViewById(R.id.tv_version);  
    98.         version =getString(R.string.current_version) + " " + getVersion();  
    99.         tv_version.setText(version);  
    100.           
    101.         /* 
    102.          *  在界面設置一個動畫, 用來表明正在運行 
    103.          *  a. 獲取布局 
    104.          *  b. 創建一個動畫對象 
    105.          *  c. 將動畫設置到布局中 
    106.          */  
    107.         splash_rl = (RelativeLayout) findViewById(R.id.splash_rl);  
    108.         AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f, 1.0f);  
    109.         alphaAnimation.setDuration(2000);  
    110.         splash_rl.setAnimation(alphaAnimation);  
    111.           
    112.         /* 
    113.          * 這個時間值是用來控制Splash界面顯示時間的 
    114.          * 記錄下這個值, 然后執行到下面, 如果時間差在3秒以內,  
    115.          * 就執行下面的操作, 如果時間差不足3秒, 就Thread.sleep時間差 
    116.          * 等夠3秒在執行下面的操作 
    117.          */  
    118.         time = System.currentTimeMillis();  
    119.   
    120.         //從SharedPreference中獲取一些配置  
    121.         sp = getSharedPreferences("config", Context.MODE_PRIVATE);  
    122.           
    123.         //開啟檢查版本號線程  
    124.         new Thread(new CheckVersionTask()).start();  
    125.     }  
    126.       
    127.     private final class CheckVersionTask implements Runnable{  
    128.         public void run() {  
    129.             try {  
    130.                 /* 
    131.                  * 獲取當前時間, 與onCreate方法中獲取的時間進行比較 
    132.                  * 如果不足3秒, 在等待夠3秒之后在執行下面的操作 
    133.                  */  
    134.                 long temp = System.currentTimeMillis();  
    135.                 if(temp - time < 3000){  
    136.                     SystemClock.sleep(temp - time);  
    137.                 }  
    138.                   
    139.                 /* 
    140.                  * 檢查配置文件中的設置, 是否設置了自動更新;  
    141.                  * 如果設置了自動更新, 就執行下面的操作, 
    142.                  * 如果沒有設置自動更新, 就直接進入主界面 
    143.                  */  
    144.                 boolean is_auto_update = sp.getBoolean("is_auto_update"true);  
    145.                 if(!is_auto_update){  
    146.                     loadMainUI();  
    147.                     return;  
    148.                 }  
    149.                   
    150.                 /* 
    151.                  * 獲取更新信息 
    152.                  * 如果信息不為null, 向handler發信息SUCESS_GET_UPDATEINOF, 執行后續操作 
    153.                  * 如果信息為null, 向handler發信息ERROR_GET_UPDATEINOF, 執行后續操作 
    154.                  * 如果出現異常, 向handler發信息ERROR_GET_UPDATEINOF, 執行后續操作 
    155.                  */  
    156.                 updateInfo = getUpdateInfo(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + XML_FILE_DIRECTORY);  
    157.                 if(updateInfo != null){  
    158.                     Message msg = new Message();  
    159.                     msg.what = SUCESS_GET_UPDATEINOF;  
    160.                     mHandler.sendMessage(msg);  
    161.                 }else{  
    162.                     Message msg = new Message();  
    163.                     msg.what = ERROR_GET_UPDATEINOF;  
    164.                     mHandler.sendMessage(msg);  
    165.                 }  
    166.             } catch (Exception e) {  
    167.                 e.printStackTrace();  
    168.                 Message msg = new Message();  
    169.                 msg.what = ERROR_GET_UPDATEINOF;  
    170.                 mHandler.sendMessage(msg);  
    171.             }  
    172.         }  
    173.     }  
    174.       
    175.     /** 
    176.      * 安裝apk文件流程 
    177.      *  
    178.      * a. 設置Action : Intent.ACTION_VIEW. 
    179.      * b. 設置數據和類型 : 設置apk文件的uri 和 MIME類型 
    180.      * c. 開啟安裝文件的Activity. 
    181.      */  
    182.     protected void installApk() {  
    183.         Intent intent = new Intent();  
    184.         intent.setAction(Intent.ACTION_VIEW);  
    185.         intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");  
    186.         startActivity(intent);  
    187.     }  
    188.       
    189.     /** 
    190.      * 彈出更新對話框 
    191.      *  
    192.      * a. 創建builder對象 
    193.      * b. 設置標題 
    194.      * c. 設置對話框顯示信息 
    195.      * d. 設置該對話框不可回退, 如果回退的話就會卡在本界面 
    196.      * e. 設置確定按鈕 
    197.      * f. 設置取消按鈕 
    198.      * g. 創建對話框 
    199.      * h. 顯示對話框 
    200.      *  
    201.      * 確定按鈕按下顯示進度條對話框 
    202.      * a. 創建一個進度條對話框 
    203.      * b. 設置該對話框不能回退 
    204.      * c. 設置進度條樣式 
    205.      * d. 設置進度條的信息 
    206.      * e. 顯示進度條對話框 
    207.      * f. 開啟一個線程, 下載apk文件 
    208.      */  
    209.     protected void showUpdateDialog() {  
    210.         //創建builder對象  
    211.         AlertDialog.Builder builder = new AlertDialog.Builder(this);  
    212.         //設置標題  
    213.         builder.setTitle(getString(R.string.update_dialog_tittle));  
    214.         //設置對話框信息  
    215.         builder.setMessage(updateInfo.getDescription());  
    216.         //設置不可回退  
    217.         builder.setCancelable(false);  
    218.         //設置確定按鈕  
    219.         builder.setPositiveButton(getString(R.string.confirm), new DialogInterface.OnClickListener() {  
    220.             public void onClick(DialogInterface dialog, int which) {  
    221.                 //創建進度條對話框  
    222.                 mPb = new ProgressDialog(SplashActivity.this);  
    223.                 //設置進度條對話框不可回退  
    224.                 mPb.setCancelable(false);  
    225.                 //設置進度條對話框樣式  
    226.                 mPb.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);  
    227.                 //設置進度條對話框的信息  
    228.                 mPb.setMessage(getString(R.string.update_dialog_messsage));  
    229.                 //顯示進度條對話框  
    230.                 mPb.show();  
    231.                 //開啟顯示進度條對話框線程  
    232.                 new Thread(new DownloadApkTask()).start();  
    233.             }  
    234.         });  
    235.         builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {  
    236.             public void onClick(DialogInterface dialog, int which) {  
    237.                 loadMainUI();  
    238.             }  
    239.         });  
    240.         //創建更新信息提示對話框  
    241.         mUpdateInfoDialog = builder.create();  
    242.         //顯示更新信息提示對話框  
    243.         mUpdateInfoDialog.show();  
    244.     }  
    245.       
    246.     /** 
    247.      * 在這個線程中主要執行downloadApk方法, 這個方法傳入apk路徑和進度條對話框 
    248.      * 注意 : 下載的前提是sd卡的狀態是掛載的 
    249.      */  
    250.     private final class DownloadApkTask implements Runnable{  
    251.         public void run() {  
    252.             if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){  
    253.                 try {  
    254.                     SystemClock.sleep(2000);  
    255.                     apkFile = downloadApk(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + updateInfo.url,mPb);  
    256.                     Message msg = new Message();  
    257.                     msg.what = SUCCESS_DOWNLOAD_APK;  
    258.                     mHandler.sendMessage(msg);  
    259.                 } catch (Exception e) {  
    260.                     e.printStackTrace();  
    261.                     Message msg = new Message();  
    262.                     msg.what = ERROR_DOWNLOAD_APK;  
    263.                     mHandler.sendMessage(msg);  
    264.                 }  
    265.             }  
    266.         }  
    267.     }  
    268.       
    269.     /** 
    270.      * 下載apk更新文件 
    271.      *   
    272.      * a. 根據SD卡路徑創建文件對象, 這個文件用來保存下載的文件 
    273.      * b. 創建URL對象 
    274.      * c. 創建HttpUrlConnection對象 
    275.      * d. 設置鏈接對象超時時間 
    276.      * e. 設置請求方式 get 
    277.      * f. 如果請求成功執行下面的操作 
    278.      *  
    279.      * g. 通過鏈接對象獲取網絡資源的大小 
    280.      * h. 將文件大小設置給進度條對話框 
    281.      * i. 獲取輸入流, 並且讀取輸入流信息 
    282.      * j. 根據讀取到的字節數, 將已經讀取的數據設置給進度條對話框 
    283.      */  
    284.     public File downloadApk(String path,ProgressDialog pb) throws Exception{  
    285.         //創建本地文件對象  
    286.         File file = new File(Environment.getExternalStorageDirectory(), getFileName(path));  
    287.         //創建HttpURL連接  
    288.         URL url = new URL(path);  
    289.         HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
    290.         conn.setConnectTimeout(5000);  
    291.         conn.setRequestMethod("GET");  
    292.         if(conn.getResponseCode() == 200){  
    293.             int max = conn.getContentLength();  
    294.             //設置進度條對話框的最大值  
    295.             pb.setMax(max);  
    296.             int count = 0;  
    297.             InputStream is = conn.getInputStream();  
    298.             FileOutputStream fos = new FileOutputStream(file);  
    299.             byte[] buffer = new byte[1024];  
    300.             int len = 0;  
    301.             while((len = is.read(buffer)) != -1){  
    302.                 fos.write(buffer, 0, len);  
    303.                 //設置進度條對話框進度  
    304.                 count = count + len;  
    305.                 pb.setProgress(count);  
    306.             }  
    307.             is.close();  
    308.             fos.close();  
    309.         }  
    310.         return file;  
    311.     }  
    312.       
    313.     private String getFileName(String path){  
    314.         return path.substring(path.lastIndexOf("/") + 1);  
    315.     }  
    316.       
    317.     private String getVersion() {  
    318.         try {  
    319.             pm = this.getPackageManager();  
    320.             PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);  
    321.             return packageInfo.versionName;  
    322.         } catch (Exception e) {  
    323.             e.printStackTrace();  
    324.         }  
    325.         return null;  
    326.     }  
    327.       
    328.     /** 
    329.      * 獲取更新信息 
    330.      *      ① 根據字符串地址創建URL對象 
    331.      *      ② 根據URL對象創建HttpURLConnection鏈接對象 
    332.      *      ③ 設置鏈接對象5秒超時 
    333.      *      ④ 設置鏈接對象獲取的方式為get方式 
    334.      *      ⑤ 如果成功連接, conn.getRequestCode值就是200, 此時就可以獲取輸入流 
    335.      *      ⑥ 解析輸入流獲取更新信息 
    336.      *       
    337.      */  
    338.     private UpdateInfo getUpdateInfo(String path){  
    339.         try {  
    340.             URL url = new URL(path);    //創建URL對象  
    341.             //創建連接對象  
    342.             HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
    343.             //設置鏈接超時  
    344.             conn.setConnectTimeout(5000);  
    345.             //設置獲取方式  
    346.             conn.setRequestMethod("GET");  
    347.             //如果連接成功, 獲取輸入流  
    348.             if(conn.getResponseCode() == 200){  
    349.                 InputStream is = conn.getInputStream();  
    350.                 //解析輸入流中的數據, 返回更新信息  
    351.                 return parserUpdateInfo(is);  
    352.             }  
    353.         } catch (MalformedURLException e) {  
    354.             e.printStackTrace();  
    355.         } catch (ProtocolException e) {  
    356.             e.printStackTrace();  
    357.         } catch (IOException e) {  
    358.             e.printStackTrace();  
    359.         }  
    360.         return null;  
    361.     }  
    362.       
    363.     /** 
    364.      * 獲取更新信息 
    365.      *      ① 創建pull解析器 
    366.      *      ② 為解析器設置編碼格式 
    367.      *      ③ 獲取解析事件 
    368.      *      ④ 遍歷整個xml文件節點, 獲取標簽元素內容 
    369.      */  
    370.     private UpdateInfo parserUpdateInfo(InputStream is){  
    371.         try {  
    372.             UpdateInfo updateInfo = null;  
    373.             //1. 創建pull解析解析器  
    374.             XmlPullParser parser = Xml.newPullParser();  
    375.             //2. 設置解析編碼  
    376.             parser.setInput(is, "UTF-8");  
    377.             //3. 獲取解析器解事件, 如解析到文檔開始 , 結尾, 標簽等  
    378.             int eventType = parser.getEventType();  
    379.             //4. 在文檔結束前一直解析  
    380.             while (eventType != XmlPullParser.END_DOCUMENT) {  
    381.                 switch (eventType) {  
    382.                 //只解析標簽  
    383.                 case XmlPullParser.START_TAG:  
    384.                     if ("updateInfo".equals(parser.getName())) {  
    385.                         //當解析到updateInfo標簽的時候, 跟標簽開始, 創建一個UpdateInfo對象  
    386.                         updateInfo = new UpdateInfo();  
    387.                     } else if ("version".equals(parser.getName())) {  
    388.                         //解析版本號標簽  
    389.                         updateInfo.setVersion(parser.nextText());  
    390.                     } else if ("url".equals(parser.getName())) {  
    391.                         //解析url標簽  
    392.                         updateInfo.setUrl(parser.nextText());  
    393.                     } else if ("description".equals(parser.getName())) {  
    394.                         //解析描述標簽  
    395.                         updateInfo.setDescription(parser.nextText());  
    396.                     }  
    397.                     break;  
    398.                 default:  
    399.                     break;  
    400.                 }  
    401.                 //每解析完一個元素, 就將解析標志位下移  
    402.                 eventType = parser.next();  
    403.             }  
    404.             is.close();  
    405.             return updateInfo;  
    406.         } catch (XmlPullParserException e) {  
    407.             e.printStackTrace();  
    408.         } catch (IOException e) {  
    409.             e.printStackTrace();  
    410.         }  
    411.         return null;  
    412.     }  
    413.       
    414.     private void loadMainUI(){  
    415.         Intent intent = new Intent(this,HomeActivity.class);  
    416.         startActivity(intent);  
    417.         finish();  
    418.         overridePendingTransition(R.anim.main_in, R.anim.splash_out);  
    419.     }  
    420.       
    421.       
    422.     public class UpdateInfo {  
    423.         private String version; //當前軟件版本號  
    424.         private String url;     //獲取到的軟件地址  
    425.         private String description; //軟件描述  
    426.           
    427.         public String getVersion() {  
    428.             return version;  
    429.         }  
    430.         public void setVersion(String version) {  
    431.             this.version = version;  
    432.         }  
    433.         public String getUrl() {  
    434.             return url;  
    435.         }  
    436.         public void setUrl(String url) {  
    437.             this.url = url;  
    438.         }  
    439.         public String getDescription() {  
    440.             return description;  
    441.         }  
    442.         public void setDescription(String description) {  
    443.             this.description = description;  
    444.         }  
    445.         @Override  
    446.         public String toString() {  
    447.             return "UpdateInfo [version=" + version + ", url=" + url  
    448.                     + ", description=" + description + "]";  
    449.         }  
    450.     }  
    451. }  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM