美文网首页
第 9 章 - Android 网络编程

第 9 章 - Android 网络编程

作者: AaronZheng丶 | 来源:发表于2018-09-05 21:14 被阅读0次

    1. WebView 的用法

    1.1 在布局文件中添加 WebView 控件,使其充满屏幕
    1.2 修改 MainActivity 中的代码
    • getSetting() 方法可以设置一些浏览器的属性,在这里只调用了 setJavaScriptEnabled() 方法来使 WebView 支持 JavaScript 脚本。
    • 接着调用了 setWebViewClient() 方法,并传入一个 WebViewClient 实例。这段代码的作用是,当需要从一个网页跳转到另一个网页时,目标网页仍然在当前 WebView 中显示,而不是打开系统浏览器。
    • 最后调用 loadUrl() 方法,将网址传入即可展示相应网页的内容。
    WebView webView = findViewById(R.id.web_view);
    webView.getSettings().setJavaScriptEnabled(true);
    webView.setWebViewClient(new WebViewClient());
    webView.loadUrl("http://www.baidu.com");
    
    1.3 声明访问网络权限
    • 由于程序加入了网络功能,因此需要到 AndroidManifest.xml 中声明权限
    • <uses-permission android:name="android.permission.INTERNET"/>

    2. 使用 HTTP 协议访问网络

    • HTTP 原理:客户端向服务器发出一条 HTTP 请求,服务器收到请求后会返回一些数据给客户端,然后客户端再对这些数据进行解析和处理即可。如上一节中向百度的服务器发起了一条 HTTP 请求,接着服务器分析出我们想要访问的是百度的首页,于是会把该网址的 HTML 代码进行返回,然后 WebView 再调用手机浏览器的内核对返回的 HTML 代码进行解析,最终将页面展示出来。
    2.1 使用 HttpURLConnection:

    目标:使用此技术获取数据,并显示到界面上

    public class MainActivity extends AppCompatActivity {
    
        private TextView responseText;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            responseText = findViewById(R.id.response_text);
            Button sendRequest = findViewById(R.id.send_request);
            sendRequest.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v){
                    sendRequestWithHttpURLConnection();
                }
            });
        }
    
        /**
         * 定义一个发送请求方法,具体的逻辑都在里面编写
         */
        private void sendRequestWithHttpURLConnection(){
            // 因为网络请求是耗时操作,所以开启子线程来发起请求
            new Thread(new Runnable() {
                @Override
                public void run() {
                    HttpURLConnection connection = null;
                    BufferedReader reader = null;
                    try{
                        // 首先要获取 HttpURLConnection 实例
                        URL url = new URL("https://www.baidu.com");
                        connection = (HttpURLConnection)url.openConnection();
    
                        // 设置请求方法,各种超时
                        connection.setRequestMethod("GET");
                        connection.setConnectTimeout(8000);
                        connection.setReadTimeout(8000);
    
                        // 获取服务器返回的输入流,并使用 BufferedReader 进行读取
                        InputStream in = connection.getInputStream();
                        reader = new BufferedReader(new InputStreamReader(in));
                        StringBuilder response = new StringBuilder();
                        String line;
                        while((line = reader.readLine()) != null){
                            response.append(line);
                        }
                        // 调用此方法用于将结果显示到界面上
                        showResponse(response.toString());
                    }catch(Exception e){
                        e.printStackTrace();
                    }finally{
                        try{
                            if(reader != null){
                                reader.close();
                            }
                        }catch(IOException e){
                            e.printStackTrace();
                        }
                        if(connection != null){
                            connection.disconnect();
                        }
                    }
                }
            }).start();
        }
    
        /**
         * 定义此方法用于将结果显示在界面上,由于发起请求的逻辑操作
         * 是在子线程进行,因此在这里需要将线程切换回主线程来进行
         * UI 操作。
         *
         * @param response 服务器返回的响应结果,声明为 final 是
         *                 因为参数需要在内部类中使用
         */
        private void showResponse(final String response){
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    responseText.setText(response);
                }
            });
        }
    }
    
    • 步骤:
    1. 获取 HTTPURLConnection 实例
    2. 设置 HTTP 请求方法,GET 表示希望从服务器获取数据,POST 表示希望提交数据给服务器
    3. 设置连接超时,读取超时等
    4. 获取返回的输入流,并使用 BufferedReader 进行读取
    5. 关闭 HTTP 连接
    • 发起一条 POST 请求:
    connection.setRequestMethod("POST");
    DataOutputStream out = new DataOutputStream(connection.getOutputStream());
    out.writeBytes("username=admin&password=123456");
    
    2.2 使用 OkHttp:
    • app/build.gradle 中添加依赖:
      implementation 'com.squareup.okhttp3:okhttp:3.11.0'
    • 接着上一节内容,只需修改 sendRequestWithHttpURLConnection() 方法即可:
    private void sendRequestWithOkHttp(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    // 创建 OkHttpClient 实例
                    OkHttpClient client = new OkHttpClient();
                    // 创建 Request 对象发起请求
                    Request request = new Request.Builder()
                            .url("https://www.baidu.com")
                            .build();
                    
                    /* 调用 newCall(request) 方法创建一个 Call 对象,再调用 Call 对象
                       的 execute() 方法来发送请求并并并获取服务器返回的数据,
                       再赋值给 Response 对象 */
                    Response response = client.newCall(request).execute();
                    // 将 Response 对象转换为 String 类型
                    String responseData = response.body().string();
                    showResponse(responseData);
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        }).start();
    }
    
    • 发起一条 POST 请求:
    RequestBody requestBody = new FormBody.Builder()
            .add("username","admin")
            .add("password", "123456")
            .build();
    Request request = new Request.Builder()
            .url("https://www.baidu.com")
            .post(requestBody)
            .build();
    

    3. 解析 XML 格式数据

    <apps>
        <app>
            <id>1</id>
            <name>Google Maps</name>
            <version>1.0</version>
        </app>
        <app>
            <id>2</id>
            <name>Chrome</name>
            <version>2.1</version>
        </app>
        <app>
            <id>3</id>
            <name>Google Play</name>
            <version>2.3</version>
        </app>
    </apps>
    
    3.1 Pull 解析方式:
    public class MainActivity extends AppCompatActivity {
    
        private static final String TAG = "MainActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Button sendRequest = findViewById(R.id.send_request);
            sendRequest.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v){
                    sendRequestWithHttpURLConnection();
                }
            });
        }
    
        /**
         * 定义一个发送请求方法,具体的逻辑都在里面编写
         */
        private void sendRequestWithHttpURLConnection(){
            // 因为网络请求是耗时操作,所以开启子线程来发起请求
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        OkHttpClient client = new OkHttpClient();
                        Request request = new Request.Builder()
                                // 指定访问的服务器地址是电脑本机
                                .url("http://10.0.2.2/get_data.xml")
                                .build();
                        Response response = client.newCall(request).execute();
                        String responseData = response.body().string();
                        parseXMLWithPull(responseData);
                    }catch(IOException e){
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        private void parseXMLWithPull(String xmlData){
            try{
                XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
                XmlPullParser parser = factory.newPullParser();
                parser.setInput(new StringReader(xmlData));
                // 得到当前解析事件
                int eventType = parser.getEventType();
                String id = null;
                String name = null;
                String version = null;
                while(eventType != XmlPullParser.END_DOCUMENT){
                    String nodeName = parser.getName();
                    switch(eventType){
                        case XmlPullParser.START_TAG:
                            if("id".equals(nodeName)){
                                id = parser.nextText();
                            }else if("name".equals(nodeName)){
                                name = parser.nextText();
                            }else if("version".equals(nodeName)){
                                version = parser.nextText();
                            }
                            break;
                        case XmlPullParser.END_TAG:
                            if("app".equals(nodeName)){
                                Log.d(TAG, "id is " + id);
                                Log.d(TAG, "name is " + name);
                                Log.d(TAG, "version is " + version);
                            }
                            break;
                    }
                    // 获取下一个解析事件
                    eventType = parser.next();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    
    3.2 SAX 解析方式:
    • 新建一个类继承自 DefaultHandler,并复写父类的 5 个方法
    public class ContentHandler extends DefaultHandler{
    
        private String nodeName;
        private StringBuilder id;
        private StringBuilder name;
        private StringBuilder version;
    
        /**
         * 此方法在开始 XML 解析时调用
         *
         * @throws SAXException
         */
        @Override
        public void startDocument() throws SAXException{
            id = new StringBuilder();
            name = new StringBuilder();
            version = new StringBuilder();
        }
    
        /**
         * 此方法在开始解析某个节点时调用
         *
         * @param uri 命名空间
         * @param localName 标签名称
         * @param qName 带命名空间的标签名称
         * @param attributes 存放该标签的所有属性
         * @throws SAXException
         */
        @Override
        public void startElement(String uri, String localName, 
                                 String qName, Attributes attributes) throws SAXException {
            // 记录当前节点名
            nodeName = localName;
        }
    
        /**
         * 此方法会在获取节点中内容时调用
         *
         * @param ch 当前读到的 TextNode 字节数组
         * @param start 字节开始的位置,如果要读取全部,那就是从 0 开始
         * @param length 当前 TextNode 的长度
         * @throws SAXException
         */
        @Override
        public void characters(char[] ch, int start, int length) throws SAXException{
            /* 根据当前节点名判断将内容添加到哪一个
            StringBuilder 对象中*/
            if("id".equals(nodeName)){
                id.append(ch, start, length);
            }else if("name".equals(nodeName)){
                name.append(ch, start, length);
            }else if("version".equals(nodeName)){
                version.append(ch, start, length);
            }
        }
    
        /**
         * 此方法会在完成解析某个节点时调用
         *
         * @param uri 命名空间
         * @param localName 标签名称
         * @param qName 带命名空间的标签名称
         * @throws SAXException
         */
        @Override
        public void endElement(String uri, 
                               String localName, String qName) throws SAXException{
            if("app".equals(localName)){
                Log.d("ContentHandler", "id is " + id.toString().trim());
                Log.d("ContentHandler", "name is " + name.toString().trim());
                Log.d("ContentHandler", "version is " + version.toString().trim());
                // 最后要将 StringBuilder 清空掉
                id.setLength(0);
                name.setLength(0);
                version.setLength(0);
            }
        }
    
        /**
         * 此方法会在完成整个 XML 解析时调用
         *
         * @throws SAXException
         */
        @Override
        public void endDocument() throws SAXException{
            super.endDocument();
        }
    }
    
    • 修改 MainActivity 中的代码:
    public class MainActivity extends AppCompatActivity {
    
        private static final String TAG = "MainActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Button sendRequest = findViewById(R.id.send_request);
            sendRequest.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v){
                    sendRequestWithHttpURLConnection();
                }
            });
        }
    
        /**
         * 定义一个发送请求方法,具体的逻辑都在里面编写
         */
        private void sendRequestWithHttpURLConnection(){
            // 因为网络请求是耗时操作,所以开启子线程来发起请求
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        OkHttpClient client = new OkHttpClient();
                        Request request = new Request.Builder()
                                // 指定访问的服务器地址是电脑本机
                                .url("http://10.0.2.2/get_data.xml")
                                .build();
                        Response response = client.newCall(request).execute();
                        String responseData = response.body().string();
                        parseXMLWithSAX(responseData);
                    }catch(IOException e){
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        private void parseXMLWithSAX(String xmlData){
            try{
                SAXParserFactory factory = SAXParserFactory.newInstance();
                XMLReader reader = factory.newSAXParser().getXMLReader();
                ContentHandler handler = new ContentHandler();
                // 将 ContentHandler 的实例设置到 XMLReader 中
                reader.setContentHandler(handler);
                // 开始解析
                // public void parse (InputSource input)
                // public InputSource (Reader characterStream)
                // public StringReader(String s)
                reader.parse(new InputSource(new StringReader(xmlData)));
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    

    4. 解析 JSON 格式数据

    [{"id":"5","version":"5.5","name":"Clash of Clans"},
    {"id":"6","version":"7.0","name":"Boom Beach"},
    {"id":"7","version":"3.5","name":"Clash Royale"}]

    4.1 使用 JSONObject:
    public class MainActivity extends AppCompatActivity {
    
        private static final String TAG = "MainActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Button sendRequest = findViewById(R.id.send_request);
            sendRequest.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v){
                    sendRequestWithHttpURLConnection();
                }
            });
        }
    
        /**
         * 定义一个发送请求方法,具体的逻辑都在里面编写
         */
        private void sendRequestWithHttpURLConnection(){
            // 因为网络请求是耗时操作,所以开启子线程来发起请求
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        OkHttpClient client = new OkHttpClient();
                        Request request = new Request.Builder()
                                // 指定访问的服务器地址是电脑本机
                                .url("http://10.0.2.2/get_data.json")
                                .build();
                        Response response = client.newCall(request).execute();
                        String responseData = response.body().string();
                        parseJSONWithJSONObject(responseData);
                    }catch(IOException e){
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        private void parseJSONWithJSONObject(String jsonData){
            try{
                JSONArray jsonArray = new JSONArray(jsonData);
                // 用循环遍历 JSONArray ,从中取出的每一个元素都是一个,JSONObject 对象
                for(int i = 0; i < jsonArray.length(); i++){
                    JSONObject object = jsonArray.getJSONObject(i);
                    String id = object.getString("id");
                    String name = object.getString("name");
                    String version = object.getString("version");
                    Log.d(TAG, "id is " + id);
                    Log.d(TAG, "name is " + name);
                    Log.d(TAG, "version is " + version);
                }
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
    
    4.2 使用 GSON:

    GSON 可以将一段 JSON 格式的字符串自动映射成一个对象

    • 添加 app/build.gradle 依赖库:
      implementation 'com.google.code.gson:gson:2.7'
    • 定义一个 App类:
    public class App {
    
        private String id;
        private String name;
        private String version;
    
        public String getId(){
            return id;
        }
        public void setId(String id){
            this.id = id;
        }
    
        public String getName(){
            return name;
        }
        public void setName(String name){
            this.name = name;
        }
    
        public String getVersion(){
            return version;
        }
        public void setVersion(String version){
            this.version = version;
        }
    }
    
    • 修改 MainActivity 中的代码:
    public class MainActivity extends AppCompatActivity {
    
        private static final String TAG = "MainActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Button sendRequest = findViewById(R.id.send_request);
            sendRequest.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v){
                    sendRequestWithHttpURLConnection();
                }
            });
        }
    
        /**
         * 定义一个发送请求方法,具体的逻辑都在里面编写
         */
        private void sendRequestWithHttpURLConnection(){
            // 因为网络请求是耗时操作,所以开启子线程来发起请求
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        OkHttpClient client = new OkHttpClient();
                        Request request = new Request.Builder()
                                // 指定访问的服务器地址是电脑本机
                                .url("http://10.0.2.2/get_data.json")
                                .build();
                        Response response = client.newCall(request).execute();
                        String responseData = response.body().string();
                        parseJSONWithGSON(responseData);
                    }catch(IOException e){
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        private void parseJSONWithGSON(String jsonData){
            Gson gson = new Gson();
            List<App> appList = gson.fromJson(jsonData, new TypeToken<List<App>>(){}.getType());
            for(App app : appList){
                Log.d(TAG, "id is " + app.getId());
                Log.d(TAG, "name is " + app.getName());
                Log.d(TAG, "version is " + app.getVersion());
            }
        }
    }
    

    5. 封装一个网络库

    当想要发起网络请求时,只需调用里面的静态方法即可

    5.1 使用 HttpURLConnection 进行封装
    • 定义一个接口用于回调:
    public interface HttpCallbackListener {
    
        /**
         * 当服务器成功响应请求时调用
         * @param response 服务器返回的数据
         */
        void onFinish(String response);
    
        /**
         * 当进行网络操作出现错误时调用
         * @param e 异常对象
         */
        void onError(Exception e);
    }
    
    • 定义一个公共类用于发起网络请求:
    public class HttpUtil {
    
        public static void sendHttpRequest(final String address, final HttpCallbackListener listener){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    HttpURLConnection connection = null;
                    BufferedReader reader = null;
                    try{
                        URL url = new URL(address);
                        connection = (HttpURLConnection)url.openConnection();
                        connection.setRequestMethod("GET");
                        connection.setConnectTimeout(8000);
                        connection.setReadTimeout(8000);
                        // DoInput() 用于 GET,默认是 true,可以不调用
                        // DoOutput() 用于 POST,默认是 false,用 POST 时一定要调用
                        connection.setDoInput(true);
                        connection.setDoOutput(true);
                        InputStream input = connection.getInputStream();
                        reader = new BufferedReader(new InputStreamReader(input));
                        StringBuilder response = new StringBuilder();
                        String line;
                        while((line = reader.readLine()) != null){
                            response.append(line);
                        }
                        if(listener != null){
                            // 回调 onFinish() 方法
                            listener.onFinish(response.toString());
                        }
                    }catch(Exception e){
                        if(listener != null){
                            // 回调 onError() 方法
                            listener.onError(e);
                        }
                    }finally{
                        try{
                            if(reader != null){
                                reader.close();
                            }
                        }catch (IOException e){
                            e.printStackTrace();
                        }
                        if(connection != null){
                            connection.disconnect();
                        }
                    }
                }
            }).start();
        }
    }
    
    • 发起请求时:
    HttpUtil.sendHttpRequest(address, new HttpCallbackListener(){
        @Override
        public void onFinish(String response){
            // 根据返回内容执行逻辑
        }
        @Override
        public void onError(Exception e){
            // 对异常情况进行处理
        }
    });
    
    5.2 使用 OkHttp 进行封装
    • HttpUtil 中加入 sendOkHttpRequest() 方法:
    public class HttpUtil {
        
        public static void sendOkHttpRequest(String address, okhttp3.Callback callback){
            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder()
                    .url(address)
                    .build();
            client.newCall(request).enqueue(callback);
        }
    }
    
    • 调用请求方法时:
    HttpUtil.sendOkHttpRequest("https://www.baidu.com", new okhttp3.Callback(){
        @Override
        public void onResponse(Call call, Response response)throws IOException{
            // 得到服务器返回的具体内容
            String responseData = response.body().string();
        }
        @Override
        public void onFailure(Call call, IOException e){
            // 在这里对异常情况进行处理
            e.printStackTrace();
        }
    });
    

    相关文章

      网友评论

          本文标题:第 9 章 - Android 网络编程

          本文链接:https://www.haomeiwen.com/subject/udszwftx.html