文章目录

  • 1.WebView的用法
  • 2.使用HTTP协议访问网络
  • 2.1使用HttpURLConnection
  • 2.2使用OkHttp
  • 3.解析XML格式数据
  • 3.1Pull解析方式
  • 3.2SAX解析方式
  • 4.解析JSON数据
  • 4.1使用JSONObiect
  • 4.2使用GSON
  • 5.网络编程的最佳实践
  • 5.1HttpURLConnection
  • 5.2OkHttp


1.WebView的用法

有时候我么可能会遇到一些比较特殊的需求,比如说要在应用程序里展示一些网页,但又明确指出不允许打开系统的浏览器。这个时候我们就可以利用WebView控件,帮助我们在自己的应用程序中嵌入一个浏览器,从而轻易的展示各种各样的网页。

使用步骤:

1.在布局文件中添加WebView控件。
2.在活动中获得WebView实例。
3.让WebView支持js脚本。
4.让网页在WebView中打开。
5.传入网址

示例:

//步骤一
<?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>

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.setWebViewClient(new WebViewClient());//步骤四
        webView.loadUrl("https://www.baidu.com");//步骤五
    }
}

这里还有一点要注意的,打开网页属于网络操作,所以我们需要在AndroidManifest.xml文件中申请权限。

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

2.使用HTTP协议访问网络

HTTP协议,它的工作原理就是客户端向服务器发出一条HTTP请求,服务器收到请求之后会返回一些数据给客户端,然后客户端再对这些数据进行解析和处理就可以了。

Android上发送HTTP请求一般有两种方式:HttpURLConnection和OkHttp。接下来我们就来分别学习一下这两种方式。

2.1使用HttpURLConnection

使用步骤:

1.获取URL实例,并在器构造参数中传入网址。
2.获取HttpURLConnection实例。
3.给HttpURLConnection设置请求模式"GET"或"POST"。
4.给HttpURLConnection进行自由定制。
5.用IO操作来解析获得的数据。

示例:

//布局文件
<?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:text="Button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

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

</LinearLayout>
//活动中的代码
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final Button button=(Button) findViewById(R.id.button);
        final TextView textView=(TextView) findViewById(R.id.text);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {//所有的网络操作或耗时操作都需在子线程中进行
                    @Override
                    public void run() {
                        BufferedReader reader = null;
                        HttpURLConnection connection = null;
                        try {
                            URL url=new URL("https://www.baidu.com");//步骤一
                            connection= (HttpURLConnection) url.openConnection();//步骤二
                            connection.setRequestMethod("GET");//步骤三
                            connection.setReadTimeout(8000);//步骤四  这里设置了读取超时的毫秒数
                            connection.setConnectTimeout(8000);//这里设置了链接超时的毫秒数
                            InputStream in=connection.getInputStream();//步骤五
                            reader=new BufferedReader(new InputStreamReader(in));
                            final StringBuilder content=new StringBuilder();
                            String line;
                            while((line=reader.readLine())!=null){
                                content.append(line);
                            }
                            MainActivity.this.runOnUiThread(new Runnable() {//不能在子线程中更改UI,故采取该措施
                                @Override
                                public void run() {
                                    textView.setText(content);
                                }
                            });
                        } catch (Exception e) {
                            e.printStackTrace();
                        } finally {
                            if(reader!=null){
                                try {
                                    reader.close();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                            if(connection!=null){
                                connection.disconnect();
                            }
                        }
                    }
                }).start();
            }
        });
    }
}

若是想要提交数据给服务器,只需要将HTTP请求的方法改为POST,并在获取输入流之前把要提交的数据写出即可。注意每条数据都要以键值对的形式存在,数据与数据之间用"&"符合隔开。

示例:

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

2.2使用OkHttp

OkHttp是一个著名的开源项目,它相较于HttpURLConnection来说更加的简单易用。项目主页

使用步骤:

1.添加依赖。
2.获取OkHttpClient实例。
3.获得Request实例,采用连缀的方式为其添加url。
4.获得Response实例,服务器所返回的数据就存储在response中。

示例:

//步骤一
dependencies {
    implementation 'com.squareup.okhttp3:okhttp:4.8.1'
}
//
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final Button button=(Button) findViewById(R.id.button);
        final TextView textView=(TextView) findViewById(R.id.text);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            OkHttpClient client=new OkHttpClient();//步骤二
                            Request request= new Request.Builder()//步骤三
                                    .url("https://www.baidu.com")
                                    .build();
                            Response response=client.newCall(request).execute();//步骤四
                            final String content=response.body().string();
                            MainActivity.this.runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    textView.setText(content);
                                }
                            });
                        } catch (Exception e) {
                            e.printStackTrace();
                        } 
                    }
                }).start();
            }
        });
    }
}

如果你想要提交数据给服务器,就要先构建一个RequestBody实例来存放待提交的数据,然后在Request.Builder中调用post()方法,并将RequestBody实例传入。

示例:

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格式数据

在网络上传输数据时最常用的格式有两种:XML和JSON,接下来我们就来逐一进行学习。

解析XML格式数据的方式一般有两种:Pull解析和SAX解析。

我们要解析的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.1Pull解析方式

使用步骤:

1.获取XmlPullParserFactory实例x。
2.获取XmlPullParser实例。
3.给xmlPullParser实例添加待解析的数据。
4.开始解析。

示例:

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="";
            String name="";
            String version="";
            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.e("MainActivity",id );
                            Log.e("MainActivity",name );
                            Log.e("MainActivity",version );
                        }
                        break;
                    }
                    default:
                        break;
                }
                eventType=parser.next();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3.2SAX解析方式

使用步骤:

1.新建一个类继承DefaultHandler类,并重写其中的5大方法。
2.获取SAXParserFactory实例。
3.获取XMLReader实例。
4.获取新建类的实例。
5.给XMLReader设置Handler。
6.给XMLReader传入待解析的数据。

示例:

//步骤一
public class MyHandler extends DefaultHandler {

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

    @Override
    public void startDocument() throws SAXException {//开始解析时
        super.startDocument();
        id=new StringBuilder();
        name=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);
        //记录当前节点名
        nodeName=localName;
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {//获取节点中内容时
        super.characters(ch, start, length);
        //根据当前节点名判断将内容添加到哪一个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 {//完成解析某个节点时
        super.endElement(uri, localName, qName);
        if("app".equals(nodeName)){
            Log.e("MyHandler",id.toString().trim());
            Log.e("MyHandler",name.toString().trim());
            Log.e("MyHandler",version.toString().trim());
        }
        //最后要将StringBuilder清空掉,否则会影响下一次内容的读取
        id.setLength(0);
        name.setLength(0);
        version.setLength(0);
    }

    @Override
    public void endDocument() throws SAXException {//完成解析时
        super.endDocument();
    }
}
//活动中代码
private void parseXMLWithSAX(String xmlData){
    try {
        SAXParserFactory factory=SAXParserFactory.newInstance();//步骤二
        XMLReader reader=factory.newSAXParser().getXMLReader();//步骤三
        MyHandler myHandler=new MyHandler();//步骤四
        reader.setDTDHandler(myHandler);//步骤五
        reader.parse(new InputSource(new StringReader(xmlData)));//步骤六
    }catch (Exception e){
        e.printStackTrace();
    }
}

4.解析JSON数据

接下来我们来学习如何解析JSON格式的数据。比起XML,JSON的主要优势在于它的体积更小,在网络上传输的时候可以更省流量。但缺点在于,它的语义性较差,看起来不如XML直观。

解析JSON格式数据的方式一般有两种:JSONObject解析和GSON解析。

我们要解析的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使用JSONObiect

使用步骤:

1.将待处理的数据传入到一个JSONArray对象中。
2.遍历JSON数组,获取数据。

示例:

private void parseJSONWithJSONObject(String jsonData){
        try {
            JSONArray array=new JSONArray(jsonData);//步骤一
            for(int i=0;i<array.length();i++){//步骤二
                JSONObject object=array.getJSONObject(i);
                String id=object.getString("id");
                String name=object.getString("name");
                String version=object.getString("version");
                Log.e("MainActivity",id);
                Log.e("MainActivity",name);
                Log.e("MainActivity",version);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

4.2使用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> list=gson.fromJson(jsonData, new TypeToken<List<Person>>(){}.getType());

使用步骤总结:

1.首先添加依赖。
2.创建一个数据类。
3.获取Gson实例。
4.将待解析的数据解析成数据类的实例。

示例:

//步骤一
dependencies {
    implementation 'com.google.code.gson:gson:2.8.6'
}
//步骤二
public class Person {
    String id;
    String name;
    String version;

    public String getId() {
        return id;
    }

    public String getVersion() {
        return version;
    }

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

    public String getName() {
        return name;
    }

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

    public void setId(String id) {
        this.id = id;
    }
}
//步骤三,四
private void parseJSONWithGSON(String jsonData){
    Gson gson=new Gson();
    List<App> list=gson.fromJson(jsonData,new TypeToken<List<App>>(){}.getType());
    for(App app:list){
        Log.e("MainActivity", app.getId());
        Log.e("MainActivity", app.getName());
         Log.e("MainActivity", app.getVersion());
    }
}

5.网络编程的最佳实践

这里涉及到两个问题:首先是网络连接的代码比较长,所以可以把它封装在一个类里面,然后设置一个静态的方法,每次要进行网络连接的时候调用它就可以了;还有一个问题是由于网络连接需要开启子线程,然而子线程又不能返回数据,所以需要设置回调函数。

5.1HttpURLConnection

首先这里先建立一个回调接口:

public interface HttpCallbackListener {
    
    void onFinish(String response);//当服务器成功响应我们请求时调用
    
    void onError(Exception e);//当网络操作错误时调用
    
}

然后定义HttpUtil,作为网络连接的通用类:

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.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);
                    }
                    if(listener!=null){
                        listener.onFinish(content.toString());
                    }
                }catch (Exception e){
                    e.printStackTrace();
                    if(listener!=null){
                        listener.onError(e);
                    }
                }finally {
                    if(reader!=null){
                        try {
                            reader.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if(connection!=null){
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }
}

然后我们在MainActivity里调用它:

public void onClick(View view) {
    HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {
        @Override
         public void onFinish(String response) {
             //在这里根据返回内容执行具体逻辑
         }

         @Override
         public void onError(Exception e) {
             //在这里对异常情况进行处理
         }
    });
}

5.2OkHttp

事实上OkHttp已经帮我们实现好了开辟子线程的方法,以及回调接口的定义。

示例:

//HttUtil中
public class HttpUtil {
    public static void sendOkHttpRequest(String address,okhttp3.Callback callback){//这里的okhttp3.Callback就是OkHttp帮我们定义好的回调接口
        OkHttpClient client=new OkHttpClient();
        Request request=new Request.Builder()
                .url(address)
                .build();
        client.newCall(request).enqueue(callback);//enqueue会自动帮我们开辟线程
    }
}
//MainActivity中调用它
public void onClick(View view) {
    HttpUtil.sendOkHttpRequest(address, new okhttp3.Callback() {
        @Override
        public void onFailure(@NotNull Call call, @NotNull IOException e) {
            //在这里对异常情况进行处理
        }

        @Override
        public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
            //得到服务器返回的具体内容
            String responseData=response.body().string();
        }
    });
}

注意:无论是HttpURLConnection还是OkHttp,最终回调接口中的代码依然是在子线程中进行的,所以不能更改UI。