轉載:Android應用的自動更新模塊

軟件的自動更新通常都與Splash界面綁定在一塊兒, 因爲須要維護的軟件界面很複雜, 一個Activity中嵌入ViewPager, 而且邏輯比較複雜, 索性從新寫一個Activity, 如今的軟件都很流行使用Splash界面, 正好與自動更新配套在一塊兒;html

 

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

 

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

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

 

一. 建立Activity

1. 建立Activity大概流程

a. 設置全屏顯示.web

b. 設置佈局, 並在佈局中顯示當前版本號, 爲Splash界面添加動畫.編程

c. 獲取當前時間.服務器

d. 獲取SharedPerence配置文件.網絡

e. 開啓檢查版本號線程, 後續的操做都在這個線程中執行.app

 

2. 設置窗口樣式

(1) 設置全屏顯示

a. 代碼實現 : 因爲是Splash界面, 這裏須要設置成無標題, 而且全屏顯示, 注意下面的兩行代碼須要在setContentView()方法以前調用;eclipse

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

b. 配置實現 :

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

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

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

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

    //③AndroidManifest.xml添加權限  
    <uses-permission android:name="android.permission.WAKE_LOCK" />  
    //設置窗體背景模糊  
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,WindowManager.LayoutParams.FLAG_BLUR_BEHIND);  

(3) 屏幕方向設置

a. 配置文件實現

    //設置橫屏  
    <activity android:name="myAcitivty"  android:screenOrientation="landscape" />       
      
    //設置豎屏  
    <activity android:name="myAcitivty"  android:screenOrientation="portrait" />    

b. 代碼實現

    //設置橫屏  
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);  
      
    //設置豎屏  
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);  

c. 獲取屏幕方向

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

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

3. 設置動畫

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

(1) 建立動畫

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

建立動畫吧: 建立的這個動畫是透明度漸變更畫, 傳入浮點型參數, 0表明徹底透明, 1表明不透明, 傳入參數表明透明度從徹底透明到不透明.

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

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

(2) 動畫經常使用方法

a. 普通設置 

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

b. 設置監聽器

    alphaAnimation.setAnimationListener(new AnimationListener() {  
                //動畫開始時回調  
                @Override  
                public void onAnimationStart(Animation animation) {  
                }  
                //動畫重複執行時回調  
                @Override  
                public void onAnimationRepeat(Animation animation) {  
                }  
                //動畫執行結束時回調  
                @Override  
                public void onAnimationEnd(Animation animation) {  
                }  
            });  

 

4. SharedPerference使用

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

 

5. onCreate()方法代碼 

    /** 
         * 建立Activity時調用 
         *  
         * ① 設置全屏顯示, 因爲是Splash界面, 所以不能有標題 
         * ② 設置佈局, 版本號, 執行動畫  
         * ③ 設置當前時間 
         * ④ 獲取SharedPerference配置文件 
         * ⑤ 開啓檢查版本號線程, 後續操做都在改線程中操做 
         *  
         */  
        @Override  
        public void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            //隱藏標題欄  
            requestWindowFeature(Window.FEATURE_NO_TITLE);  
            //隱藏狀態欄  
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,   
                    WindowManager.LayoutParams.FLAG_FULLSCREEN);  
            //設置佈局  
            setContentView(R.layout.splash);  
              
            /* 
             *  顯示當前軟件的版本號 
             *  獲取佈局中的TextView控件, 將版本號設置到這個TextView控件中 
             */  
            tv_version = (TextView) findViewById(R.id.tv_version);  
            version =getString(R.string.current_version) + " " + getVersion();  
            tv_version.setText(version);  
              
            /* 
             *  在界面設置一個動畫, 用來代表正在運行 
             *  a. 獲取佈局 
             *  b. 建立一個動畫對象 
             *  c. 將動畫設置到佈局中 
             */  
            splash_rl = (RelativeLayout) findViewById(R.id.splash_rl);  
            AlphaAnimation animation = new AlphaAnimation(0.0f, 1.0f);  
            animation.setDuration(2000);  
            splash_rl.setAnimation(animation);  
              
            /* 
             * 這個時間值是用來控制Splash界面顯示時間的 
             * 記錄下這個值, 而後執行到下面, 若是時間差在3秒之內,  
             * 就執行下面的操做, 若是時間差不足3秒, 就Thread.sleep時間差 
             * 等夠3秒在執行下面的操做 
             */  
            time = System.currentTimeMillis();  
      
            //從SharedPreference中獲取一些配置  
            sp = getSharedPreferences("config", Context.MODE_PRIVATE);  
              
            //開啓檢查版本號線程  
            new Thread(new CheckVersionTask()).start();  
        }  

二. 檢查版本號

1. 檢查版本號線程

流程 : 

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

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

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

 

源碼 : 

    private final class CheckVersionTask implements Runnable{  
        public void run() {  
            try {  
                /* 
                 * 獲取當前時間, 與onCreate方法中獲取的時間進行比較 
                 * 若是不足3秒, 在等待夠3秒以後在執行下面的操做 
                 */  
                long temp = System.currentTimeMillis();  
                if(temp - time < 3000){  
                    SystemClock.sleep(temp - time);  
                }  
                  
                /* 
                 * 檢查配置文件中的設置, 是否設置了自動更新;  
                 * 若是設置了自動更新, 就執行下面的操做, 
                 * 若是沒有設置自動更新, 就直接進入主界面 
                 */  
                boolean is_auto_update = sp.getBoolean("is_auto_update", true);  
                if(!is_auto_update){  
                    loadMainUI();  
                    return;  
                }  
                  
                /* 
                 * 獲取更新信息 
                 * 若是信息不爲null, 向handler發信息SUCESS_GET_UPDATEINOF, 執行後續操做 
                 * 若是信息爲null, 向handler發信息ERROR_GET_UPDATEINOF, 執行後續操做 
                 * 若是出現異常, 向handler發信息ERROR_GET_UPDATEINOF, 執行後續操做 
                 */  
                updateInfo = getUpdateInfo(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + XML_FILE_DIRECTORY);  
                if(updateInfo != null){  
                    Message msg = new Message();  
                    msg.what = SUCESS_GET_UPDATEINOF;  
                    mHandler.sendMessage(msg);  
                }else{  
                    Message msg = new Message();  
                    msg.what = ERROR_GET_UPDATEINOF;  
                    mHandler.sendMessage(msg);  
                }  
            } catch (Exception e) {  
                e.printStackTrace();  
                Message msg = new Message();  
                msg.what = ERROR_GET_UPDATEINOF;  
                mHandler.sendMessage(msg);  
            }  
        }  
       }  

2. 獲取版本號方法

流程 : 

a. 創URL建對象;

b. 建立HttpURLConnection對象;

c. 設置超時時間;

d. 設置獲取方式;

e. 查看連接是否成功;

f. 解析輸入流信息;

 

源碼 : 

    /** 
     * 獲取更新信息 
     *      ① 根據字符串地址建立URL對象 
     *      ② 根據URL對象建立HttpURLConnection連接對象 
     *      ③ 設置連接對象5秒超時 
     *      ④ 設置連接對象獲取的方式爲get方式 
     *      ⑤ 若是成功鏈接, conn.getRequestCode值就是200, 此時就能夠獲取輸入流 
     *      ⑥ 解析輸入流獲取更新信息 
     *       
     */  
    private UpdateInfo getUpdateInfo(String path){  
        try {  
            URL url = new URL(path);    //建立URL對象  
            //建立鏈接對象  
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
            //設置連接超時  
            conn.setConnectTimeout(5000);  
            //設置獲取方式  
            conn.setRequestMethod("GET");  
            //若是鏈接成功, 獲取輸入流  
            if(conn.getResponseCode() == 200){  
                InputStream is = conn.getInputStream();  
                //解析輸入流中的數據, 返回更新信息  
                return parserUpdateInfo(is);  
            }  
        } catch (MalformedURLException e) {  
            e.printStackTrace();  
        } catch (ProtocolException e) {  
            e.printStackTrace();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
        return null;  
    }  

3. 更新信息對象

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

    public class UpdateInfo {  
        private String version; //當前軟件版本號  
        private String url;     //獲取到的軟件地址  
        private String description; //軟件描述  
          
        public String getVersion() {  
            return version;  
        }  
        public void setVersion(String version) {  
            this.version = version;  
        }  
        public String getUrl() {  
            return url;  
        }  
        public void setUrl(String url) {  
            this.url = url;  
        }  
        public String getDescription() {  
            return description;  
        }  
        public void setDescription(String description) {  
            this.description = description;  
        }  
        @Override  
        public String toString() {  
            return "UpdateInfo [version=" + version + ", url=" + url  
                    + ", description=" + description + "]";  
        }  
    }  

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文件

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

(3) 源碼

    /** 
     * 獲取更新信息 
     *      ① 建立pull解析器 
     *      ② 爲解析器設置編碼格式 
     *      ③ 獲取解析事件 
     *      ④ 遍歷整個xml文件節點, 獲取標籤元素內容 
     */  
    private UpdateInfo parserUpdateInfo(InputStream is){  
        try {  
            UpdateInfo updateInfo = null;  
            //1. 建立pull解析解析器  
            XmlPullParser parser = Xml.newPullParser();  
            //2. 設置解析編碼  
            parser.setInput(is, "UTF-8");  
            //3. 獲取解析器解事件, 如解析到文檔開始 , 結尾, 標籤等  
            int eventType = parser.getEventType();  
            //4. 在文檔結束前一直解析  
            while (eventType != XmlPullParser.END_DOCUMENT) {  
                switch (eventType) {  
                //只解析標籤  
                case XmlPullParser.START_TAG:  
                    if ("updateInfo".equals(parser.getName())) {  
                        //當解析到updateInfo標籤的時候, 跟標籤開始, 建立一個UpdateInfo對象  
                        updateInfo = new UpdateInfo();  
                    } else if ("version".equals(parser.getName())) {  
                        //解析版本號標籤  
                        updateInfo.setVersion(parser.nextText());  
                    } else if ("url".equals(parser.getName())) {  
                        //解析url標籤  
                        updateInfo.setUrl(parser.nextText());  
                    } else if ("description".equals(parser.getName())) {  
                        //解析描述標籤  
                        updateInfo.setDescription(parser.nextText());  
                    }  
                    break;  
                default:  
                    break;  
                }  
                //每解析完一個元素, 就將解析標誌位下移  
                eventType = parser.next();  
            }  
            is.close();  
            return updateInfo;  
        } catch (XmlPullParserException e) {  
            e.printStackTrace();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
        return null;  
    }  

 

三. Handler對象

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

    private Handler mHandler = new Handler(){  
        public void handleMessage(android.os.Message msg) {  
            switch (msg.what) {  
            /* 
             * 獲取更新信息錯誤 , 在斷網或者獲取信息出現異常執行 
             * 提示一下, 以後進入主界面 
             */  
            case ERROR_GET_UPDATEINOF:  
                ToastHint.getInstance().showHint(R.string.fail_to_get_updateinfo);  
                loadMainUI();  
                break;  
            /* 
             * 成功獲取更新信息, 通常在成功從網上獲取xml文件並解析出來 
             * 若是版本號相同, 說明不用更新, 直接進入主界面 
             * 若是版本號不一樣, 須要彈出更新對話框 
             */  
            case SUCESS_GET_UPDATEINOF:  
                if(updateInfo.getVersion().equals(version)){  
                    loadMainUI();  
                }else{  
                    showUpdateDialog();  
                }  
                break;  
            /* 
             * 下載apk文件出現錯誤, 中途斷網 出現異常等狀況 
             * 提示後進入主界面 
             */  
            case ERROR_DOWNLOAD_APK:  
                mPb.dismiss();  
                ToastHint.getInstance().showHint(R.string.fail_to_get_apk);  
                loadMainUI();  
                break;  
            /* 
             * 成功下載apk文件以後執行的操做 
             * 取消進度條對話框, 以後安裝apk文件 
             */  
            case SUCCESS_DOWNLOAD_APK:  
                mPb.dismiss();  
                installApk();  
                break;  
            default:  
                break;  
            }  
        };  
    };  

 

四. 下載安裝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) 源碼 

    /** 
     * 彈出更新對話框 
     *  
     * a. 建立builder對象 
     * b. 設置標題 
     * c. 設置對話框顯示信息 
     * d. 設置該對話框不可回退, 若是回退的話就會卡在本界面 
     * e. 設置肯定按鈕 
     * f. 設置取消按鈕 
     * g. 建立對話框 
     * h. 顯示對話框 
     *  
     * 肯定按鈕按下顯示進度條對話框 
     * a. 建立一個進度條對話框 
     * b. 設置該對話框不能回退 
     * c. 設置進度條樣式 
     * d. 設置進度條的信息 
     * e. 顯示進度條對話框 
     * f. 開啓一個線程, 下載apk文件 
     */  
    protected void showUpdateDialog() {  
        //建立builder對象  
        AlertDialog.Builder builder = new AlertDialog.Builder(this);  
        //設置標題  
        builder.setTitle(getString(R.string.update_dialog_tittle));  
        //設置對話框信息  
        builder.setMessage(updateInfo.getDescription());  
        //設置不可回退  
        builder.setCancelable(false);  
        //設置肯定按鈕  
        builder.setPositiveButton(getString(R.string.confirm), new DialogInterface.OnClickListener() {  
            public void onClick(DialogInterface dialog, int which) {  
                //建立進度條對話框  
                mPb = new ProgressDialog(SplashActivity.this);  
                //設置進度條對話框不可回退  
                mPb.setCancelable(false);  
                //設置進度條對話框樣式  
                mPb.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);  
                //設置進度條對話框的信息  
                mPb.setMessage(getString(R.string.update_dialog_messsage));  
                //顯示進度條對話框  
                mPb.show();  
                //開啓顯示進度條對話框線程  
                new Thread(new DownloadApkTask()).start();  
            }  
        });  
        builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {  
            public void onClick(DialogInterface dialog, int which) {  
                loadMainUI();  
            }  
        });  
        //建立更新信息提示對話框  
        mUpdateInfoDialog = builder.create();  
        //顯示更新信息提示對話框  
        mUpdateInfoDialog.show();  
    }  

2. 下載apk線程

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

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

    /** 
     * 下載apk更新文件 
     *   
     * a. 根據SD卡路徑建立文件對象, 這個文件用來保存下載的文件 
     * b. 建立URL對象 
     * c. 建立HttpUrlConnection對象 
     * d. 設置連接對象超時時間 
     * e. 設置請求方式 get 
     * f. 若是請求成功執行下面的操做 
     *  
     * g. 經過連接對象獲取網絡資源的大小 
     * h. 將文件大小設置給進度條對話框 
     * i. 獲取輸入流, 而且讀取輸入流信息 
     * j. 根據讀取到的字節數, 將已經讀取的數據設置給進度條對話框 
     */  
    public File downloadApk(String path,ProgressDialog pb) throws Exception{  
        //建立本地文件對象  
        File file = new File(Environment.getExternalStorageDirectory(), getFileName(path));  
        //建立HttpURL鏈接  
        URL url = new URL(path);  
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
        conn.setConnectTimeout(5000);  
        conn.setRequestMethod("GET");  
        if(conn.getResponseCode() == 200){  
            int max = conn.getContentLength();  
            //設置進度條對話框的最大值  
            pb.setMax(max);  
            int count = 0;  
            InputStream is = conn.getInputStream();  
            FileOutputStream fos = new FileOutputStream(file);  
            byte[] buffer = new byte[1024];  
            int len = 0;  
            while((len = is.read(buffer)) != -1){  
                fos.write(buffer, 0, len);  
                //設置進度條對話框進度  
                count = count + len;  
                pb.setProgress(count);  
            }  
            is.close();  
            fos.close();  
        }  
        return file;  
    }  

4. 安裝apk文件

    /** 
     * 安裝apk文件流程 
     *  
     * a. 設置Action : Intent.ACTION_VIEW. 
     * b. 設置數據和類型 : 設置apk文件的uri 和 MIME類型 
     * c. 開啓安裝文件的Activity. 
     */  
    protected void installApk() {  
        Intent intent = new Intent();  
        intent.setAction(Intent.ACTION_VIEW);  
        intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");  
        startActivity(intent);  
    }  

五. 相關的源碼 

 

 

(1) 佈局文件

splash.xml

    <?xml version="1.0" encoding="utf-8"?>  
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        android:layout_width="match_parent"  
        android:layout_height="match_parent"  
        android:background="@drawable/ivt_splash"   
        android:id="@+id/splash_rl">  
      
        <ProgressBar android:id="@+id/pb"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:layout_centerHorizontal="true"  
            android:layout_alignParentBottom="true"  
            android:layout_marginBottom="30dip"/>  
          
        <TextView android:id="@+id/tv_version"  
           android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:layout_centerHorizontal="true"  
            android:layout_above="@id/pb"  
            android:layout_marginBottom="60dip"  
            android:textSize="30sp"  
            android:textColor="#17A6E8"  
            android:text="version"  
            />  
    </RelativeLayout>  

(2) Activity頁面切換動畫

 

main_in.xml

<?xml version="1.0" encoding="utf-8"?>  
<set xmlns:android="http://schemas.android.com/apk/res/android"  
     >  
    <translate  
        android:fromXDelta="100%p"  
        android:toXDelta="0"  
        android:fromYDelta="0"  
        android:toYDelta="0"   
        android:duration="200"  
        />  
</set> 

splash_out.xml

    <?xml version="1.0" encoding="utf-8"?>  
    <set xmlns:android="http://schemas.android.com/apk/res/android"  
         >  
        <translate  
            android:fromXDelta="0"  
            android:toXDelta="-100%p"  
            android:fromYDelta="0"  
            android:toYDelta="0"   
            android:duration="200"  
            />  
    </set>  

(3) SplashActivity源碼

 

SplashActivity.java

        public class SplashActivity extends Activity {  
          
            private static final String TAG = "SplashActivity";  
              
            public static final int ERROR_GET_UPDATEINOF = 0;  
            public static final int SUCESS_GET_UPDATEINOF = 1;  
            public static final int ERROR_DOWNLOAD_APK = 2;  
            public static final int SUCCESS_DOWNLOAD_APK = 3;  
              
            private static final String XML_FILE_DIRECTORY = "updateinfo.xml";  
            private static final String UPDATE_FOLDER_DIRECTORY = "/webupdate/";  
              
            private TextView tv_version;  
            private PackageManager pm;  
            private String version;  
            private UpdateInfo updateInfo;  
              
            private Dialog mUpdateInfoDialog;  
            private ProgressDialog mPb;  
            private File apkFile;  
              
            private RelativeLayout splash_rl;  
            private long time;  
            private SharedPreferences sp;  
              
            private Handler mHandler = new Handler(){  
                public void handleMessage(android.os.Message msg) {  
                    switch (msg.what) {  
                    /* 
                     * 獲取更新信息錯誤 , 在斷網或者獲取信息出現異常執行 
                     * 提示一下, 以後進入主界面 
                     */  
                    case ERROR_GET_UPDATEINOF:  
                        ToastHint.getInstance().showHint(R.string.fail_to_get_updateinfo);  
                        loadMainUI();  
                        break;  
                    /* 
                     * 成功獲取更新信息, 通常在成功從網上獲取xml文件並解析出來 
                     * 若是版本號相同, 說明不用更新, 直接進入主界面 
                     * 若是版本號不一樣, 須要彈出更新對話框 
                     */  
                    case SUCESS_GET_UPDATEINOF:  
                        if(updateInfo.getVersion().equals(version)){  
                            loadMainUI();  
                        }else{  
                            showUpdateDialog();  
                        }  
                        break;  
                    /* 
                     * 下載apk文件出現錯誤, 中途斷網 出現異常等狀況 
                     * 提示後進入主界面 
                     */  
                    case ERROR_DOWNLOAD_APK:  
                        mPb.dismiss();  
                        ToastHint.getInstance().showHint(R.string.fail_to_get_apk);  
                        loadMainUI();  
                        break;  
                    /* 
                     * 成功下載apk文件以後執行的操做 
                     * 取消進度條對話框, 以後安裝apk文件 
                     */  
                    case SUCCESS_DOWNLOAD_APK:  
                        mPb.dismiss();  
                        installApk();  
                        break;  
                    default:  
                        break;  
                    }  
                };  
            };  
              
            /** 
             * 建立Activity時調用 
             *  
             * ① 設置全屏顯示, 因爲是Splash界面, 所以不能有標題 
             * ② 設置佈局, 版本號, 執行動畫  
             * ③ 設置當前時間 
             * ④ 獲取SharedPerference配置文件 
             * ⑤ 開啓檢查版本號線程, 後續操做都在改線程中操做 
             *  
             */  
            @Override  
            public void onCreate(Bundle savedInstanceState) {  
                super.onCreate(savedInstanceState);  
                //隱藏標題欄  
                requestWindowFeature(Window.FEATURE_NO_TITLE);  
                //隱藏狀態欄  
                getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,   
                        WindowManager.LayoutParams.FLAG_FULLSCREEN);  
                //設置佈局  
                setContentView(R.layout.splash);  
                  
                /* 
                 *  顯示當前軟件的版本號 
                 *  獲取佈局中的TextView控件, 將版本號設置到這個TextView控件中 
                 */  
                tv_version = (TextView) findViewById(R.id.tv_version);  
                version =getString(R.string.current_version) + " " + getVersion();  
                tv_version.setText(version);  
                  
                /* 
                 *  在界面設置一個動畫, 用來代表正在運行 
                 *  a. 獲取佈局 
                 *  b. 建立一個動畫對象 
                 *  c. 將動畫設置到佈局中 
                 */  
                splash_rl = (RelativeLayout) findViewById(R.id.splash_rl);  
                AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f, 1.0f);  
                alphaAnimation.setDuration(2000);  
                splash_rl.setAnimation(alphaAnimation);  
                  
                /* 
                 * 這個時間值是用來控制Splash界面顯示時間的 
                 * 記錄下這個值, 而後執行到下面, 若是時間差在3秒之內,  
                 * 就執行下面的操做, 若是時間差不足3秒, 就Thread.sleep時間差 
                 * 等夠3秒在執行下面的操做 
                 */  
                time = System.currentTimeMillis();  
          
                //從SharedPreference中獲取一些配置  
                sp = getSharedPreferences("config", Context.MODE_PRIVATE);  
                  
                //開啓檢查版本號線程  
                new Thread(new CheckVersionTask()).start();  
            }  
              
            private final class CheckVersionTask implements Runnable{  
                public void run() {  
                    try {  
                        /* 
                         * 獲取當前時間, 與onCreate方法中獲取的時間進行比較 
                         * 若是不足3秒, 在等待夠3秒以後在執行下面的操做 
                         */  
                        long temp = System.currentTimeMillis();  
                        if(temp - time < 3000){  
                            SystemClock.sleep(temp - time);  
                        }  
                          
                        /* 
                         * 檢查配置文件中的設置, 是否設置了自動更新;  
                         * 若是設置了自動更新, 就執行下面的操做, 
                         * 若是沒有設置自動更新, 就直接進入主界面 
                         */  
                        boolean is_auto_update = sp.getBoolean("is_auto_update", true);  
                        if(!is_auto_update){  
                            loadMainUI();  
                            return;  
                        }  
                          
                        /* 
                         * 獲取更新信息 
                         * 若是信息不爲null, 向handler發信息SUCESS_GET_UPDATEINOF, 執行後續操做 
                         * 若是信息爲null, 向handler發信息ERROR_GET_UPDATEINOF, 執行後續操做 
                         * 若是出現異常, 向handler發信息ERROR_GET_UPDATEINOF, 執行後續操做 
                         */  
                        updateInfo = getUpdateInfo(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + XML_FILE_DIRECTORY);  
                        if(updateInfo != null){  
                            Message msg = new Message();  
                            msg.what = SUCESS_GET_UPDATEINOF;  
                            mHandler.sendMessage(msg);  
                        }else{  
                            Message msg = new Message();  
                            msg.what = ERROR_GET_UPDATEINOF;  
                            mHandler.sendMessage(msg);  
                        }  
                    } catch (Exception e) {  
                        e.printStackTrace();  
                        Message msg = new Message();  
                        msg.what = ERROR_GET_UPDATEINOF;  
                        mHandler.sendMessage(msg);  
                    }  
                }  
            }  
              
            /** 
             * 安裝apk文件流程 
             *  
             * a. 設置Action : Intent.ACTION_VIEW. 
             * b. 設置數據和類型 : 設置apk文件的uri 和 MIME類型 
             * c. 開啓安裝文件的Activity. 
             */  
            protected void installApk() {  
                Intent intent = new Intent();  
                intent.setAction(Intent.ACTION_VIEW);  
                intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");  
                startActivity(intent);  
            }  
              
            /** 
             * 彈出更新對話框 
             *  
             * a. 建立builder對象 
             * b. 設置標題 
             * c. 設置對話框顯示信息 
             * d. 設置該對話框不可回退, 若是回退的話就會卡在本界面 
             * e. 設置肯定按鈕 
             * f. 設置取消按鈕 
             * g. 建立對話框 
             * h. 顯示對話框 
             *  
             * 肯定按鈕按下顯示進度條對話框 
             * a. 建立一個進度條對話框 
             * b. 設置該對話框不能回退 
             * c. 設置進度條樣式 
             * d. 設置進度條的信息 
             * e. 顯示進度條對話框 
             * f. 開啓一個線程, 下載apk文件 
             */  
            protected void showUpdateDialog() {  
                //建立builder對象  
                AlertDialog.Builder builder = new AlertDialog.Builder(this);  
                //設置標題  
                builder.setTitle(getString(R.string.update_dialog_tittle));  
                //設置對話框信息  
                builder.setMessage(updateInfo.getDescription());  
                //設置不可回退  
                builder.setCancelable(false);  
                //設置肯定按鈕  
                builder.setPositiveButton(getString(R.string.confirm), new DialogInterface.OnClickListener() {  
                    public void onClick(DialogInterface dialog, int which) {  
                        //建立進度條對話框  
                        mPb = new ProgressDialog(SplashActivity.this);  
                        //設置進度條對話框不可回退  
                        mPb.setCancelable(false);  
                        //設置進度條對話框樣式  
                        mPb.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);  
                        //設置進度條對話框的信息  
                        mPb.setMessage(getString(R.string.update_dialog_messsage));  
                        //顯示進度條對話框  
                        mPb.show();  
                        //開啓顯示進度條對話框線程  
                        new Thread(new DownloadApkTask()).start();  
                    }  
                });  
                builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {  
                    public void onClick(DialogInterface dialog, int which) {  
                        loadMainUI();  
                    }  
                });  
                //建立更新信息提示對話框  
                mUpdateInfoDialog = builder.create();  
                //顯示更新信息提示對話框  
                mUpdateInfoDialog.show();  
            }  
              
            /** 
             * 在這個線程中主要執行downloadApk方法, 這個方法傳入apk路徑和進度條對話框 
             * 注意 : 下載的前提是sd卡的狀態是掛載的 
             */  
            private final class DownloadApkTask implements Runnable{  
                public void run() {  
                    if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){  
                        try {  
                            SystemClock.sleep(2000);  
                            apkFile = downloadApk(SettingsFactory.readWebLoadUrl(getApplicationContext()) + UPDATE_FOLDER_DIRECTORY + updateInfo.url,mPb);  
                            Message msg = new Message();  
                            msg.what = SUCCESS_DOWNLOAD_APK;  
                            mHandler.sendMessage(msg);  
                        } catch (Exception e) {  
                            e.printStackTrace();  
                            Message msg = new Message();  
                            msg.what = ERROR_DOWNLOAD_APK;  
                            mHandler.sendMessage(msg);  
                        }  
                    }  
                }  
            }  
              
            /** 
             * 下載apk更新文件 
             *   
             * a. 根據SD卡路徑建立文件對象, 這個文件用來保存下載的文件 
             * b. 建立URL對象 
             * c. 建立HttpUrlConnection對象 
             * d. 設置連接對象超時時間 
             * e. 設置請求方式 get 
             * f. 若是請求成功執行下面的操做 
             *  
             * g. 經過連接對象獲取網絡資源的大小 
             * h. 將文件大小設置給進度條對話框 
             * i. 獲取輸入流, 而且讀取輸入流信息 
             * j. 根據讀取到的字節數, 將已經讀取的數據設置給進度條對話框 
             */  
            public File downloadApk(String path,ProgressDialog pb) throws Exception{  
                //建立本地文件對象  
                File file = new File(Environment.getExternalStorageDirectory(), getFileName(path));  
                //建立HttpURL鏈接  
                URL url = new URL(path);  
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
                conn.setConnectTimeout(5000);  
                conn.setRequestMethod("GET");  
                if(conn.getResponseCode() == 200){  
                    int max = conn.getContentLength();  
                    //設置進度條對話框的最大值  
                    pb.setMax(max);  
                    int count = 0;  
                    InputStream is = conn.getInputStream();  
                    FileOutputStream fos = new FileOutputStream(file);  
                    byte[] buffer = new byte[1024];  
                    int len = 0;  
                    while((len = is.read(buffer)) != -1){  
                        fos.write(buffer, 0, len);  
                        //設置進度條對話框進度  
                        count = count + len;  
                        pb.setProgress(count);  
                    }  
                    is.close();  
                    fos.close();  
                }  
                return file;  
            }  
              
            private String getFileName(String path){  
                return path.substring(path.lastIndexOf("/") + 1);  
            }  
              
            private String getVersion() {  
                try {  
                    pm = this.getPackageManager();  
                    PackageInfo packageInfo = pm.getPackageInfo(getPackageName(), 0);  
                    return packageInfo.versionName;  
                } catch (Exception e) {  
                    e.printStackTrace();  
                }  
                return null;  
            }  
              
            /** 
             * 獲取更新信息 
             *      ① 根據字符串地址建立URL對象 
             *      ② 根據URL對象建立HttpURLConnection連接對象 
             *      ③ 設置連接對象5秒超時 
             *      ④ 設置連接對象獲取的方式爲get方式 
             *      ⑤ 若是成功鏈接, conn.getRequestCode值就是200, 此時就能夠獲取輸入流 
             *      ⑥ 解析輸入流獲取更新信息 
             *       
             */  
            private UpdateInfo getUpdateInfo(String path){  
                try {  
                    URL url = new URL(path);    //建立URL對象  
                    //建立鏈接對象  
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
                    //設置連接超時  
                    conn.setConnectTimeout(5000);  
                    //設置獲取方式  
                    conn.setRequestMethod("GET");  
                    //若是鏈接成功, 獲取輸入流  
                    if(conn.getResponseCode() == 200){  
                        InputStream is = conn.getInputStream();  
                        //解析輸入流中的數據, 返回更新信息  
                        return parserUpdateInfo(is);  
                    }  
                } catch (MalformedURLException e) {  
                    e.printStackTrace();  
                } catch (ProtocolException e) {  
                    e.printStackTrace();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
                return null;  
            }  
              
            /** 
             * 獲取更新信息 
             *      ① 建立pull解析器 
             *      ② 爲解析器設置編碼格式 
             *      ③ 獲取解析事件 
             *      ④ 遍歷整個xml文件節點, 獲取標籤元素內容 
             */  
            private UpdateInfo parserUpdateInfo(InputStream is){  
                try {  
                    UpdateInfo updateInfo = null;  
                    //1. 建立pull解析解析器  
                    XmlPullParser parser = Xml.newPullParser();  
                    //2. 設置解析編碼  
                    parser.setInput(is, "UTF-8");  
                    //3. 獲取解析器解事件, 如解析到文檔開始 , 結尾, 標籤等  
                    int eventType = parser.getEventType();  
                    //4. 在文檔結束前一直解析  
                    while (eventType != XmlPullParser.END_DOCUMENT) {  
                        switch (eventType) {  
                        //只解析標籤  
                        case XmlPullParser.START_TAG:  
                            if ("updateInfo".equals(parser.getName())) {  
                                //當解析到updateInfo標籤的時候, 跟標籤開始, 建立一個UpdateInfo對象  
                                updateInfo = new UpdateInfo();  
                            } else if ("version".equals(parser.getName())) {  
                                //解析版本號標籤  
                                updateInfo.setVersion(parser.nextText());  
                            } else if ("url".equals(parser.getName())) {  
                                //解析url標籤  
                                updateInfo.setUrl(parser.nextText());  
                            } else if ("description".equals(parser.getName())) {  
                                //解析描述標籤  
                                updateInfo.setDescription(parser.nextText());  
                            }  
                            break;  
                        default:  
                            break;  
                        }  
                        //每解析完一個元素, 就將解析標誌位下移  
                        eventType = parser.next();  
                    }  
                    is.close();  
                    return updateInfo;  
                } catch (XmlPullParserException e) {  
                    e.printStackTrace();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
                return null;  
            }  
              
            private void loadMainUI(){  
                Intent intent = new Intent(this,HomeActivity.class);  
                startActivity(intent);  
                finish();  
                overridePendingTransition(R.anim.main_in, R.anim.splash_out);  
            }  
              
              
            public class UpdateInfo {  
                private String version; //當前軟件版本號  
                private String url;     //獲取到的軟件地址  
                private String description; //軟件描述  
                  
                public String getVersion() {  
                    return version;  
                }  
                public void setVersion(String version) {  
                    this.version = version;  
                }  
                public String getUrl() {  
                    return url;  
                }  
                public void setUrl(String url) {  
                    this.url = url;  
                }  
                public String getDescription() {  
                    return description;  
                }  
                public void setDescription(String description) {  
                    this.description = description;  
                }  
                @Override  
                public String toString() {  
                    return "UpdateInfo [version=" + version + ", url=" + url  
                            + ", description=" + description + "]";  
                }  
            }  
        }  

 

 

 

*******************************************************************************************************************************

感謝原文做者:安卓吧

原文地址: http://www.cnblogs.com/android100/p/android-auto-update.html

*******************************************************************************************************************************

相關文章
相關標籤/搜索