Android笔记之网络编程—初识使用HTTP协议访问网络

Do you want to spend the rest of your life selling sugared water or do you want a chance to change the world?
你想用卖糖水来度过余生,还是想要一个机会来改变世界?——乔布斯

小弟初学安卓,该文算是小弟的学习过程,课后笔记与一些自己的思考,希望在自己的自学路上留下印记,也许有一些自己想的不对的地方,希望各位前辈斧正。(心有愧疚,这篇笔记没有太多自己思考的地方,暂时把已经了解的先记下,未来深入研究)

一、使用HTTP协议访问网络获取数据


简单的来说就是我们向服务器发送一条HTTP请求,服务器接收请求后返回一些数据给我们,然后我们可以对这些数据处理或者解析。
  HTTP请求方式有八种:
   - OPTIONS
   - GET
   - HEAD
   - POST
   - PUT
   - DELETE
   - TRACE
   - CONNECT
  这些请求的具体作用可以通过搜索了解,小弟就不在这复制别人的了,对我们来说最常用的应该是"GET"和"POST"了,"GET"是向服务器请求数据,"POST"是向服务器提交数据。

Android要发送HTTP请求原本有两种,HttpURLConnection 和HttpClient,但是HttpClient已经在最新的几个API中弃用了,且HttpURLConnection 受到了Google的青睐。感兴趣的朋友可以搜索看看。

1.获取HttpURLConnection 的实例

URL url = new URL(http://www.baidu.com);//以百度为例
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

其中URL实例化对象会抛出MalformedURLException异常,而URL的openConnection()方法会抛出IOException异常,需要我们捕获。

2.连接前的设置
  首先是我们之前提到的HTTP请求方式,我们这里用"GET":

connection.setRequestMethod("GET");//该方法同样会抛出ProtocolException异常,该异常类是IOException的子类,指在底层协议中存在错误,如 TCP 错误。

注意,安卓中该方法参数不能使用"CONNECT"。且我们可以在HttpURLConnection类中看到一个字符串数组PERMITTED_USER_METHODS,里面是不含"CONNECT"的。

然后就是我们平时在上网时或是玩网络游戏时会出现的很可恶的东西,"网络连接超时!",这时候我们要不就是重新登录要不就是放弃抵抗,而实际上,超时超时,到底超过多久时间算超时呢?实际上这是可以由我们自己设置的,代码如下:

connection.setConnectTimeout(20000);//设置超时时间20秒
connection.setReadTimeout(20000);//还可以设置读取超时的时间20秒

最后设置完成就可以连接了:

connection.connect();//请求连接

另外我们可以在尝试连接后去获取返回码与返回信息,分别是HttpURLConnection的getResponseCode()和getResponseMessage()两个方法,返回值类型分别为int和String类型。

用Log打印出ResponseCode与ResponseMessage

  可以从上图看出,返回信息显示OK就是连接成功了,而返回码为200,在HttpURLConnection中的静态常量HTTP_OK值就是200,就是表示HTTP请求成功的意思,实际上还有很多返回码,像我们常用的404.

3.获取服务器返回的输入流
  我们之前的请求其实令我们获得了百度首页的HTML文本,它以输入流的形式传给了我们,而我们需要获得这个输入流只需要使用HttpURLConnection的getInputStream()方法就行了:

InputStream inputStream = connection.getInputStream();

当我们完成我们需要的操作时,应该断开HTTP连接:

connection.disconnect();

二、获取网站HTML文本小DEMO


主要功能,输入网站地址后点击按钮,在底下的文本框中显示HTML文本内容。

界面

注意,一定要在AndroidManifest.xml中申请网络权限:

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

主活动HttpActivity的XML布局 activity_http.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.qdf.test.HttpActivity">

    <EditText
        android:id="@+id/editText_uri"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:inputType="textUri"/>

    <Button
        android:id="@+id/btn_getData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/editText_uri"
        android:layout_centerHorizontal="true"
        android:text="获取数据"/>

    <ScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btn_getData"
        android:layout_centerHorizontal="true">

        <TextView
            android:id="@+id/textView_present"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="New Text"
            android:textSize="15sp"/>
    </ScrollView>
</RelativeLayout>


其中用来显示HTML文本的TextView放在ScrollView中,因为一个屏幕可能显示不完整个HTML文本。

主活动代码HttpActivit.class:

package com.qdf.test;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class HttpActivity extends AppCompatActivity {

    private EditText mUriEditText;
    private TextView mPresentTextView;
    private Button mGetDataButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_http);
        mUriEditText = (EditText) findViewById(R.id.editText_uri);
        mPresentTextView = (TextView) findViewById(R.id.textView_present);
        mGetDataButton = (Button) findViewById(R.id.btn_getData);
        mGetDataButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String url = getEditTextUrl(mUriEditText).trim();
                if (!url.startsWith("http://")) {
                    Toast.makeText(HttpActivity.this, "URL输入错误",Toast.LENGTH_SHORT).show();
                    return;
                }
                new RequestNetWorkDataTask().execute(url);
            }
        });
    }

    private String getEditTextUrl(EditText editText) {
        return editText != null ? editText.getText().toString() : "";
    }

    private String requestData(String strUrl) {
        HttpURLConnection connection = null;
        try {
            URL url = new URL(strUrl);
            //打开链接
            connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(20000);//设置超时时间
            connection.setRequestMethod("GET");//HTTP的请求方式,GET是发送一个请求来取得服务器上的某一资源。
            connection.connect();//请求连接
            int responseCode = connection.getResponseCode();
            String responseMessage = connection.getResponseMessage();
            Log.i("tag", "responseCode: " + responseCode + "  responseMessage: " + responseMessage);
            if (responseCode == HttpURLConnection.HTTP_OK) {
                //当连接成功时
                InputStream inputStream = connection.getInputStream();
                Reader reader = new InputStreamReader(inputStream, "UTF-8");
                char[] buffer = new char[1024];
                int len = 0;
                String result = null;
                while ((len = reader.read(buffer)) != -1) {
                    result = result + new String(buffer, 0, len);
                }
                reader.close();
                return result;
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
            Toast.makeText(HttpActivity.this, "非法的URL", Toast.LENGTH_SHORT).show();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
        return null;
    }

    class RequestNetWorkDataTask extends AsyncTask<String, Integer, String> {

        //在后台work之前
        @Override
        protected void onPreExecute() {
            //在主线程中
            //可加载数据,如:UI Loading
            super.onPreExecute();

        }

        @Override
        protected String doInBackground(String[] params) {
            return requestData(params[0]);
        }

        @Override
        protected void onPostExecute(String result) {
            //执行完之后在主线程中
            super.onPostExecute(result);
            mPresentTextView.setText(result);
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            //进度更新操作
            super.onProgressUpdate(values);
        }
    }
}



因为IO操作应该放在线程中,但因为我们要将获得的HTML文本输入到TextView上,这是UI操作不可以在线程中进行,我们这里利用了AsyncTask来解决,使用起来很方便,也可以使用Handler来处理。关于AsyncTask,推荐一篇博客:[详解Android中AsyncTask的使用]

稍微说一下requestData()这个方法,当我点击“获取数据”按钮后就会先将文本输入框中的字符串(url)获取,然后传入RequestNetWorkDataTask类的匿名对象,而requestData()方法将在RequestNetWorkDataTask的doInBackground()方法内执行,即开启线程并运行了requestData()方法,而为什么requestData()方法的参数是params[0]呢,首先这个params[0]就是我们之前传进来的url,而这个params在AsyncTask是个可变参数,它的数量是可变的,最后JAVA将其处理为数组,现在我们只传了一个字符串(url),那它自然是这个数组的第一位元素(params[0])。
  而当我们connection.connect()连接后,获取了回应码,我们通过它来判断是否连接成功,如果成功了才去获取输入流。最后我将输入流内的信息都存放在result这个字符串中,然后返回这个值。而这个值正是doInBackground的返回值,它被onPostExecute()方法接收,而此时已经回到了UI线程,我们再将其输出到TextView上。

最后的效果,说起来我也应该学习一下HTML呢。


效果

总结


其实我挺害怕学习网络编程的,因为我觉得网络这块领域真的好庞大,事实上确实自己懂得不是很多,所以先灌了自己一口鸡汤,未来继续拼搏吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容