执行网络操作

     这一部分阐述了如何来进行最基本的网络连接任务,管理网络连接(包含网络状态的改变),还有让用户来管理一个应用网络的用法,还有描述了如何来解析和运用XML数据



     经过学习了这些课程后,你能够基本的在一个应用中有效的从网络上下载和解析数据,并且使用最少的网络资源



通过本章你将会学到



连接到网络


          怎么样去连接一个网络,选择合适的HTTP的客户端,在UI祝线程外执行一个网络操作


     

管理网络的使用


          怎么样去检查网络连接状态,创建一个前台界面去管理网络的使用,怎么去应对网络状态改变



解析 XML 数据


          怎么样去解析和使用XML数据



网络操作最佳实践



取得device的网络连接状态

// Checks the network connection and sets the wifiConnected and mobileConnected
    // variables accordingly.
    private void updateConnectedFlags() {
       
       //网络管理服务
        ConnectivityManager connMgr =
                (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE );

        NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
        if (activeInfo != null && activeInfo.isConnected()) {
              //wifi 连接状态
            wifiConnected = activeInfo.getType() == ConnectivityManager.TYPE_WIFI ;
            //mobile连接状态
            mobileConnected = activeInfo.getType() == ConnectivityManager.TYPE_MOBILE ;
        } else {
            wifiConnected = false ;
            mobileConnected = false ;
        }
    }






在UI主线程外运用AsyncTask类异步的执行网络连接操作



// Implementation of AsyncTask used to download XML feed from stackoverflow.com.
    private class DownloadXmlTask extends AsyncTask<String, Void, String> {

        @Override
        protected String doInBackground( String... urls) {
            try {
                return loadXmlFromNetwork(urls[0]);
            } catch (IOException e) {
                return getResources().getString(R.string.connection_error);
            } catch (XmlPullParserException e) {
                return getResources().getString(R.string.xml_error);
            }
        }

        @Override
        protected void onPostExecute(String result) {
            setContentView(R.layout. activity_main);
            // Displays the HTML string in the UI via a WebView
            WebView myWebView = (WebView) findViewById(R.id. webview);
            myWebView.loadData(result, "text/html", null );
        }
    }



执行网络连接



// 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 */);
        //设置为GET请求
        conn. setRequestMethod("GET");
        //设置允许读取网络输入流
        conn.setDoInput( true);
        // Starts the query
        conn. connect();
        InputStream stream = conn. getInputStream();
        return stream;
    }



在Android操作系统中,每次网络状态有变化时都会发送一个广播,我们只要注册了该广播的接收器,就可以得知网络状态的变化了



// Register BroadcastReceiver to track connection changes.
        // 动态注册广播接收器去接收网络状态改变的广播
        IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION );
        receiver = new NetworkReceiver();
        this.registerReceiver( receiver, filter);



/**
     *
     * This BroadcastReceiver intercepts the android.net.ConnectivityManager.CONNECTIVITY_ACTION,
     * which indicates a connection change. It checks whether the type is TYPE_WIFI.
     * If it is, it checks whether Wi- Fi is connected and sets the wifiConnected flag in the
     * main activity accordingly.
     * 网络状态改变时,进行一些处理
     */
    public class NetworkReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            ConnectivityManager connMgr =
                    (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE );
            NetworkInfo networkInfo = connMgr.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();
            }
        }
    }



Android SDK 自带的解析器PULL解析xml时最佳实践点



//设置不使用namespace特性,节省资源,加快解析速度
            parser.setFeature(XmlPullParser. FEATURE_PROCESS_NAMESPACES, false );

//测试一下是否是标签的起始,支持任何 namespace,标签的名为feed.有效的减少一些无效的解析操作
        parser.require(XmlPullParser. START_TAG, ns , "feed" );

// Skips tags the parser isn't interested in. Uses depth to handle nested tags. i.e.,
    // if the next tag after a START_TAG isn't a matching END_TAG, it keeps going until it
    // finds the matching END_TAG (as indicated by the value of "depth" being 0).
    // 跳过一些对我们无用的标签不进行解析,不需要每次都去判断当前标签是否是我们需要的标签,加快解析速度
    private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
        if (parser.getEventType() != XmlPullParser.START_TAG) {
            throw new IllegalStateException();
        }
        int depth = 1;
        while (depth != 0) {
            switch (parser.next()) {
            case XmlPullParser.END_TAG:
                    depth--;
                    break;
            case XmlPullParser.START_TAG:
                    depth++;
                    break;
            }
        }
    }

总结

在Android中去执行一个网络任务,一般要在UI主线程之外的线程中去执行以免阻塞UI主线程,监听到网络状态的变化,进而做一些操作,是很有必要的!有助于我们的应用提供更好的用户体验。xml数据的解析也需要更好的进行优化,以更快的速度把数据展现在用户面前,这些都是很好的实践。