美文网首页
Android网络编程

Android网络编程

作者: Anwfly | 来源:发表于2020-10-06 23:15 被阅读0次

一、后台与APP 交互过程分析

1 、后台与APP 交互过程分析

基于http/https协议的app前后台交互包含以下几个步骤:

1.建立连接:前台后台建立连接。
2.发送请求:建立连接后,app向后台发送请求。
3.发送响应:后台处理完请求后,要向app发送响应消息。
比如:android代表客户端开发,php代表服务端开发。json在其中是一种通用语言的角色。中国人(客户端)和日    本人(服务器)就用英语(json)交流。
4.断开连接:以上交互完成后可以断开连接了。
img02.jpg

二、Tomcat基本使用

1、 Tomcat简介★★★★

Tomcat服务器由Apache提供,开源免费。由于Sun和其他公司参与到了Tomcat的开发中,所以最新JSP/Servlet规范总是能在Tomcat中体现出来。当前最新版本是Tomcat10,我们课程中使用Tomcat7。Tomcat7支持Servlet3.0,而Tomcat6只支持Servlet2.5!
访问:http://localhost:8080
​ localhost是指访问自己电脑上的tomcat服务器
格式:http://ip地址:8080,Tomcat默认的端口号是8080,可以修改,打开Tomcat安装路径,%CATALANA_HOME%\conf\server.xml文件:

img02.png

2、 Tomcat部署json★★★★

Tomacat下载安装:

​ 下载Tomcat可以到http://tomcat.apache.org下载。
Tomcat分为安装版和解压版:

1.安装版:一台电脑上只能安装一个Tomcat;
2.解压版:无需安装,解压即可用,解压多少份都可以,所以我们选择解压版。

在启动Tomcat之前,我们必须要配置环境变量:

1.JAVA_HOME:必须先配置JAVA_HOME,因为Tomcat启动需要使用JDK;
2.CATALANA_HOME:如果是安装版,那么还需要配置这个变量,这个变量用来指定Tomcat的安装路径,例如:F:\apache-tomcat-7.0.42。
3.启动:进入%CATALANA_HOME%\bin目录,找到startup.bat,双击即可;(linux系统,双击startup.sh)
4.关闭:进入%CATALANA_HOME%\bin目录,找到shutdown.bat,双击即可;注意:我们必须在启动Tomcat之前把JAVA_HOME配置正确。

启动问题:点击startup.bat后窗口一闪即消失:检查JAVA_HOME环境变量配置是否正确;
启动成功后再浏览器中访问http://localhost:8080/,如果能看到如下图所示界面,说明Tomcat启动成功。

img04.jpg

Tomcat部署json:
可以在Tomcat安装目录下找到webapps目录,新建一个文件夹,将资源放入文件夹,例如新建res目录,放入class.json资源,访问路径:http://192.168.1.103:8080/res/class.json(192.168.1.103为ip,res为资源目录路径),或者:http://localhost:8080/res/class.json

三、HttpURLConnection

1、 HttpURLConnection 简介

  • ​客户端向网络请求数据,需要借助URLConnection。任何网络连接都需要经过socket才能连接,URLConnection 不需要设置socket,所以URLConnection并不是底层的连接,而是在底层连接上的一个请求。这就是为什么URLConneciton只是一个抽象类,自身不能被实例化的原因。URLConnection只能通过url.openConnection()方法创建具体的实例。而HttpURLConnection是URLConnection的子类,加入了http规范相关的操作。

  • ​虽然底层的网络连接可以被多个HttpURLConnection实例共享,但每一个HttpURLConnection实例只能发送一个请求。请求结束之后,应该调用HttpURLConnection实例的InputStream或OutputStream的close()方法以释放请求的网络资源,不过这种方式对于持久化连接没用。对于持久化连接,得用disconnect()方法关闭底层连接的socket。

  • HttpURLConnection是一个抽象类,其对象不能通过构造器直接产生,应用中通常通过URL对象调用openConnection()方法获取。http协议中最常用两种请求方式:get请求和post请求。

区别:

1.get请求没有请求体,post请求有请求体
2.get请求请求参数拼接到url地址上,post请求请求参数通过请求体发送给服务器
3.get请求的网页可以被收藏,post请求不能被收藏
4.get请求的url地址限制长度为2048个字符,post请求请求体长度没有限制

2、 HttpURLConnection 获取服务器数据★★★★★

  1. Get请求
1.首先创建URL对象,注意如果有请求参数应该拼接到这个对象中
  URL url = new URL("http://localhost/day07/AutoLoginServlet?name=zs");
2.然后获取HttpURLConnection对象,注意需要强转。
  HttpURLConnection conn = (HttpURLConnection) url.openConnection();
3.设置相关参数
  a)conn.setConnectTimeout(10);//请求超时时间10秒
  b)conn.setUseCaches(false);// 设置是否缓存,不缓存
  c)conn.setRequestMethod("GET");// 可以不写,默认get请求
  d)conn.setDoOutput(true);// 有请求体就要设置为true,默认为false
  e)conn.setDoInput(true);// 有响应体就设置为true,默认为true
  f)conn.connect();//连接服务器,这个方法调用与否都不影响
  g)conn.getResponseCode();//获取响应码,该语句会自动执行连接服务器的动作
4.获取响应数据

案例代码:

private void initData() {
  new Thread(new Runnable() {
    @Override
    public void run() {
      try {
        //网址不能写这个,http://localhost:8080/res/class.json,必须使用带ip的地址
        //1.创建URL对象
        URL url = new URL("http://192.168.1.103:8080/res/class.json");
        //2.然后获取HttpURLConnection对象
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        //3.获取响应码
        int responseCode = conn.getResponseCode();
        //4.判断响应码,成功的响应码为200
        if (responseCode == HttpURLConnection.HTTP_OK){
          //获取响应码描述信息,可以不写
          String msg = conn.getResponseMessage();
          //获取响应头信息,可以不写
          String field=conn.getHeaderField("content-type");
          Log.d("tag", "msg: "+msg+",field:"+field);

          //5.获取输入流
          InputStream is = conn.getInputStream();
          //6.创建输出流
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          byte[] buf = new byte[1024];
          int len = -1;
          while ((len = is.read(buf)) != -1) {
            //7.将输入流数据读取出来写入输出流
            baos.write(buf, 0, len);
          }
          //8.打印数据
          Log.d("tag", "结果:"+baos.toString());

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

*注意:

1.清单文件添加网络权限
2.如果模拟器或者真机试Android9.0以上的系统,需要在清单文件中配置支持http网络请求(因为Android9.0开始系统不支持http请求,只支持加密的https请求,如果需要支持http请求,需要做配置)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.xts.day09">
    <!--网络权限-->
    <uses-permission android:name="android.permission.INTERNET"/>

    <!--android:usesCleartextTraffic="true",android9.0支持http请求配置-->
    <application
        android:usesCleartextTraffic="true"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
  1. Post请求
    相比get请求,post请求也并无太多不同之处。不同之处主要有,post请求有请求体,必须通过请求体发送请求参数,不能将请求参数拼接到url中。

这里我们拿一个真实的接口讲解案例

搜索接口:
https://www.wanandroid.com/article/query/0/json
请求方式:post
参数:k:“android”;  //关键字

案例代码:

private void query() {
  new Thread(new Runnable() {
    @Override
    public void run() {
      URL url = null;
      try {
        //1.创建URL对象
        url = new URL("https://www.wanandroid.com/article/query/0/json");
        //2.然后获取HttpURLConnection对象
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        //3.设置请求方式为post,下面两句代码都要有
        conn.setRequestMethod("POST");
        //4.有请求体就要设置为true,默认为false
        conn.setDoOutput(true);
        //有响应体就设置为true,默认为true
        //conn.setDoInput(true);
        //5.通过请求体发送请求数据
        OutputStream os = conn.getOutputStream();
        os.write(("k=android").getBytes());
        // 如有中文需要url编码
        // String userInfo = "user=" + URLEncoder.encode("zs", "utf-8");
        // os.write(userInfo.getBytes());
        os.flush();
        //6.接收响应信息
        int code = conn.getResponseCode();
        if (code == 200) {
          //7.读取响应信息
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          InputStream is = conn.getInputStream();

          byte[] buf = new byte[1024];
          int len = -1;
          while ((len = is.read(buf)) != -1) {
            baos.write(buf, 0, len);
          }
          Log.d("tag", baos.toString());

          //8.关闭资源
          os.close();
          is.close();
        }
        //conn.disconnect();//这个方法调用与否都不影响,彻底关闭网络连接
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }).start();
}

3、 解析从服务器获取的jsonObject★★★★★

4、 解析从服务器获取的jsonArray★★★★★

​ 返回的数据如果是以“{}”括起来的,那么我们需要使用JSONObject解析,如果是以“[]”括起来的,则需要使用JSONArray,以get请求为例,我们将返回的数据进行解析,服务器返回的json数据如下:

{"error":false,"results":[{"_id":"5b3ed2d5421aa91cfe803e35","createdAt":"2018-07-06T10:24:21.907Z","desc":"2018-07-06","publishedAt":"2018-07-06T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fszxi9lmmzj30f00jdadv.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b3d883f421aa906e5b3c6f1","createdAt":"2018-07-05T10:53:51.361Z","desc":"2018-07-05","publishedAt":"2018-07-05T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fsysqszneoj30hi0pvqb7.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b3ae394421aa906e7db029b","createdAt":"2018-07-03T10:46:44.112Z","desc":"2018-07-03","publishedAt":"2018-07-03T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fswhaqvnobj30sg14hka0.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b398cf8421aa95570db5491","createdAt":"2018-07-02T10:24:56.546Z","desc":"2018-07-02","publishedAt":"2018-07-02T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fsvb1xduvaj30u013175p.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b33ccf2421aa95570db5478","createdAt":"2018-06-28T01:44:18.488Z","desc":"2018-06-28","publishedAt":"2018-06-28T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fsq9iq8ttrj30k80q9wi4.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b32807e421aa95570db5471","createdAt":"2018-06-27T02:05:50.227Z","desc":"2018-06-27","publishedAt":"2018-06-27T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fsp4iok6o4j30j60optbl.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b31aa33421aa9556d2cc4a7","createdAt":"2018-06-26T10:51:31.60Z","desc":"2018-06-26","publishedAt":"2018-06-26T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fsoe3k2gkkj30g50niwla.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b2f8847421aa9556b44c666","createdAt":"2018-06-24T20:02:15.413Z","desc":"2018-06-24","publishedAt":"2018-06-25T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fsmis4zbe7j30sg16fq9o.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b0d6ac0421aa97efda86560","createdAt":"2018-05-29T22:59:12.622Z","desc":"2018-06-02","publishedAt":"2018-06-22T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1frslruxdr1j30j60ok79c.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b27c7aa421aa923c0fbfda0","createdAt":"2018-06-18T22:54:34.199Z","desc":"2018-06-19","publishedAt":"2018-06-21T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fsfq1k9cb5j30sg0y7q61.jpg","used":true,"who":"lijinshanmx"}]}

分析:

1.最外层的是{}括起来的,所以返回的数据需要放入JSONObject解析
2.results对应的value是以[]括起来的,所以对应的value需要放入JSONArray解析
3.results对应的value集合中,每一个都是使用{}括起来的,所以需要JSONObject去解析

编写实体类Bean,具体代码这里就不贴了,以下是Java原生的解析方式:

//服务器返回的数据
String json = baos.toString();
//创建实体类对象
Bean bean = new Bean();
ArrayList<Bean.ResultsBean> list = new ArrayList<>();
//将list设置到bean中
bean.setResults(list);
//JsonObject解析数据
JSONObject jsonObject = new JSONObject(json);
//获取到值为boolean类型,key为error的value,
boolean error = jsonObject.optBoolean("error");
bean.setError(error);
//通过格式化json串,我们看到key为results的value是个集合
JSONArray results = jsonObject.optJSONArray("results");
//通过for循环将JSONArray中的数据分别取出
for (int i = 0; i < results.length(); i++) {
    //JSONArray中的每一个元素都是JSONObject
    JSONObject item = (JSONObject) results.get(i);

    //获取JSONObject中的每一个key对应的value
    String _id = item.optString("_id");
    String createdAt = item.optString("createdAt");
    String desc = item.optString("desc");
    String publishedAt = item.optString("publishedAt");
    String source = item.optString("source");
    String type = item.optString("type");
    String _url = item.optString("url");
    boolean used = item.optBoolean("used");
    String who = item.optString("who");

    //创建一个Bean.ResultsBean,将解析出的数据设置到这个对象中
    Bean.ResultsBean resultsBean = new Bean.ResultsBean();
    resultsBean.set_id(_id);
    resultsBean.setCreatedAt(createdAt);
    resultsBean.setDesc(desc);
    resultsBean.setPublishedAt(publishedAt);
    resultsBean.setSource(source);
    resultsBean.setType(type);
    resultsBean.setUrl(_url);
    resultsBean.setUsed(used);
    resultsBean.setWho(who);
    //将resultsBean 添加到集合中
    list.add(resultsBean);
}

//打印bean
Log.d("tag", "list: "+bean.toString());

四、Gson解析json

1、Gson解析jsonObject★★★★★

2、 Gson解析jsonArray★★★★★

​ 通过上面的案例我们发现,如果自己使用原生的解析,写起来比较费劲,需要判断是jsonObject还是jsonArray,然后再去解析,还需要通过key去解析出对应的value,然后设置到实体类对象中。接下来我们介绍一个工具类Gson,它可以帮助我们解析Json串,省去很多原生解析的麻烦,使用如下:

1. 添加依赖:

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

2. 使用Gson对象将json串解析成实体类:

//服务器返回的json串
String json = baos.toString();
//创建Gson对象
Gson gson = new Gson();
//解析,第一个参数:json字符串,第二个参数:实体类对应的class对象
Bean data = gson.fromJson(json, Bean.class);
Log.d("tag", "data: "+data.toString());

五、 Handler

Handler是Android中的消息机制,主要用来做线程之间通信的。在Android中,为了保障线程安全,规定只能由主线程来更新UI信息。而在实际开发中,会经常遇到多个子线程都去操作UI信息的情况,那么就会导致UI线程不安全。这时,我们就需要借助 Handler 作为媒介,让 Handler 通知主线程按顺序一个个去更新UI,避免UI线程不安全。

1、 子线程给主线程发送数据★★

2、使用TextView显示获取的json★★★

1.在Activity中创建Handler对象

//1.Handler在哪个线程中创建,最终handleMessage处理消息就在哪个线程
//这里是属于Acivity,是主线程
Handler mHandler = new Handler(){
    @Override
    public void handleMessage(@NonNull Message msg) {
        //处理消息的回调方法,这里是主线程
        //msg:这个msg就是子线程发送过来的对象
        int what = msg.what;
        if (what == 0){
          //注意,子线程message对象携带的是什么对象,这里拿到的就是什么对象
          //我们子线程传递的是Bean对象,这里可以强转成Bean
          Bean bean = (Bean) msg.obj;
          //将数据设置到TextView上
          mTv.setText(bean.toString());
        }
    }
};

2.在获取网络数据的子线程中,通过Handler将携带数据的Message对象发送到主线程

//子线程不能刷新ui,需要将子线程中获取的网络数据发送到主线程
//2.创建/获取Message对象
//一般不直接new,会额外消耗内存,
// Message message = new Message();
//可以通过Message.obtain()获取Message对象,这个对象是Message消息池中已有的对象
Message message = Message.obtain();
//将数据通过message携带过去
message.obj = bean;
//可以设置一个what,区分是哪个线程发送的数据
message.what = 0;
//通过handler将message发送到主线程
mHandler.sendMessage(message);

相关文章

网友评论

      本文标题:Android网络编程

      本文链接:https://www.haomeiwen.com/subject/jpuxpktx.html