【邊做項目邊學Android】手機安全衛士03:擷取更新的伺服器配置,顯示更新對話方塊

來源:互聯網
上載者:User

標籤: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:擷取更新的伺服器配置,顯示更新對話方塊

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.