flex布局小结

STM32笔记之AD9833

  返回  

第一行代码第九章使用网络技术Android11

2021/8/21 20:20:50 浏览:

目录

一、webView

二、使用Http协议访问网络

2.1 使用HttpURLConnection访问网络

2.1 使用OkHttp访问网络

三、解析XML文件

3.1 Pull解析方式

3.2 SAX 解析方式

四、解析JSON文件

4.1 JSONObject解析方式

4.2 GSON解析方式

五、回调机制的学习和使用 


一、webView

这里主要就是要了解原理和过程是什么。

就是我们作为客户端,向服务器发送了一条请求,然后服务器返回一个响应,并且是以流的形式返回,然后我们读取这个流。比如这里访问了百度的服务器,百度以流的形式给我们响应,然后我们读完流之后,发现百度的服务器就是给我们返回了一堆html代码,然后浏览器的内核webkit对这个html代码进行了解析,所以我们可以看到百度的页面。

主要就是四个流程:1、向服务器发送https请求;2、服务器接收到我们的请求给我们一个响应(以流的形式);3、我们接收响应并且对这个响应进行解析;4、解析完之后将页面展示出来。

而这个webView就是替我们做好了以上所有的工作了,注意它就是一个控件,这里又体现了控件其实就是一个Java类了。

<?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/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    
</LinearLayout>
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.webview);
        //首先给webView设置一些属性
        WebSettings settings = webView.getSettings();
        //直接JavaScript脚本
        settings.setJavaScriptEnabled(true);
        //当打开另外一个网页时不要打开浏览器 而是仍然展示在当前页面上面
        webView.setWebViewClient(new WebViewClient());
        //载入这个浏览器要执行的动作
        webView.loadUrl("https://wwww.baidu.com");
    }
}

二、使用Http协议访问网络

这一部分学完《计算机网络》这一本书之后应该会理解的更清楚。在android 8.0之后,访问网络要使用https了,所以这里有很多坑。访问百度什么的都需要用https了。

这里主要就是要完成上面的四个流程,向服务器发送https请求,接收响应,对响应进行解析,将解析完的结果展示出来。然后注意这是耗时操作,需要创建一个线程来执行。

2.1 使用HttpURLConnection访问网络

主要就是要记住流程,然后理解什么是URL,什么是HttpURLConnection,是以什么形式返回的

1、创建一个URL对象

2、利用这个URL对象创建一个连接HttpURLConnection对象

3、给这个连接对象设置一些连接的属性,比如连接超时时间……

4、连接上服务器,并且读取服务器返回来的响应数据,是以流的形式返回的

5、读取这个流,将流里面的内容读取出来

6、以上就完成了网络的一次操作,然后最终可以将读出内容展示在需要的地方

代码如下

private void sendRequestWithHttpUrlConnection(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection connection = null;
                BufferedReader reader = null;
                try {
                    //【1】创建URL对象
                    /*
                    * Class URL represents a Uniform Resource Locator, a pointer to a "resource" on the World Wide Web.
                    * A resource can be something as simple as a file or a directory, or it can be a reference to a more complicated object,
                    * such as a query to a database or to a search engine.
                    * */
                    URL url = new URL("https://www.baidu.com");
                    
                    //【2】利用这个对象和服务器进行连接
                    connection = (HttpURLConnection) url.openConnection();
                    
                    //【3】设置连接的一些属性
                    connection.setRequestMethod("GET");
                    connection.setConnectTimeout(8000);
                    connection.setReadTimeout(8000);
                    
                    //【4】读取这个连接以流的形式返回的内容
                    //Returns an input stream that reads from this open connection.
                    InputStream in = connection.getInputStream();
                    
                    //【5】读这个流
                    reader = new BufferedReader(new InputStreamReader(in));
                    String line;
                    StringBuffer content = new StringBuffer();
                    while ((line = reader.readLine())!=null){
                        content.append(line);
                    }
                    
                    //【6】将内容展示到控件上面
                    showData(content.toString());
                    //【7】解析资源

                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    //【7】关闭相关的资源 主要就是连接和流
                    if (reader!=null){
                        try {
                            reader.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (connection!=null){
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }

向服务器提交数据,主要就是将提交模式改为“POST”方式,然后用一个输出流写出去:

connection.setRequestMethod("POST");
DataOutputStream out = new DataOutputStream(connection.getOutputStream()); 
out.writeBytes("username=admin&password=123456");

2.1 使用OkHttp访问网络

其实原理都是一样的,发送请求,接收响应,解析响应。这里和HttpURLConnection最大的不同就是服务器返回的数据是一个响应对象,响应的内容在响应体里面,拿到响应体里面的内容在转化为string就行了,比HttpUrlConnection简单。

使用之前需要先添加依赖:

implementation 'com.google.code.gson:gson:2.8.7'

一样需要记住流程,理解什么是client,什么是call,什么是request。

1、创建一个发送请求和接收的OkHttpClient

2、准备一个请求Request,这里创建Request使用.Builder().build()方式创建的,可以在里面设置一些属性

3、利用client来执行这个请求并且返回一个响应对象

4、拿到响应对象上的响应体内容

private void sendRequestWithOkHttpConnection(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //【1】创建可以发送请求的客户端对象
                    //A call is a request that has been prepared for execution.
                    //Factory for calls, which can be used to send HTTP requests and read their responses.
                    OkHttpClient client = new OkHttpClient();
                    //【2】创建即将要发送的请求的内容
                    Request request =  new Request.Builder()
                            .url("https://www.baidu.com")
                            .build();
                    //【3】客户端创建请求发送并且执行 返回一个响应对象
                    Response response = client.newCall(request).execute();
                    //【4】读取响应对象的响应体内容 转化为String类型
                    String responseData = response.body().string();
                    //【5】将内容展示在控件上面
                    showData(responseData);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

利用okHttpClient向服务器提交数据:

RequestBody requestBody = new FormBody.Builder()
                .add("username","admin")
                .add("password","123")
                .build();

Request request = new Request.Builder()
                .url("https://www.baidu.com")
                .post(requestBody)    
                .build();

//后面就和上面的一样了

Note that:这里书中用的是“http://www.baidu.com”,而现在必须要使用“https://www.baidu.com”来访问这些服务器了。

然后联网操作是需要权限的,需要加一下权限

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

三、解析XML文件

这一部分需要回顾Tomcat服务器的相关知识,开启服务器,关闭服务器,服务器中的网站托管,要被访问的资源要放在哪里。XML文件是什么,有什么用,为什么要有xml文件和json文件,XML文件怎么写。然后最重要的就是xml文件的两种解析方式,Pull解析和SAX解析。

大体思路过程都差不多:

1、首先明确要被解析的数据是什么在哪里;

2、拿到解析器工厂对象,注意创建这个对象的方式是newInstance的方式

3、利用解析器工厂创建出解析器对象

4、为解析器对象设置需要解析的内容

5、开始解析,解析分为几个重要的部分:文档的开头,文档的结束,标签的开始,标签的结束,标签当中的内容。

注意:需要将访问的地址设为访问我们自己的服务器,这里的网址是让我最懵逼的,到底要怎么写,https的方式测试过是不能访问到自己的服务器的,然后再手机或者模拟机上访问本地电脑的地址最终是在cmd当中敲的ipConfig中的ipv4地址,然后采用的协议是http,端口号8080。还需再清单文件中设置允许http访问方式的属性。

<application
        android:usesCleartextTraffic="true"
        ……
</application>
Request request =  new Request.Builder()
                      //.url("https://www.csdn.net/")
                      //.url("http://172.20.10.2:8080/Myweb/get_data.xml")
                      //.url("http://192.168.0.115:8080/Myweb/get_data.xml")
                      //这个是ipConfig中的ipv4地址
                      .url("http://192.168.0.115:8080/Myweb/get_data.json")
                      .build();

以上就是一些坑,当然到现在都没有搞明白为什么是这样的,需要找时间把它真正搞明白。

解析的xml文档:

<?xml version="1.0" encoding="utf-8"?>
<apps>
	<app>
		<id>1</id>
		<name>java</name>
		<version>2.4</version>
	</app>
	<app>
		<id>2</id>
		<name>Python</name>
		<version>1.0</version>
	</app>
</apps>

3.1 Pull解析方式

这里在解析过程中要理解getEventType()拿到的是这个元素的类型,类型有DOCUMENT、START_TAG标签………注意和getName()的区别,getName()拿到的是标签的名字,而不是标签的类型!在解析过程中我们是通过标签名字和标签类型来解析的。

public void parseXMLWithPull(String data){
        try {
            //【1】首先拿到pull解析工厂
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            //【2】通过解析工厂拿到解析器
            XmlPullParser xmlPullParser = factory.newPullParser();
            //【3】将要解析的数据放入解析器当中
            xmlPullParser.setInput(new StringReader(data));
            //【4】开始对数据进行解析
            //【4.1】解析先拿到第一个标签的类型:Returns the type of the current event (START_TAG, END_TAG, TEXT, etc.)
            int eventType = xmlPullParser.getEventType();
            String name = "";
            String id = "";
            String version = "";
            //【4.2】只要标签类型不是xml文档的结束标签 那么就可以一直解析
            while (eventType!=XmlPullParser.END_DOCUMENT){
                //【4.2】拿到标签的名字
                String nodeName = xmlPullParser.getName();
                //【4.3】对文档标签进行判断
                switch (eventType){
                    //【4.4】如果是开始标签
                    case XmlPullParser.START_TAG:
                        if ("id".equals(nodeName)){
                            //【4.5】获取标签的文本内容
                            //If current event is START_TAG then if next element is TEXT then element content is returned or
                            //if next event is END_TAG then empty string is returned, otherwise exception is thrown.
                            id = xmlPullParser.nextText();
                        }else if ("name".equals(nodeName)){
                            name = xmlPullParser.nextText();
                        }else if ("version".equals(nodeName)){
                            version = xmlPullParser.nextText();
                        }
                        break;
                    //【4.4】如果是结束标签
                    case XmlPullParser.END_TAG:
                        if ("app".equals(nodeName)){
                            Log.d(TAG, "parseXMLWithPullParser: id = "+id);
                            Log.d(TAG, "parseXMLWithPullParser: name ="+name);
                            Log.d(TAG, "parseXMLWithPullParser: version ="+version);
                        }
                        break;
                    default:
                        break;
                }
                eventType = xmlPullParser.next();
            }
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

3.2 SAX 解析方式

SAX 解析方式其实原理也都是差不多的,它则是用来SAXParser来创建多一个XMLReader,并且使用它采用了一个contentHandler的处理器,专门用来处理xml文档的,需要在reader中设置。

public void parseXMLWithSAXParser(String data){
        try {
            //【1】拿到parse解析工厂
            SAXParserFactory factory = SAXParserFactory.newInstance();
            //【2】通过解析工厂拿到解析器
            SAXParser saxParser = factory.newSAXParser();
            //【3】通过解析器拿到xmlReader 读取xml字符流
            XMLReader reader = saxParser.getXMLReader();
            //【4】为这个reader设置内容处理器contentHandler
            reader.setContentHandler(new ContentHandler());
            //【5】将需要解析的内容放入xmlReader里面开始解析
            reader.parse(new InputSource(new StringReader(data)));
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

public class ContentHandler extends DefaultHandler {

    StringBuilder name;
    StringBuilder id;
    StringBuilder version;
    String nodeName;

    private static final String TAG = "ContentHandler";

    @Override
    //解析文档的开始节点
    public void startDocument() throws SAXException {
        super.startDocument();
        name = new StringBuilder();
        id = new StringBuilder();
        version = new StringBuilder();
    }

    @Override
    //解析文档的内容开始标签
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        super.startElement(uri, localName, qName, attributes);
        //localName就是节点(标签)的名字
        nodeName = localName;
    }

    @Override
    //解析标签中的具体内容
    public void characters(char[] ch, int start, int length) throws SAXException {
        super.characters(ch, start, length);
        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 {
        super.endElement(uri, localName, qName);
        if ("app".equals(localName)){
            //记得一定要.toString().trim()  【疑问】为什么要这样?哪里有空格和换行符????
            Log.d(TAG, "endElement: id = "+id.toString().trim());
            Log.d(TAG, "endElement: name ="+name.toString().trim());
            Log.d(TAG, "endElement: version="+version.toString().trim());
            //最后记得将它们清空 否则会连续append
            id.setLength(0);
            name.setLength(0);
            version.setLength(0);
        }
    }


    @Override
    //解析文档的结束节点
    public void endDocument() throws SAXException {
        super.endDocument();
    }

}

四、解析JSON文件

需要理解Json是什么,如何写Json。其实JSON是一种偏向于面向对象的思想的文档,里面的每一个{  }可以当成一个对象来处理这里,以键值对的形式存在,包裹在一个集合当中,这个集合又可以被当作对象的集合。

解析的JSON文档

[{"id":"5","version":"5.5","name":"jackson Li"},
{"id":"6","version":"5.3","name":"jackson Chen"},
{"id":"7","version":"4.4","name":"jack Liu"}]

4.1 JSONObject解析方式

public void parseJSONWithJsonObject(String JsonData){
        try {
            //【1】创建一个JsonArray数组并且将json集合数据放进去
            JSONArray jsonArray = new JSONArray(JsonData);
            //【2】遍历将数据里面的JsonObject对象拿出来
            //【2.1】通过JsonObject对象拿出里面的数据
            for (int i = 0;i<jsonArray.length();i++){
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                Log.d(TAG, "parseJSONWithJsonObject: id="+jsonObject.getString("id"));
                Log.d(TAG, "parseJSONWithJsonObject: name="+jsonObject.getString("name"));
                Log.d(TAG, "parseJSONWithJsonObject: version="+jsonObject.getString("version"));
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

4.2 GSON解析方式

需要先添加依赖:

implementation 'com.google.code.gson:gson:2.8.7'

映射的对象:

public class App {
    String name;
    String id;
    String version;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }
}

解析:

public void parseJsonWithGSON(String jsonData){
        Gson gson = new Gson();
        List<App> apps = gson.fromJson(jsonData,new TypeToken<List<App>>(){}.getType());
        for (App app:apps){
            Log.d(TAG, "parseJsonWithGSON: id="+app.getId());
            Log.d(TAG, "parseJsonWithGSON: name="+app.getName());
            Log.d(TAG, "parseJsonWithGSON: version="+app.getVersion());
        }
    }

五、回调机制的学习和使用

这部分的内容个人认为非常培养编程思想(本知识点代码放到的是webViewTest当中的)

前因后果的阐述:刚开始,我们发现有很多的重复性代码,因此我们需要将其封装起来作为一个工具类。这里必须要理解什么是工具类,工具类一般就是直接使用的,并且使用起来很方便,有时候是给自己用,有时候也是给别人用,因此要封装的很好。这个工具类的目的是进行联网操作,然后最终我们需要获得联网之后得到的数据结果,然后进行相关的操作,一般主要就是用来更新ui。

一开始我们就是这样的:

public class HttpUtil {
    //主要就是要获取到服务器返回的内容
    //返回肯定是返回给调用者的
    public static void sendHttpRequest(String address){
        //String data = "";内部类访问外部局部变量必须将外部的局部变量定义为final!也就是外部这个变量是不可变的
        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.setReadTimeout(8000);
                    connection.setConnectTimeout(8000);
                    InputStream in = connection.getInputStream();
                    reader = new BufferedReader(new InputStreamReader(in));
                    StringBuilder content = new StringBuilder();
                    String line ="";
                    while ((line=reader.readLine())!=null){
                        content.append(line);
                    }
//Variable 'data' is accessed from within inner class, 
//needs to be final or effectively final
                    //data = content.toString();
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    if (reader!=null){
                        try {
                            reader.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (connection!=null){
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }
}

首先因为联网获取资源是耗时操作,因此我们需要开启一个分支线程来执行这个操作,但是方法是public void run(),返回值是void,因此是不可以返回任何数据的。于是我又想出来定义一个成员变量String data;然后在分支线程过程中将结果赋值给这个data,最后在sendHttpRequest()自定义的这个方法中返回,但是发现了一个经典的java基础错误:Variable 'data' is accessed from within inner class, needs to be final or effectively final 。也就是成员变量要是要在内部类中使用的话必须定义为final,而final修饰的又不能再次赋值了https://blog.csdn.net/qq_41864648/article/details/108214590 这篇文章中有讲解为什么不行,为什么要定义为final,因此这种方法是不行的。

然后再次进行改进

于是这时候我又想到和回调其实很接近的方式(和第10章的service实践中的下载案例中的那个监听器很像的方式)。我们在参数中传一个自定义的类进去,然后这个类中有方法。而这个方法就是可以拿到结果,然后进行相关的操作。我们在工具类方法中拿到这个自定义的类这个参数然后调用类里面的方法:

但是当我想要自定义类里面的方法的时候却发现了问题,自定义的类无法调用MainActivity中一些东西(当然可能可以让我们这个类继承某个类或者实现某个接口的方式来实现,但是有些复杂,后面有更好的方法),自定义了一个Java类,代码如下:

public class HttpCallbackListener01{
    public void showData(String data){
        //无法调用RunOnUIThread等一系列方法
    }
}

这样无法调用MainActivity……之类的方法,于是我直接在MainActivity中定义了一个内部类,内部类中就可以调用这些方法了,代码如下:

public class MainActivity extends AppCompatActivity {

    TextView textView;

    class HttpCallbackListener02 {
        public void showData(String content) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    textView.setText(content);
                }
            });
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView);

        HttpUtil.sendHttpRequest("https://www.baidu.com", new HttpCallbackListener02());

这样我们定义了一个内部类,然后里面定义了更新ui的方法,然后我们将这个创建出这个类的对象传到方法里面,这样我们在工具类中就可以调用这个方法了,非常顺利,代码如下:

public class HttpUtil {
    //主要就是要获取到服务器返回的内容
    //返回肯定是返回给调用者的
    public static void sendHttpRequest(final String address,final HttpCallbackListener02 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.setReadTimeout(8000);
                    connection.setConnectTimeout(8000);
                    InputStream in = connection.getInputStream();
                    reader = new BufferedReader(new InputStreamReader(in));
                    StringBuilder content = new StringBuilder();
                    String line ="";
                    while ((line=reader.readLine())!=null){
                        content.append(line);
                    }
//就是这里,我们调用了参数中的showData方法并且将结果传进去
                    listener.showData(content.toString());
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    if (reader!=null){
                        try {
                            reader.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (connection!=null){
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }
}

这样就完成了ui的更新,但是又出现了一个问题,这样确实是使用到这个工具类并且根据数据的结果更新了ui。但是却有一个很麻烦的地方,需要在MainActivity中定义这个内部类和方法,灵活性很差。并且如果是别人调用,别人根本就不知道要定义一个内部类和方法。

那有没有办法能让我们或别人在调用的时候就可以不用定义内部类,或者说调用的时候提醒调用者需要写具体方法的实现?有------->接口出现了,我们可以定义一个接口,接口里面写方法。然后调用这个工具类的时候需要传入这个接口的实现类!这样自然调用者在调用这个方法的时候,在传入这个接口参数的时候,就会去实现这个接口的方法了,在方法里面具体写结果的实现!这个接口方法中的参数就是由调用者传入的,就是联网返回的结果了。代码如下:

定义接口:

public interface HttpCallbackListener03 {
    void showData(String content);
}

编写具体接口的方法:

public class MainActivity extends AppCompatActivity {

    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView);

        HttpUtil.sendHttpRequest("https://www.baidu.com", new HttpCallbackListener03() {
            @Override
            public void showData(String content) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText(content);
                    }
                });
            }
        });
    }

}

调用接口:

public class HttpUtil {
    //主要就是要获取到服务器返回的内容
    //返回肯定是返回给调用者的
    public static void sendHttpRequest(final String address,final HttpCallbackListener03 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.setReadTimeout(8000);
                    connection.setConnectTimeout(8000);
                    InputStream in = connection.getInputStream();
                    reader = new BufferedReader(new InputStreamReader(in));
                    StringBuilder content = new StringBuilder();
                    String line ="";
                    while ((line=reader.readLine())!=null){
                        content.append(line);
                    }
                    listener.showData(content.toString());
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    if (reader!=null){
                        try {
                            reader.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (connection!=null){
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }
}

以上就完成了,需要强调接口的作用,谁定义的接口,谁写接口的实现,谁调用这个接口!


当然这里思路是最重要的,就是我们需要将联网的数据返回结果传出去,然后一般方法传不出去,于是我们利用这个方法传进来的一个对象参数,调用这个对象参数的方法,而方法的具体实现我们不要写死,我们让调用者来写,我们要提醒调用者重写,那么我们就利用传参时传的是接口,然后写一个匿名内部类的形式来写。

这是一种回调机制,充分利用了接口的作用。原理:当我们在一个方法需要根据结果进行相关的处理时,而这个结果又很难被return回去时(当然简单return也可以用这个方法)。我们就可以在这个方法的参数传入一个对象,然后就可以在这个方法里面调用这个对象的方法。而对象最好并且好像必须是接口,这样调用者才可以编写方法的具体实现(第10章实践中也充分体现了)。其实一般接口的作用就是重写接口的方法,分为定义接口,调用接口,编写接口具体实现.

这里利用这个HttpURLConnection的方式来进行的标准写法;

定义接口:

public interface HttpCallbackListener04 {
    void onFinish(String content);
    void onError(Exception e);
}

调用接口:


public class HttpUtil {
    public static void sendHttpRequest(final String address,final HttpCallbackListener04 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.setReadTimeout(8000);
                    connection.setConnectTimeout(8000);
                    InputStream in = connection.getInputStream();
                    reader = new BufferedReader(new InputStreamReader(in));
                    StringBuilder content = new StringBuilder();
                    String line ="";
                    while ((line=reader.readLine())!=null){
                        content.append(line);
                    }
                    Log.d(TAG, "run获得的数据: "+content.toString());
                    listener.onFinish(content.toString());
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    listener.onError(e);
                    e.printStackTrace();
                }finally {
                    if (reader!=null){
                        try {
                            reader.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (connection!=null){
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }
}

编写接口的具体实现:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView);

        HttpUtil.sendHttpRequest("https://www.baidu.com", new HttpCallbackListener04() {
            @Override
            public void onFinish(String content) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText(content);
                    }
                });
            }

            @Override
            public void onError(Exception e) {
                e.printStackTrace();
            }
        });
    }

然后如果使用OkHttp的方式会更加简单,就没有这么麻烦了,当然上面是为了弄懂原理。使用OkHttp的方式将不用手动开启线程,调用的是enqueue方法,这个方法会开启线程,然后会将结果回调到Callback对象上面,调用callback对象的两个方法,我们需要对其进行重写。

public class HttpUtil {
   
    //使用OkHttp的方式访问服务器
    //这里我们就不需要手动开启一个线程了 在enqueue方法中会帮我们开启 并且将结果回调给callback
    //的方法的参数中,我们在方法中获得结果                    
    public static void sendRequestWithOkHttp(String address, Callback callback){
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(address)
                .build();
        client.newCall(request).enqueue(callback);
    }
}

这里方法的参数就获得了联网的结果:

HttpUtil.sendRequestWithOkHttp("https://www.baidu.com", new 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 {
             final String content = response.body().string();
             runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     textView.setText(content);
                 }
              });
         }
     });

然后还需要注意了,两种方式回调接口方法都是在分支线程中允许的(因为都是在分支线程中调的),因此是不能进行ui操作的,想要进行ui操作还必须runOnuiThread()。所以还需有充分弄懂线程的知识,那就是java基础知识了。

联系我们

如果您对我们的服务有兴趣,请及时和我们联系!

服务热线:18288888888
座机:18288888888
传真:
邮箱:888888@qq.com
地址:郑州市文化路红专路93号