標籤:android 對話方塊 多線程
配置應用程式在手機案頭顯示的名稱和表徵圖-AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.liuhao.mobilesafe" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher"<span style="color:#FF0000;">《在程式管理列表中顯示的表徵圖》 ①</span> android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:icon="@drawable/icon5"<span style="color:#FF0000;">《在案頭顯示為自己配置的icon5表徵圖》 ②</span> android:name="com.liuhao.mobilesafe.ui.SplashActivity" android:label="@string/app_name" ><span style="color:#FF0000;">《名稱為自己配置的app_name》 ②</span> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest>
配置後,顯示
① ②
擷取更新的伺服器配置流程:
伺服器配置:
以tomcat作為伺服器,在TOMCAT_HOME/ROOT目錄下建立update.xml檔案,在其中添加新版本的相關資訊;
<?xml version="1.0" encoding="utf-8"?><info> <version>2.0</version> <description>親,最新的版本,速度來下載!</description> <apkurl>http://localhost:18081/newapk.apk</apkurl></info>
在瀏覽器訪問:http://localhost:18081/update.xml
xml設定檔的擷取和解析
那麼我們的應用程式啟動時就要嘗試到上述地址擷取新版本的資訊,同時對xml設定檔進行解析。
那麼應用程式如何擷取到上述的版本資訊的地址呢?一般在資源檔中以設定檔的方式儲存。
config.xml<?xml version="1.0" encoding="utf-8"?><resources> <string name="updateurl">http://192.168.1.123:18081/update.xml</string></resources>
下面要建立update.xml檔案對應的實體類-UpdateInfo.java:
package com.liuhao.mobilesafe.domain;/*** @author liuhao* 升級資訊*/public class UpdateInfo { String version; String description; String apkurl; public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getApkurl() { return apkurl; } public void setApkurl(String apkurl) { this.apkurl = apkurl; }}
如何擷取這個config.xml裡url對應的檔案內容(即http://192.168.1.123:18081/update.xml)?
建立更新資訊服務類:UpdateInfoService.java:
package com.liuhao.mobilesafe.engine;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.URL;import android.content.Context;import com.liuhao.mobilesafe.domain.UpdateInfo;public class UpdateInfoService { private Context context; // 應用程式環境的上下文資訊 public UpdateInfoService(Context context) { this.context = context; } /** * @param urlId * 伺服器資源路徑對應的id * @return 更新資訊 * @throws Exception */ public UpdateInfo getUpdateInfo(int urlId) throws Exception { String path = context.getResources().getString(urlId);// 根據urlId擷取資源檔中對應的內容 URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(2000); conn.setRequestMethod("GET"); InputStream is = conn.getInputStream(); //得到url對應的檔案流,應該是xml檔案流,需要對其進行解析 return UpdateInfoParser.getUpdateInfo(is); }}
- 知識點:為什麼在業務類中不對異常進行捕獲,而是直接拋出了?
向外傳播給更高層處理,以便異常的錯誤原因不丟失,便於排查錯誤或進行捕獲處理。對於異常處理,應該從設計、需要、維護等多個角度綜合考慮,有一個通用準則:千萬別捕獲了異常什麼事情都不幹,這樣一旦出現異常了,你沒法依據異常資訊來排錯。
見:J2EE系統異常的處理準則
解析xml檔案:
擷取到xml檔案流後,要對其進行解析,使用XmlPullParser:
XmlPullParser將xml分解成不同的事件類型(EventType)
常用的有:
XmlPullParser.END_DOCUMENT:文檔的結束
XmlPullParser.START_DOCUMENT:文檔的開始
XmlPullParser.START_TAG:標籤的開始
XmlPullParser.END_TAG:標籤的結束
XmlPullParser.TEXT :內容
並且該類中的方法主要是用於擷取EventType的內容,以及在EventType之間進行跳轉。
建立解析更新資訊的工具服務類UpdateInfoParser:
package com.liuhao.mobilesafe.engine;import java.io.InputStream;import org.xmlpull.v1.XmlPullParser;import android.util.Xml;import com.liuhao.mobilesafe.domain.UpdateInfo;public class UpdateInfoParser { /** * @param is xml格式的檔案輸入資料流 * @return 解析好的UpdateInfo */ public static UpdateInfo getUpdateInfo(InputStream is) throws Exception{ XmlPullParser parser = Xml.newPullParser(); UpdateInfo info = new UpdateInfo(); // 初始化parser解析器,設定準備對哪個輸入資料流進行解析 // 這個方法會對parser進行重設,同時會將事件類型(event type)定位到文檔初始位置(START_DOCUMENT) parser.setInput(is, "utf-8"); int type = parser.getEventType(); //擷取當前的EventType while(type != XmlPullParser.END_DOCUMENT){ switch (type) { // 對其中的標籤類型進行處理 case XmlPullParser.START_TAG: if("version".equals(parser.getName())){ String version = parser.nextText(); info.setVersion(version); } else if("description".equals(parser.getName())){ String description = parser.nextText(); info.setDescription(description); } else if("apkurl".equals(parser.getName())){ String apkurl = parser.nextText(); info.setApkurl(apkurl); } break; } type = parser.next(); } return info; } }
測試不知道Android如何測試?
1、建立一個Android Test Project,將我們的項目放在測試專案中。
2、將test項目中AndroidManifest.xml的<uses-library android:name="android.test.runner" />內容和<instrumentation>節點下的內容拷貝到項目的AndroidManifest.xml中,注意節點的對應。
之後,test項目便可以暫時不用了。
3、建立測試類別
package com.liuhao.mobilesafe.test;import junit.framework.Assert;import com.liuhao.mobilesafe.R;import com.liuhao.mobilesafe.domain.UpdateInfo;import com.liuhao.mobilesafe.engine.UpdateInfoService;import android.test.AndroidTestCase;public class TestGetUpdateInfo extends AndroidTestCase { public void testGetInfo() throws Exception{ UpdateInfoService service = new UpdateInfoService(getContext()); UpdateInfo info = service.getUpdateInfo(R.string.updateurl); Assert.assertEquals("2.0", info.getVersion()); } }
4、從伺服器上擷取更新資訊的設定檔,需要程式有訪問Internet的許可權:
儲存,即可。
5、運行測試代碼:
出現異常!!!connect failed: ECONNREFUSED (Connection refused)
異常處理:java.net.ConnectException
android 從tomcat讀取檔案時出現以下異常:
08-10 14:53:09.118: W/System.err(12527): java.net.ConnectException: failed to connect to localhost/127.0.0.1 (port 8080): connect failed: ECONNREFUSED (Connection refused)
解決方案:
String url = "http://localhost:18081/update.xml"; 修改成 String url = "http://192.168.1.123:18081/update.xml";
主機ip不能使用localhost或者127.0.0.1,使用本機真實ip地址即可。使用ipconfig命令就可以查看到:
異常處理後,運行成功!
在activity使用業務
所有的業務代碼已經完成,回到splash的activity使用業務!
package com.liuhao.mobilesafe.ui;import com.liuhao.mobilesafe.R;import com.liuhao.mobilesafe.domain.UpdateInfo;import com.liuhao.mobilesafe.engine.UpdateInfoService;import android.os.Bundle;import android.app.Activity;import android.app.AlertDialog;import android.app.AlertDialog.Builder;import android.content.DialogInterface;import android.content.DialogInterface.OnClickListener;import android.content.pm.PackageInfo;import android.content.pm.PackageManager;import android.util.Log;import android.view.Menu;import android.view.Window;import android.view.WindowManager;import android.view.animation.AlphaAnimation;import android.widget.LinearLayout;import android.widget.TextView;import android.widget.Toast;public class SplashActivity extends Activity {private static final String TAG = "SplashActivity";private TextView tv_splash_version;private LinearLayout ll_splash_main;private UpdateInfo info; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //取消標題列 requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.splash); tv_splash_version = (TextView) this.findViewById(R.id.tv_splash_version); ll_splash_main = (LinearLayout) this.findViewById(R.id.ll_splash_main); String versiontext = getVersion(); tv_splash_version.setText(versiontext); if(isNeedUpdate(versiontext)){ Log.i(TAG, "彈出升級對話方塊"); showUpdateDialog(); } /* AlphaAnimation類:透明度變化動畫類 * AlphaAnimation類是Android系統中的透明度變化動畫類,用於控制View對象的透明度變化,該類繼承於Animation類。 * AlphaAnimation類中的很多方法都與Animation類一致,該類中最常用的方法便是AlphaAnimation構造方法。 * * public AlphaAnimation (float fromAlpha, float toAlpha)參數說明fromAlpha:開始時刻的透明度,取值範圍0~1。toAlpha:結束時刻的透明度,取值範圍0~1。 */ AlphaAnimation aa = new AlphaAnimation(0.0f, 1.0f); aa.setDuration(2000); //Animation類的方法,設定期間 ll_splash_main.startAnimation(aa); //設定動畫 //完成表單的全螢幕顯示 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } private void <span style="color:#FF6666;">showUpdateDialog</span>() { //彈出一個訊息框 <span style="color:#FF0000;">AlertDialog.Builder builder = new Builder(this);</span> builder.setIcon(R.drawable.icon5); //設定訊息框的標題表徵圖 builder.setTitle("升級提醒"); //設定訊息框的標題 builder.setMessage(info.getDescription()); //設定要顯示的內容 builder.setCancelable(false); //讓使用者不能按後退鍵取消 builder.<span style="color:#FF6666;">setPositiveButton</span>("確定", new OnClickListener() { //設定使用者選擇確定時的按鍵動作@Overridepublic void onClick(DialogInterface dialog, int which) {Log.i(TAG, "下載pak檔案:" + info.getApkurl());}}); builder.setNegativeButton("取消", new OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {Log.i(TAG, "使用者取消升級,進入程式主介面");}}); builder.create().show(); }/** * * @param versiontext 當前用戶端的版本資訊 * @return 是否需要更新 */ private boolean isNeedUpdate(String versiontext) { UpdateInfoService service = new UpdateInfoService(this); try {info = service.getUpdateInfo(R.string.updateurl);String version = info.getVersion();if(versiontext.equals(version)){Log.i(TAG, "版本號碼相同,無需升級,進入到主介面");return false;}else{Log.i(TAG, "版本號碼不同,需要升級");return true;}} catch (Exception e) {e.printStackTrace();/** * Toast使用情境 * 1、需要提示使用者,但又不需要使用者點擊“確定”或者“取消”按鈕。 * 2、不影響現有Activity啟動並執行簡單提示。 */Toast.makeText(this, "擷取更新資訊異常", 2).show();//彈出文本,並保持2秒Log.i(TAG, "擷取更新資訊異常,進入到主介面");return false;} }@Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.splash, menu); return true; } /** * 擷取當前程式的版本號碼 * @return */ private String getVersion(){ // 擷取一個PackageManager的執行個體,從而可以擷取全域包資訊 PackageManager manager = getPackageManager(); try { // Retrieve overall information about an application package that is installed on the system.PackageInfo info = manager.getPackageInfo(getPackageName(), 0);// The version name of this package, as specified by the <manifest> tag's versionName attribute.return info.versionName;} catch (Exception e) {e.printStackTrace();return "版本號碼未知";} } }
- isNeedUpdate()方法:調用UpdateInfoService 的getUpdateInfo()方法,來擷取更新資訊。同時將伺服器端的版本號碼和當前用戶端的版本號碼進行對比,並做出是否讓使用者升級的操作。若發現兩個版本號碼不一致,那麼就要提醒使用者進行升級:
- 這裡調用了showUpdateDialog()方法,在這個方法中,設定介面彈出一個訊息框,其中有兩個按鈕:“確定”“取消”,使用者點擊不同的按鈕則對應不同的操作。
異常處理android.os.NetworkOnMainThreadException--多線程問題
一切搞定,以為高枕無憂了,結果還是有問題!
log開始報錯了,擷取更新資訊異常!!!debug一下,發現Exception:android.os.NetworkOnMainThreadException
這個異常大概意思是在主線程訪問網路時出的異常。 Android在4.0之前的版本 支援在主線程中訪問網路,但是在4.0以後對這部分程式進行了最佳化,也就是說訪問網路的代碼不能寫在主線程中了。
處理方法:http://blog.csdn.net/bruce_6/article/details/39640587
【邊做項目邊學Android】手機安全衛士03:擷取更新的伺服器配置,顯示更新對話方塊