android第九章-使用网络技术

一、WebView的用法

遇到一些特殊的请求:在程序中展示一些网页。但加载和显示网页都是浏览器的任务,在不打开浏览器的情况下先写一个浏览器,webView控件可以在应用程序中嵌入一个浏览器,从而展示各种各样的网页。

1.新建一个WebViewTest项目,修改activity_main.xml,定义了一个WebView控件,用于显示网页。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <WebView
        android:id="@+id/web_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

2.修改MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        WebView webView = (WebView) findViewById(R.id.web_view);
        webView.getSettings().setJavaScriptEnabled(true); //让WebView支持javaScript脚本
        webView.setWebViewClient(new WebViewClient());//目标网页在应用程序打开
        webView.loadUrl("https://www.baidu.com/");
        WebSettings webSettings = webView.getSettings();
        webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //设置缓存
        webSettings.setDomStorageEnabled(true);//设置适应Html5 重点是这个设置
    }

}

注意:android9.0系统默认禁止http协议,即禁止明文传输,必须使用https来通讯;默认uri是https开头的,如果你的uri是http,要到AndroidManifest.xml加入这一句:android:usesCleartextTraffic="true"

如果你想打开你手机的默认浏览器,可以参考:https://www.go2live.cn/program/android/%E8%A7%A3%E5%86%B3-android-neterr_unknown_url_scheme.html

3.修改AndroidManifest.xml,添加权限的声明:

<uses-permission android:name="android.permission.INTERNET"/>

运行程序,可以看到,WebViewTest这个程序现在已经具备了一个简易浏览器的功能,不仅成功将百度的首 页展示了出来,还可以通过点击链接浏览更多的网页。

 

 

 二、使用HttpURLConnection

在Android上发送HTTP的请求一般有两种方式:

1、HttpURLConnection

2、HttpClient

由于后者存在的API数量过多,Android团队也不建议使用这种方式,在Android6.0系统中,已经将其完全移除

 学习使用HttpURLConnection:

首先:获取到HttpURLConnection的实例,一般只需要new 一个URL对象,并传入目标的网络地址

然后:再调用openConnection()方法即可:

URL url = new URL("http://www.baidu.com");

HttpURLConnection conn = (HttpURLConnection)url.openConnection();

 

在得到了HttpURLConnection的实例之后,可以设置HTTP的请求所有方法

常见的方法主要用:GRT、POST

GET:表示希望从服务器洪获取数据

POST:表示提交数据给服务器

写法:conn.serRequestMethod("GET")

 

接下来就是自由定制:设置超时连接 、读取数据超时的秒数、以及服务器西药得到的一些消息头等

conn.setConnectTimeout(1000)

conn.setReadTimeout(3000)

 

之后再调用getInputStream()方法就可以获得服务器返回的数据流

InputStream in = conn.getInputStream()

 

最后可以使用disconnect()方法将这恶HTTP连接关闭

conn.disconnect()

示例代码:

(1)activity_main.xml::ScrollView,它是用来做什么的呢?由于手机屏幕的空间一 般都比较小,有些时候过多的内容一屏是显示不下的,借助ScrollView控件的话,我们就可以以 滚动的形式查看屏幕外的那部分内容。另外,布局中还放置了一个Button和一个TextView, Button用于发送HTTP请求,TextView用于将服务器返回的数据显示出来。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发送HTTP请求"/>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:id="@+id/response_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </ScrollView>
</LinearLayout>

 

(2)MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private TextView textView;
    private Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.response_text);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);
    }

    private void sendRequestHttpURLConnection() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().detectNetwork().penaltyLog().build());
                HttpURLConnection httpURLConnection = null;
                BufferedReader br = null;
                try {
                    URL url = new URL("https://www.baidu.com/");
                    httpURLConnection = (HttpURLConnection) url.openConnection();
                    httpURLConnection.setRequestMethod("GET");
                    httpURLConnection.setConnectTimeout(8000);
                    httpURLConnection.setReadTimeout(8000);
                    InputStream in = httpURLConnection.getInputStream();
                    br = new BufferedReader(new InputStreamReader(in));
                    StringBuilder builder = new StringBuilder();
                    String line = null;
                    while ((line = br.readLine()) != null) {
                        builder.append(line);
                    }
                    Log.i("MainActivity", builder.toString());
                    //textView.setText(builder.toString());
                    showResponse(builder.toString());
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (br != null) {
                        try {
                            br.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (httpURLConnection != null) {
                        httpURLConnection.disconnect();
                    }
                }
            }
        }).start();
    }

    private void showResponse(final String response) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //在这里进行UI操作,将结果显示在TextView上
                textView.setText(response);
            }
        });

    }
        @Override
        public void onClick (View v){
            if (v.getId() == R.id.button) {
                sendRequestHttpURLConnection();
            }
        }
    }

因为Android是不允许在子线程中进行UI操作的,所以我们需要 通过showResponse()方法将线程切换到主线程,然后再更新UI元素。

 

(3)修改 AndroidManifest.xml中的代码,声明一下网络权限:
 <uses-permission    android:name="android.permission.INTERNET"    />

 

运行程序,点击发送Http按钮后:

 

三、使用OkHttp

        OkHttp是由鼎鼎大名的Square公司开发的,这个公司在开源事业上面贡献良多,除了OkHttp之 外,还开发了像Picasso、Retrofit等著名的开源项目。OkHttp不仅在接口封装上面做得简单易 用,就连在底层实现上也是自成一派,比起原生的HttpURLConnection,可以说是有过之而无不 及,现在已经成了广大Android开发者首选的网络通信库。

第一步:

添加okHttp依赖库,编辑app/build.gradle文件,在 dependencies闭包中添加如下内容:implementation("com.squareup.okhttp3:okhttp:4.7.2")

添加上述依赖会自动下载两个库,一个是OkHttp库,一个是Okio库,后者是前者的通信基础。 其中3.4.1是我写本书时OkHttp的最新版本,你可以访问OkHttp的项目主页来查看当前最新的版 本是多少。https://square.github.io/okhttp/
第二步:发起一条HTTP请求

1.创建一个OkHttpClient的实例;

2.创建一个Request 对象,可以通过url() 方法来 设置目标的网络地址;

3.调用OkHttpClient的newCall() 方法来创建一个Call 对象,并调用它的execute() 方法 来发送请求并获取服务器返回的数据

4.将数据显示到TextView中

示例代码,xml文件与HttpURLConnection一致

修改MAinActivity.java

 1 public class MainActivity extends AppCompatActivity implements View.OnClickListener {
 2     private TextView textView;
 3     private Button button;
 4 
 5     @Override
 6     protected void onCreate(Bundle savedInstanceState) {
 7         super.onCreate(savedInstanceState);
 8         setContentView(R.layout.activity_main);
 9         textView = (TextView) findViewById(R.id.response_text);
10         button = (Button) findViewById(R.id.button);
11         button.setOnClickListener(this);
12     }
13 
14     private void sendRequestHttpURLConnection() {
15         new Thread(new Runnable() {
16             @Override
17             public void run() {
18                 try {
19                     OkHttpClient client=new OkHttpClient();//创建OkHttpClient实例
20                     //发起一条Http请求
21                     Request request=new Request.Builder()
22                             .url("https://www.baidu.com").build();
23                     Response response=client.newCall(request).execute();
24                     //获取返回的数据
25                     String respondData=response.body().string();
26                     showResponse(respondData);
27                 } catch (Exception e) {
28                     e.printStackTrace();
29                 }
30 
31             }
32         }).start();
33     }
34 
35     private void showResponse(final String response) {
36         runOnUiThread(new Runnable() {
37             @Override
38             public void run() {
39                 //在这里进行UI操作,将结果显示在TextView上
40                 textView.setText(response);
41             }
42         });
43 
44     }
45         @Override
46         public void onClick (View v){
47             if (v.getId() == R.id.button) {
48                 sendRequestHttpURLConnection();
49             }
50         }
51     }

不要忘记修改 AndroidManifest.xml中的代码,声明一下网络权限:
 <uses-permission    android:name="android.permission.INTERNET"    />

运行程序:

 

四、解析XML格式数据

       在网络上传输数据时最常用的格式有两种:XML和JSON。在开始之前我们还需要先解决一个问题,就是从哪儿才能获取一段XML格式的数据呢?这里我 准备教你搭建一个最简单的Web服务器,在这个服务器上提供一段XML文本,然后我们在程序 里去访问这个服务器,再对得到的XML文本进行解析。

搭建web服务器的步骤https://www.cnblogs.com/panqiaoyan/p/12982323.html

搭建完服务器后,开始进行解析XML文件的学习。解析XML格式的数据其实也有挺多种方式的,本节中我们学习比较常用的两种,Pull解析和 SAX解析。

(一)Pull解析方式

xml数据:

 

 既然XML格式的数据已经提供好了,现在要做的就是从中解析出我们想要得到的那部分内容。 修改MainActivity中的代码,如下所示:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private TextView textView;
    private Button button;
    private String Tag="MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.response_text);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);
    }

    private void sendRequestHttpURLConnection() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    OkHttpClient client=new OkHttpClient();
                    Request request=new Request.Builder().
                            url("http://192.168.XX.X/get_data.xml").build();
                    Response response=client.newCall(request).execute();
                    String responseData=response.body().string();
                    parseXMLWithPull(responseData);

                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }).start();
    }

    private void parseXMLWithPull(String responseData) {
        try {
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            XmlPullParser parse = factory.newPullParser();
            parse.setInput(new StringReader(responseData));//得到要解析的xml字符串
            int eventType= parse.getEventType();//获取解析的类型
            Log.i(Tag,eventType+"");
            String id="";
            String name="";
            String version="";
            while(eventType!=parse.END_DOCUMENT){
                String nodeName=parse.getName();//获取当前标签的名字
                switch (eventType){
                    case XmlPullParser.START_TAG://开始解析标签
                        Log.i(Tag,nodeName);
                        if(id.equals(nodeName)){//如果标签是id
                            id=parse.nextText();//获取当前标签的具体内容
                        }else if(name.equals(nodeName)){//如果标签是name
                            name=parse.getName();
                        }else if(version.equals(nodeName)){//如果标签是version
                            version=parse.nextText();
                        }
                        break;
                    case XmlPullParser.END_TAG://结束解析标签
                        Log.i(Tag,nodeName);
                        break;
                    default:
                        break;
                }
                eventType=parse.next();
                Log.i(Tag,eventType+"");
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
        @Override
        public void onClick (View v){
            if (v.getId() == R.id.button) {
                sendRequestHttpURLConnection();
            }
        }
    }

 

Request request=new Request.Builder().url("http://192.168.XX.X/get_data.xml").build();里面url是你自己服务器的ip地址+xml文件名

 

parseXMLWithPull()方法是pull解析代码,现在来解释一下,该方法传入的是get_data.xml文件的数据。

我们首先创建了一个XmlPullParserFactory(pull解析工厂),通过解析工厂获得XmlPullParser(解析员),我们的解析员通过setInput(new StringReader(xml字符串))得到你想要解析的xml字符串。用一个循环进行解析。
int eventType=parse.getEventType();获取解析的类型。在pull解析当中有五种解析类型,分别是
XmlPullParser.START_DOCUMENT=0(开始解析文档),
XmlPullParser.EDN_DOCUMENT=1(结束解析文档),
XmlPullParser.START_TAG=2(开始解析标签),
XmlPullParser.END_TAG=3(结束解析标签);
XmlPullParser.TEXT=4(解析文本时用的);
先来看看我解析出来的结果:
从上图可以看到看到,
XmlPullParser.START_DOCUMENT,与XmlPullParser.END_DOCUMENT只执行了一次。
在这个当中我们发现2,3,4都在不停的执行。所以我们可以总结pull解析的原理。拿下面这个来举例说明原理。
pull解析这个xml字符串,首先从<apps>开始解析,所以出现数字0.接下来进入开始标签<apps>显示数字2,此时会进入TEXT。无论你有没有写文本,它都在哪里,显示4.遇到结尾显示1.

(二)SAX解析方式
SAX解析也是一种特别常用的XML 解析方式,虽然它的用法比Pull解析要复杂一些,但在语义方面会更加清楚。 通常情况下我们都会新建一个类继承自DefaultHandler    ,并重写父类的5个方法。
 
package com.example.networktest;

import android.util.Log;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

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();
    }

    /**
     * 开始解析标签,从xml解析出的数据会以参数的形式传入到方法中
     * @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;
    }
    //获取标签的内容
    @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);
        }
    }
    //结束解析标签
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if("app".equals(localName)){
            Log.d("ContentHandler","id= "+id.toString().trim());
            Log.d("ContentHandler","name= "+name.toString().trim());
            Log.d("ContentHandler","version= "+version.toString().trim());
            //最后要将StringBuilder清除
            id.setLength(0);
            name.setLength(0);
            version.setLength(0);
        }
    }
    //结束解析xml文件
    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
    }


}

 

修改MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private TextView textView;
    private Button button;
    private String Tag="MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.response_text);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);
    }

    private void sendRequestHttpURLConnection() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    OkHttpClient client=new OkHttpClient();
                    Request request=new Request.Builder().
                            url("http://192.168.XX.X/get_data.xml").build();
                    Response response=client.newCall(request).execute();
                    String responseData=response.body().string();
                    parseXMLWithSax(responseData);

                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }).start();
    }

    private void parseXMLWithSax(String responseData) {
        try {
            SAXParserFactory factory=SAXParserFactory.newInstance();
            XMLReader xmlReader=factory.newSAXParser().getXMLReader();
            ContentHandler contentHandler=new ContentHandler();
            xmlReader.setContentHandler(contentHandler);
            xmlReader.parse(new InputSource(new StringReader(responseData)));

        }catch (Exception e){
            e.printStackTrace();
        }
    }
        @Override
        public void onClick (View v){
            if (v.getId() == R.id.button) {
                sendRequestHttpURLConnection();
            }
        }
    }

运行程序,可以看到:

 

五、解析JSON格式数据

比起XML,JSON的主要优势在于它的体积更小,在网络上传输的时候可以更省流 量。但缺点在于,它的语义性较差,看起来不如XML直观。

 在开始之前,我们还需要在E:\Apache24\htdocs目录中新建一个get_data.json的文件,然后编辑这个 文件,并加入如下JSON格式的内容:

 好了,这样我们把JSON格式的数据也准备好了,下面就开始学习如何在Android程序中解析这些 数据吧。


(一)使用JSONObject

修改MainActivity.java的代码

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private TextView textView;
    private Button button;
    private String Tag="MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.response_text);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);
    }

    private void sendRequestHttpURLConnection() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    OkHttpClient client=new OkHttpClient();
                    Request request=new Request.Builder().
                            url("http://192.168.56.1/get_data.json").build();
                    Response response=client.newCall(request).execute();
                    String responseData=response.body().string();
                    parseJSONWithJSONObject(responseData);

                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }).start();
    }

    private void parseJSONWithJSONObject(String responseData) {
        try {
            JSONArray jsonArray=new JSONArray(responseData);
            for(int i=0;i<jsonArray.length();i++){
                JSONObject jsonObject=jsonArray.getJSONObject(i);
                String id=jsonObject.getString("id");
                String name=jsonObject.getString("name");
                String version=jsonObject.getString("version");
                Log.d("MainActivity","id= "+id.toString().trim());
                Log.d("MainActivity","name= "+name.toString().trim());
                Log.d("MainActivity","version= "+version.toString().trim());
            }

        }catch (Exception e){
            e.printStackTrace();
        }
    }
        @Override
        public void onClick (View v){
            if (v.getId() == R.id.button) {
                sendRequestHttpURLConnection();
            }
        }
    }

运行程序,点击按钮,查看Logcat:

 

(二)使用GSON

谷歌提供 的GSON开源库可以让解析JSON数据的工作简单到让你不敢想象的地步,那我们肯定是不能错 过这个学习机会的。

先添加依赖库:

 

 gson最新版本号可以看这个:https://mvnrepository.com/artifact/com.google.code.gson/gson

gson主要就是可以将一段JSON格式的字符串自动映射成 一个对象,从而不需要我们再手动去编写代码进行解析了。
比如说一段JSON格式的数据如下所示:
{"name":"Tom","age":20}
那我们就可以定义一个Person    类,并加入name    和age    这两个字段,然后只需简单地调用如下 代码就可以将JSON数据自动解析成一个Person    对象了:
Gson    gson    =    new    Gson();
Person    person    =    gson.fromJson(jsonData,    Person.class);
如果需要解析的是一段JSON数组会稍微麻烦一点,我们需要借助TypeToken将期望解析成的数 据类型传入到fromJson()    方法中,如下所示:
List<Person>    people    =    gson.fromJson(jsonData,    new    TypeToken<List<Person>>()    {}.getType());
 
首先新增一个App    类,并加 入id    、name    和version    这3个字段,如下所示:

package com.example.networktest;

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;
    }

}
View Code

然后修改MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private TextView textView;
    private Button button;
    private String Tag="MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.response_text);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);
    }

    private void sendRequestHttpURLConnection() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    OkHttpClient client=new OkHttpClient();
                    Request request=new Request.Builder().
                            url("http://192.168.56.1/get_data.json").build();
                    Response response=client.newCall(request).execute();
                    String responseData=response.body().string();
                    parseJSONWithGSON(responseData);

                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }).start();
    }

    private void parseJSONWithGSON(String responseData) {
        try {
            Gson gson=new Gson();
            List<App> applist=gson.fromJson(responseData,new TypeToken<List<App>>(){}.getType());
            for(App app:applist){
                Log.d("MainActivity","id= "+app.getId());
                Log.d("MainActivity","name= "+app.getName());
                Log.d("MainActivity","version= "+app.getVersion());
            }

        }catch (Exception e){
            e.printStackTrace();
        }
    }
        @Override
        public void onClick (View v){
            if (v.getId() == R.id.button) {
                sendRequestHttpURLConnection();
            }
        }
    }

运行程序:

 

 六、网络编程的最佳实践

目前你已经掌握了HttpURLConnection和OkHttp的用法,知道了如何发起HTTP请求,以及解析 服务器返回的数据,但也许你还没有发现,之前我们的写法其实是很有问题的。因为一个应用 程序很可能会在许多地方都使用到网络功能,而发送HTTP请求的代码基本都是相同的,如果我 们每次都去编写一遍发送HTTP请求的代码,这显然是非常差劲的做法。
没错,通常情况下我们都应该将这些通用的网络操作提取到一个公共的类里,并提供一个静态 方法,当想要发起网络请求的时候,只需简单地调用一下这个方法即可。比如使用如下的写 法:

(一)使用HttpURLConnection发起HTTP请求

public class HttpUtil {
    public static void sendHttpRequest(final String address,final HttpCallbackListener listener){
        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection connection=null;
                try {
                    URL url=new URL(address);
                    connection=(HttpURLConnection)url.openConnection();
                    connection.setRequestMethod("GET");
                    connection.setConnectTimeout(8000);
                    connection.setReadTimeout(8000);
                    connection.setDoInput(true);
                    connection.setDoOutput(true);
                    InputStream in=connection.getInputStream();//获取到服务器返回的输入流
                    BufferedReader br=new BufferedReader(new InputStreamReader(in));
                    StringBuilder response=new StringBuilder();
                    String line="";
                    while((line=br.readLine())!=null){//对输入流进行读取
                        response.append(line);
                    }
                    if(listener!=null){
                        listener.onFinish(response.toString());
                    }
                }catch(Exception e){
                    if(listener!=null){
                        listener.onError(e);
                    }
                }finally {//释放资源
                    if(connection!=null){
                        connection.disconnect();
                    }
                }
            }
        }).start();

    }
}

因为线程里是无法通过return返回数据的,所以我们定义了一个HttpCallbackListener接口,用于数据的返回,也叫java的回调机制,代码如下所示:

interface HttpCallbackListener {
    void onFinish(String response);//当服务器成功响应时调用
    void onError(Exception e);//当进行网络操作时发生错误时调用
}

现在sendHttpRequest()   方法接收两个参数,因此我们在调用它的时候还需要将 HttpCallbackListener的实例传入,修改MainActivity.java代码,如下所示:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private TextView textView;
    private Button button;
    private String Tag="MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.response_text);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);
    }
    private void sendRequestHttpURLConnection() {
       HttpUtil httpUtil=new HttpUtil();
       httpUtil.sendHttpRequest("http://www.baidu.com/", new HttpCallbackListener() {
           @Override
           public void onFinish(String response) {
               setView(response);
           }

           @Override
           public void onError(Exception e) {
               e.printStackTrace();
           }
       });
    }
    //将服务器返回的数据显示在TextView中
    private void setView(final String str){
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                textView.setText(str);
            }
        });

    }
        @Override
        public void onClick (View v){
            if (v.getId() == R.id.button) {
                sendRequestHttpURLConnection();

            }
        }
    }

运行程序,点击按钮:

 

(二)使用okHttp发起HTTP请求

首先创建一个类,将通用的网络操作提取到一个公共的类里,并提供一个静态 方法,如下所示:

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);
    }
}

可以看到,sendOkHttpRequest() 方法中有一个okhttp3.Callback 参数,这个是OkHttp 库中自带的一个回调接口,类似于我们刚才自己编写的HttpCallbackListener。然后 在client.newCall() 之后没有像之前那样一直调用execute() 方法,而是调用了一 个enqueue() 方法,并把okhttp3.Callback 参数传入。相信聪明的你已经猜到了,OkHttp 在enqueue() 方法的内部已经帮我们开好子线程了,然后会在子线程中去执行HTTP请求,并将最终的请求结果回调到okhttp3.Callback当中。

接下来修改MainActivity.java代码,如下所示:

public class MainActivity<sendOkHttpURLConnection> extends AppCompatActivity implements View.OnClickListener {
    private TextView textView;
    private Button button;
    private String Tag="MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.response_text);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);
    }
    private void sendOkHttpURLConnection(){
        HttpUtil.sendOkHttpRequest("http://www.baidu.com/", new okhttp3.Callback() {
            @Override//在这里对异常情况进行处理
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                e.printStackTrace();
            }

            @Override//得到服务器返回的具体内容
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                String responseData=response.body().string();
                setView(responseData);
            }
        });
    }
    //将服务器返回的数据显示在TextView中
    private void setView(final String str){
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                textView.setText(str);
            }
        });

    }
        @Override
        public void onClick (View v){
            if (v.getId() == R.id.button) {
                sendOkHttpURLConnection();

            }
        }
    }

运行程序,点击按钮:





参考文章:

1.https://www.cnblogs.com/Mrchengs/p/10714362.html

2.https://blog.csdn.net/juesai2015/article/details/78206118

3.https://www.jianshu.com/p/4cd15faaa01d

posted @ 2020-05-27 17:16  PLG  阅读(193)  评论(0编辑  收藏  举报