軟件的自動更新通常都與Splash界面綁定在一塊兒, 因爲須要維護的軟件界面很複雜, 一個Activity中嵌入ViewPager, 而且邏輯比較複雜, 索性從新寫一個Activity, 如今的軟件都很流行使用Splash界面, 正好與自動更新配套在一塊兒;html
在這個自動更新Splash中, 使用到了 動畫設置 ,SharedPerference ,pull解析 ,dialog對話框 ,http網絡編程 ,handler 等.java
注意一個錯誤 : 已安裝具備該名稱和不一樣簽名的數據包 , 早上測試人員報告忽然出現這個問題, 在開發的時候我直接將eclipse上編譯的版本放到了服務器上, 最後出現了這個問題, 開發的時候明明是好的啊, 怎麼測試的時候出問題了呢.linux
編譯環境不一樣, 產生的簽名是不同的, 在eclipse上編譯生成 與 正式版本在linux下編譯 所產生的 數字簽名 是不同的.android
a. 設置全屏顯示.web
b. 設置佈局, 並在佈局中顯示當前版本號, 爲Splash界面添加動畫.編程
c. 獲取當前時間.服務器
d. 獲取SharedPerence配置文件.網絡
e. 開啓檢查版本號線程, 後續的操做都在這個線程中執行.app
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" />
//①設置窗體始終點亮
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);
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 .
爲了更好的用戶體驗, 這裏給Splash界面添加一個動畫, 這個動畫加給整個界面.
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界面.
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) { } });
//獲取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中的變量
/** * 建立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(); }
流程 :
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); } } }
流程 :
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; }
將從網上獲取的更新信息 包括 版本號, 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 + "]"; } }
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()來獲取.
<?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>
/** * 獲取更新信息 * ① 建立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對象用來控制整個更新過程的進行;
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文件 . 若是點擊取消, 直接進入主界面
更新對話框 : 這是一個AlertDialog , 先建立builder, 而後設置標題, 顯示內容, 設置積極消極按鈕, 建立對話框 以後顯示對話框;
進度條對話框 : 這是一個ProgressDialog, 直接使用new建立, 設置信息與顯示樣式, 最後顯示對話框.
建立一個對話框的流程 :
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();
/** * 彈出更新對話框 * * 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); } } } }
從網絡下載文件流程 :
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; }
/** * 安裝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); }
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>
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>
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
*******************************************************************************************************************************