Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 跟Google 學代碼 : Building Apps with Connectivity & the Cloud【Webapp Usage】

跟Google 學代碼 : Building Apps with Connectivity & the Cloud【Webapp Usage】

編輯:關於Android編程

本文介紹

本文是翻譯自Google 官方課程 Building Apps with Connectivity & the Cloud 第二節 Performing Network Operations

本文的demo為NetworkUsage,是一款根據不同網絡狀態,刷新UI的 web app,數據都來自網絡

\

初始狀態:

這裡寫圖片描述

可以設置是否展示文章詳情<喎?/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPqO6PC9wPg0KPHA+PGltZyBhbHQ9"這裡寫圖片描述" src="/uploadfile/Collfiles/20160806/2016080609140797.gif" title="\" />

執行網絡任務

設備要求:

設備可以連接移動網絡或WI-FI

這篇文章將講解連接網絡的基本操作檢測網絡連接(包括狀態改變),讓用戶根據網絡狀態控制*app,最後還講了如何解析XML*數據

通過這篇文章,你將了解原理-構建webAndroid應用,它可以高效下載並解析數據,減小網絡擁堵

也可以閱讀我之前翻譯的《Volley官方課程》,一個HTTP庫使得Android app的網絡連接更快更簡潔,Volley是開源的,可以幫助你提高app網絡連接的性能

本文包括三部分:

連接網絡
如何連接網絡,選擇HTTP 客戶端,避免在主線程執行網絡任務

管理網絡使用

學習如何檢查設備的網絡連接,創建UI**偏好設置響應**網絡連接狀態的改變

解析XML數據

連接網絡

這部分將實現一個簡單的可以連接網絡的app,包含了創建網絡app的所有基本步驟

這部分依照如下順序講解:

選擇HTTP 客戶端 檢查當前設備網絡連接 在子線程中執行網絡任務 下載網絡數據 將流解析成字符串

對於初學者,可能還需要了解《Volley官方課程》,《Web Apps開發》 以及後續的博文《延長電池壽命》,《App 基本組件》

額,不算提醒的提醒,app在使用網絡之前,需要添加權限:



選擇HTTP 客戶端

大部分的Android app都是使用HTTP協議發送接收數據, Android系統提供了HttpURLConnection編程接口,它是支持HTTPS協議的,並且支持上傳和下載數據,配置超時,IPV6和連接池

檢查網絡連接

在使用網絡之前,app得先檢查當前設備的網絡連接是否可用,通過getActiveNetworkInfo() 和isConnected()可以判斷網絡連接。提醒各位,我們使用的手機設備有可能在網絡覆蓋區域之外,或者WI-FI也不可用,更多的情形可以在下一節《管理網絡連接》看到

public void myClickHandler(View view) {
    ...
    ConnectivityManager connMgr = (ConnectivityManager)
        getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    if (networkInfo != null && networkInfo.isConnected()) {
        // fetch data
    } else {
        // display error
    }
    ...
}

在子線程中執行網絡任務

網絡任務有可能被延遲或阻塞,這種情況下用戶體驗非常糟糕,所以我們得使用子線程創建網絡任務,主線程去做刷新UI的事情就好

推薦使用AsyncTask ,它提供了一種非常簡單的方式–在啟動子線程異步完成任務的同時,又能通知主線程刷新界面

對於初識AsyncTask的人來說,這玩意咋用呢?

通常做法是繼承AsyncTask,按要求重寫方法

doInBackground(),調用downloadUrl()方法,去請求一些數據 onPostExecute(),將上一步請求的數據,顯示在主線程中
    public class HttpExampleActivity extends Activity {
        private static final String DEBUG_TAG = "HttpExample";
        private EditText urlText;
        private TextView textView;

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            urlText = (EditText) findViewById(R.id.myUrl);
            textView = (TextView) findViewById(R.id.myText);
        }

        // When user clicks button, calls AsyncTask.
        // Before attempting to fetch the URL, makes sure that there is a network connection.
        public void myClickHandler(View view) {
            // Gets the URL from the UI's text field.
            String stringUrl = urlText.getText().toString();
            ConnectivityManager connMgr = (ConnectivityManager)
                getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
            if (networkInfo != null && networkInfo.isConnected()) {
                new DownloadWebpageTask().execute(stringUrl);
            } else {
                textView.setText("No network connection available.");
            }
        }

         // Uses AsyncTask to create a task away from the main UI thread. This task takes a
         // URL string and uses it to create an HttpUrlConnection. Once the connection
         // has been established, the AsyncTask downloads the contents of the webpage as
         // an InputStream. Finally, the InputStream is converted into a string, which is
         // displayed in the UI by the AsyncTask's onPostExecute method.
         private class DownloadWebpageTask extends AsyncTask {
            @Override
            protected String doInBackground(String... urls) {

                // params comes from the execute() call: params[0] is the url.
                try {
                    return downloadUrl(urls[0]);
                } catch (IOException e) {
                    return "Unable to retrieve web page. URL may be invalid.";
                }
            }
            // onPostExecute displays the results of the AsyncTask.
            @Override
            protected void onPostExecute(String result) {
                textView.setText(result);
           }
        }
        ...
    }

示例中的執行順序如下:

當用戶點擊button,將調用myClickHandler(),這個app傳遞特殊的URL給DownloadWebpageTask 這個AsyncTask子類首先調用doInBackground()方法,去執行downloadUrl() downloadUrl()創建URL對象 通過HtppURLConnection使用URL建立網絡連接 一旦建立了網絡連接,HttpURLConnection將獲得Web端反饋的流數據 流通過readIt()函數轉為一個字符串 最終,AsyncTask的onPostExecute()方法將會把上一步的字符串展示到activity的界面上

連接並下載數據

通過HttpURLConnection去執行GET請求下載網絡上的數據,在調用connect()方法之後,可以獲得網絡上返回的字節流


    // Given a URL, establishes an HttpUrlConnection and retrieves
    // the web page content as a InputStream, which it returns as
    // a string.
    private String downloadUrl(String myurl) throws IOException {
        InputStream is = null;
        // Only display the first 500 characters of the retrieved
        // web page content.
        int len = 500;

        try {
            URL url = new URL(myurl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(10000 /* milliseconds */);
            conn.setConnectTimeout(15000 /* milliseconds */);
            conn.setRequestMethod("GET");
            conn.setDoInput(true);
            // Starts the query
            conn.connect();
            int response = conn.getResponseCode();
            Log.d(DEBUG_TAG, "The response is: " + response);
            is = conn.getInputStream();

            // Convert the InputStream into a string
            String contentAsString = readIt(is, len);
            return contentAsString;

        // Makes sure that the InputStream is closed after the app is
        // finished using it.
        } finally {
            if (is != null) {
                is.close();
            }
        }
    }

getResponseCode()獲得網絡連接的 status coded(狀態碼),200,400,403 404等等

將流轉換成字符串

InputStream是可讀的數據,一旦你獲得一個InputStream,通常可以解析成其他數據類型,比如我們下載圖片,可以解析並展示它,

    InputStream is = null;
    ...
    Bitmap bitmap = BitmapFactory.decodeStream(is);
    ImageView imageView = (ImageView) findViewById(R.id.image_view);
    imageView.setImageBitmap(bitmap);
    In the example shown above, the InputStream represents the text of a web page. This is how the example converts the InputStream to a string so that the activity can display it in the UI:
    // Reads an InputStream and converts it to a String.
    public String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException {
        Reader reader = null;
        reader = new InputStreamReader(stream, "UTF-8");
        char[] buffer = new char[len];
        reader.read(buffer);
        return new String(buffer);
    }

管理網絡使用

上一節我們學會了通過AsyncTask下載數據並將數據展示到界面中

接著,我們得考慮全面一些,網絡連接是否可用?網絡連接中斷了咋辦?

嗯,這一節將涉及:

檢查設備的網絡連接 管理網絡使用 實現Activity的偏好設置 響應偏好設置的改變(是否自動刷新?展示標題還是標題和內容一起展示?) 關閉網絡連接

這一部分的例子將展示如何控制網絡的數據,如果app執行大量的網絡任務,我們就需要提供偏好設置允許用戶控制app的數據,比如是否同步數據,是否僅在WI-FI狀態下更新上傳數據,是否漫游數據,諸如此類

當我們考慮的如此周到,用戶不喜歡我們的應用才怪哩!

檢查設備的網絡連接

一個設備可以有很多種類型的網絡連接,本篇博文專注於使用WI-FI或者移動蜂窩網絡連接,對於所有可用的網絡連接類型,可以參考ConnectivityManager

WI-FI 的傳輸速度很快,移動網絡經常是可測量的,但需要花費流量,代價比較高,通常在WI-FI可用的情況下,app連接網絡都是通過WI-FI傳輸數據

在執行網絡任務之前,檢查網絡狀態是非常好的習慣,如果網絡連接不可用,app應該給用戶“柔和”的提示,如果打算檢查網絡連接,可以從以下兩個類入手:

ConnectivityManager:可以獲得網絡連接的狀態,也能通知app設備網絡連接狀態發生了改變 NetworkInfo:描述當前網絡連接的類型(比如當前是移動網絡還是WI-FI網絡),下面就是有關檢查網絡狀態的示例:
    private static final String DEBUG_TAG = "NetworkStatusExample";
    ...
    ConnectivityManager connMgr = (ConnectivityManager)
            getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
    boolean isWifiConn = networkInfo.isConnected();
    networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
    boolean isMobileConn = networkInfo.isConnected();
    Log.d(DEBUG_TAG, "Wifi connected: " + isWifiConn);
    Log.d(DEBUG_TAG, "Mobile connected: " + isMobileConn);

它可以測定網絡是否可用,如果是,是否網絡已連接,如果已經連接,那麼將可以通過建立socket傳輸數據

我們不應該以網絡是否可用作為接下來業務邏輯的根據,而是應該以是否連接作為下一步執行的依據,檢查是否連接可以使用isConnected(),判斷完畢網絡連接狀態後,我們就可以處理移動網絡數據交互,飛行模式,微量的後台數據

推薦一種簡潔的檢查網絡連接的方式:

    public boolean isOnline() {
        ConnectivityManager connMgr = (ConnectivityManager)
                getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
        return (networkInfo != null && networkInfo.isConnected());
    } 

更多的信息,可以參考NetworkInfo.DetailedState

管理網絡使用

我們可以實現偏好設置,給用戶足夠多的控制權,去控制app訪問網絡的方式,例如:

可以允許用戶上傳視頻,當設備連接WI-FI網絡的時候 可以同步信息,當網絡可用的時候

記得添加這倆權限:

android.permission.INTERNET 允許app打開網絡sockets android.permission.ACCESS_NETWORK_STATE 允許app獲得網絡狀態的信息

我們也可以聲明intent filter 的action為ACTION_MANAGE_NETWORK_USAGE,定義app提供一種可選擇的方式控制網絡數據傳輸,

    
    

        

        
        

        
            ...
            
                 
                    
                    
              
            
        
    

上述示例中的SettingsActivity,設置了action為MANAGE_NETWORK_USAGE,app才可以控制網絡使用,

它提供了一個偏好設置讓用戶去決定在什麼情況下(WI-FI,Network)去上傳或下載數據

響應偏好設置改變

當用戶改變了app的偏好設置,我們還需要在activity的onstart檢查一下

   public class NetworkActivity extends Activity {
        public static final String WIFI = "Wi-Fi";
        public static final String ANY = "Any";
        private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest";

        // Whether there is a Wi-Fi connection.
        private static boolean wifiConnected = false;
        // Whether there is a mobile connection.
        private static boolean mobileConnected = false;
        // Whether the display should be refreshed.
        public static boolean refreshDisplay = true;

        // The user's current network preference setting.
        public static String sPref = null;

        // The BroadcastReceiver that tracks network connectivity changes.
        private NetworkReceiver receiver = new NetworkReceiver();

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            // Registers BroadcastReceiver to track network connection changes.
            IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
            receiver = new NetworkReceiver();
            this.registerReceiver(receiver, filter);
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            // Unregisters BroadcastReceiver when app is destroyed.
            if (receiver != null) {
                this.unregisterReceiver(receiver);
            }
        }

        // Refreshes the display if the network connection and the
        // pref settings allow it.

        @Override
        public void onStart () {
            super.onStart();

            // Gets the user's network preference settings
            SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);

            // Retrieves a string value for the preferences. The second parameter
            // is the default value to use if a preference value is not found.
            sPref = sharedPrefs.getString("listPref", "Wi-Fi");

            updateConnectedFlags();

            if(refreshDisplay){
                loadPage();
            }
        }

        // Checks the network connection and sets the wifiConnected and mobileConnected
        // variables accordingly.
        public void updateConnectedFlags() {
            ConnectivityManager connMgr = (ConnectivityManager)
                    getSystemService(Context.CONNECTIVITY_SERVICE);

            NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
            if (activeInfo != null && activeInfo.isConnected()) {
                wifiConnected = activeInfo.getType() == ConnectivityManager.TYPE_WIFI;
                mobileConnected = activeInfo.getType() == ConnectivityManager.TYPE_MOBILE;
            } else {
                wifiConnected = false;
                mobileConnected = false;
            }
        }

        // Uses AsyncTask subclass to download the XML feed from stackoverflow.com.
        public void loadPage() {
            if (((sPref.equals(ANY)) && (wifiConnected || mobileConnected))
                    || ((sPref.equals(WIFI)) && (wifiConnected))) {
                // AsyncTask subclass
                new DownloadXmlTask().execute(URL);
            } else {
                showErrorPage();
            }
        }
    ...

    }

檢測網絡連接改變

最後的部分就是實現BroadcastReceiver子類—NetworkReceiver,當設備網絡連接改變的時候,NetworkReceiver根據CONNECTIVITY_ACTION行為,判斷當前的網絡連接狀態,來設置wifiConnected和moblileConnected 的狀態為true或false,這兩個變量主要是記錄當前網絡狀態的:

    public class NetworkReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        ConnectivityManager conn =  (ConnectivityManager)
            context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = conn.getActiveNetworkInfo();

        // Checks the user prefs and the network connection. Based on the result, decides whether
        // to refresh the display or keep the current display.
        // If the userpref is Wi-Fi only, checks to see if the device has a Wi-Fi connection.
        if (WIFI.equals(sPref) && networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
            // If device has its Wi-Fi connection, sets refreshDisplay
            // to true. This causes the display to be refreshed when the user
            // returns to the app.
            refreshDisplay = true;
            Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show();

        // If the setting is ANY network and there is a network connection
        // (which by process of elimination would be mobile), sets refreshDisplay to true.
        } else if (ANY.equals(sPref) && networkInfo != null) {
            refreshDisplay = true;

        // Otherwise, the app can't download content--either because there is no network
        // connection (mobile or Wi-Fi), or because the pref setting is WIFI, and there
        // is no Wi-Fi connection.
        // Sets refreshDisplay to false.
        } else {
            refreshDisplay = false;
            Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show();
        }
    }

解析XML數據

這節得重點有:

選擇一個 解析器 分析網絡的反饋 初始化解析器 閱讀網絡的反饋 解析XML 跳過你不必關心的部分 使用XML數據

這節將講解如何解析XML文檔,並且使用這些XML數據

選擇一個解析器

建議使用XmlPullParser解析XML,Android提供了兩個編程接口:

KXmlParser:通過XmlPullParserFactory.newPullParser()獲得; ExpatPullParser:通過Xml.Xml.newPullParser()獲得

下面的解析以ExpatPullParser為例

確定數據源

第一步,想選擇感興趣的 “數據反饋”作為解析源

接下來的例子是以著名網站“StackOverflow”作為“數據源”,

    
    " + getResources().getString(R.string.page_title) + "");
        htmlString.append("" + getResources().getString(R.string.updated) + " " +
                formatter.format(rightNow.getTime()) + "");

        try {
            stream = downloadUrl(urlString);
            entries = stackOverflowXmlParser.parse(stream);
        // Makes sure that the InputStream is closed after the app is
        // finished using it.
        } finally {
            if (stream != null) {
                stream.close();
            }
         }

        // StackOverflowXmlParser returns a List (called "entries") of Entry objects.
        // Each Entry object represents a single post in the XML feed.
        // This section processes the entries list to combine each entry with HTML markup.
        // Each entry is displayed in the UI as a link that optionally includes
        // a text summary.
        for (Entry entry : entries) {
            htmlString.append("

" + entry.title + "

"); // If the user set the preference to include summary text, // adds it to the display. if (pref) { htmlString.append(entry.summary); } } return htmlString.toString(); } // Given a string representation of a URL, sets up a connection and gets // an input stream. private InputStream downloadUrl(String urlString) throws IOException { URL url = new URL(urlString); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(10000 /* milliseconds */); conn.setConnectTimeout(15000 /* milliseconds */); conn.setRequestMethod("GET"); conn.setDoInput(true); // Starts the query conn.connect(); return conn.getInputStream(); }

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved