Android学习随笔(15)------网络技术

学习流程来自《第一行代码》(第二版)
现在的Android手机基本上都是能够上网的,这样利用网络我们能开发出越来越多有意思的应用。

WebView

有时候我们需要在应用中显示一个网页,但是我们不可能自己去编写一个浏览器,Android提供了一个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"
    android:orientation="vertical">

    <WebView
        android:id="@+id/web_view"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        />

</LinearLayout>

直接在布局中引入控件。

申请网络权限 :

    <uses-permission android:name="android.permission.INTERNET" />
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);    // 设置浏览器属性 支持JavaScript
        webView.setWebViewClient(new WebViewClient());    // 当需要从一个网页跳转到另一个网页时仍在当前WebView中显示
        webView.loadUrl("http://baidu.com");    // 要加载的Url
    }
}

运行应用后,可以看到直接打开了百度的网页。

Exler

HTTP协议

客户端向服务器发送一条HTTP请求,服务器收到请求之后会返回数据给客户端,客户端再对数据进行解析处理就可以了。

HttpURLConnection

用于发送或接收数据。
1. 利用URL的openConnection()方法来获取实例。
2. 设置Http请求所使用的方法。GET表示希望从服务器上获取数据。POST表示提交数据给服务器。
3. 还可以进行一些其他设置。
4. 调用getInputStream()方法,获取到服务器返回的输入流。
5. 对输入流进行读取。
6. disconnect()方法关闭HTTP连接。

<?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"
    android:orientation="vertical">

    <Button
        android:id="@+id/send_request"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="Send request" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        <TextView
            android:id="@+id/response_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />    <!--显示服务器返回的数据-->
    </ScrollView>

</LinearLayout>

利用在TextView外面嵌套一个ScrollView来滚动显示,屏幕显示不全的信息。

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

        final Button sendRequest = (Button) findViewById(R.id.send_request);
        responseText = (TextView) findViewById(R.id.response_text);

        sendRequest.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.send_request:
                sendRequestWithHttpURLConnection();    // Use HttpURLConnection
                break;
            default:
                break;
        }
    }

    private void sendRequestWithHttpURLConnection() {    // 开启线程来发起网络请求
        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection connection = null;
                BufferedReader reader = null;
                try {
                    URL url = new URL("https://www.baidu.com");
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET");    // 设置发送GET(默认)请求
                    connection.setConnectTimeout(8000);    // 设置连接超时
                    connection.setReadTimeout(8000);    // 读取超时
                    // 获取服务器返回的状态码  connection.getResponseCode();    200说明请求成功    
                    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 {
                    if (reader != null) {
                        try {
                            reader.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (connection != null) {
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }

    private void showResponse(final String response) {    // 显示
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                responseText.setText(response);    // 进行UI操作
            }
        });
    }

一些耗时操作会导致 手机提醒ANR(Application Not Responding)所以访问网络要放在子线程中进行。
子线程中更新UI会导致android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

因为百度全站改用了https的协议,所以用http会提示302,在浏览器上输入http://baidu.com会进行一个跳转,百度自动给你跳转到https://www.baidu.com上,这个跳转由一个302页面来进行,动作很快,基本察觉不到。这就是为什么得到的是302而不是200的原因。

Exler
这里收到的就是未经解析的HTML语言。

提交数据,例 :

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

OkHttp

OkHttp是一个比较好的开源库
在dependencies闭包下添加 :

implementation 'com.squareup.okhttp3:okhttp:3.9.1'
  1. 创建一个OkHttpClient实例
  2. 创建一个request对象
  3. 调用OkHttpClient的newCall()方法创建一个Call对象,并调用它的execute()方法发送请求并获取服务器返回的数据。

发送POST,需要先构建RequestBody对象来存放待提交的参数

RequestBody requestBody = new FormBody.Bulider()
    .add("username", "admin")
    .add("passward", "123456")
    .build();

在request中调用

Request request = new Request.Builder()
    .url("https://www.baidu.com")
    .post(requestBody)
    .build();
@Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.send_request:
                // sendRequestWithHttpURLConnection();    // Use HttpURLConnection
                sendRequestWithOKHttp();    // Use OKHttp
                break;
            default:
                break;
        }
    }

    private void sendRequestWithOKHttp() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    OkHttpClient client = new OkHttpClient();
                    Request request = new Request.Builder()
                            .url("https://baidu.com")    // 获取百度的html信息
                            .build();
                    Response response = client.newCall(request).execute();
                    String responseData = response.body().string();
                    showResponse(responseData);    // 在textview中展示
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

解析数据

从Apache的官网上下载一个Tomcat,在tomcat目录下的tomcat-8.5.23\webapps\ROOT文件夹中新建一个
get_data.xml文件 :

<apps>
    <app>
        <id>1</id>
        <name>Google Map</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>

电脑本机访的网址是http://127.0.0.1:8080/get_data.xml

Exler

XML

xml文件写入到手机
        FileOutputStream fos = null;
        try {
            XmlSerializer serializer = Xml.newSerializer();    // 获取XmlSerializer的实例
            File file = new File(Environment.getExternalStorageDirectory().getPath(), "serializer.xml");
            fos = new FileOutputStream(file);
            serializer.setOutput(fos, "utf-8");    // 设置Xml序列化器的参数
            serializer.startDocument("utf-8", true);    // 开始写xml文档开头 xml文件的编码 是否是一独立的xml文件
            serializer.startTag(null, "apps");    // 写xml的根节点,namespace 命名空间

            for (App app : AppList) {    // 循环写app节点
                serializer.startTag(null, "app");
                serializer.startTag(null, "id");
                serializer.text(app.getId());
                serializer.endTag(null, "id");
                serializer.startTag(null, "name");
                serializer.text(app.getName());
                serializer.endTag(null, "name");
                serializer.startTag(null, "version");
                serializer.text(app.getVersion());
                serializer.endTag(null, "version");
                serializer.endTag(null, "app");
            }
            serializer.endTag(null, "apps");
            serializer.endDocument();    // 写文档结尾
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

对于XML解析有两个种方式Pull SAX

Pull

一行一行解析
基于事件解析

    private void sendRequestWithOKHttp() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    OkHttpClient client = new OkHttpClient();
                    Request request = new Request.Builder()
                            .url("http://10.0.2.2:8080/get_data.xml")    // 访问PC本地tomcat上的get_data.xml
                            .build();
                    Response response = client.newCall(request).execute();
                    String responseData = response.body().string();
                    parseXMLWithPull(responseData);    // 利用Pull方式解析url中的xml
                    parseJSONWithGson(responseData);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private void parseXMLWithPull(String xmlData) {
        try {
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();    // 获取实例
            XmlPullParser xmlPullParser = factory.newPullParser();    // Xml.newPullParser();
            xmlPullParser.setInput(new StringReader(xmlData));    // 将服务器返回的XML数据设置进去 或(in, "utf-8")服务器返回的数据流
            int eventType = xmlPullParser.getEventType();    // 得到当前的解析事件
            String id = "";
            String name = "";
            String version = "";
            while (eventType != XmlPullParser.END_DOCUMENT) {    // 循环解析
                String nodeName = xmlPullParser.getName();    // 得到当前节点的名字
                switch (eventType) {    // 开始解析某个节点
                    case XmlPullParser.START_TAG: {
                        if ("id".equals(nodeName))
                            id = xmlPullParser.nextText();    // 获取节点内的具体内容 开始标签中有id属性xmlPullParser.getAttributeValue(0)
                        else if ("name".equals(nodeName))
                            name = xmlPullParser.nextText();
                        else if ("version".equals(nodeName))
                            version = xmlPullParser.nextText();
                        break;
                    }
                    case XmlPullParser.END_TAG: {
                        if ("app".equals(nodeName)) {
                            Log.d("admin", "id is " + id);
                            Log.d("admin", "name is " + name);
                            Log.d("admin", "version is " + version);
                        }
                        break;
                    }
                    default:
                        break;
                }
                eventType = xmlPullParser.next();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Exler

SAX

新建ContentHandler类继承自DefaultHandler :

public class ContentHandler extends DefaultHandler {

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

    @Override
    public void startDocument() throws SAXException {    // 开始解析时调用
        id = new StringBuilder();
        name = new StringBuilder();
        version = new StringBuilder();
    }

    @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 {    // 解析出的数据会以参数的形式传入 此方法可能会被调用多次 一些换行符也被当作内容解析 需要控制
        if ("id".equals(nodeName)) {    // 根据当前节点名 判断将内容添加到哪个StringBuilder对象中
            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 is "+ id.toString().trim());
            Log.d("ContentHandler", "name is " + name.toString().trim());
            Log.d("ContentHandler", "version is " + version.toString().trim());
            id.setLength(0);    // 将StringBuilder清空
            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();
            ContentHandler handler = new ContentHandler();
            reader.setContentHandler(handler);    // 将ContentHandler实例设置到XMLReader中
            reader.parse(new InputSource(new StringReader(xmlData)));    // 开始执行解析
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

JSON

在tomcat中新建
get_data.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"}]
使用JSONObject
    private void parseJSONWithJSONObject(String jsonData) {
        try {
            JSONArray jsonArray = new JSONArray(jsonData);    // 数据传入
            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("admin", "id is " + id);
                Log.d("admin", "name is " + name);
                Log.d("admin", "version is " + version);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
使用GSON
compile '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;
    }

}

正好对应三个字段。

调用 :

    private void parseJSONWithGson(String jsonData) {
        Gson gson = new Gson();
        List<App> appList = gson.fromJson(jsonData, new TypeToken<List<App>>(){}.getType());    // 借助TypeToken将期望解析成的数据类型传入到fromJson()方法中
        for (App app : appList) {
            Log.d("admin", "id is " + app.getId());
            Log.d("admin", "name is " + app.getName());
            Log.d("admin", "version is " + app.getVersion());
        }
    }

此博文为个人学习笔记,仅供个人学习使用,希望对大家有帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值