『Android基础入门』网络请求数据与JSON解析

454 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

👨‍🎓作者简介:一位喜欢写作,计科专业的大二菜鸟

🏡个人主页:starry陆离

🥳个人主页:starry陆离

🕒首发日期:2022年6月29日星期三

🌌上期文章:『Android基础入门』复杂数据的存储SQLite和Room框架

📚订阅专栏:『Android基础入门』 如果文章有帮到你的话记得点赞👍+收藏💗支持一下哦


注意:不是教程只是笔记,如有错误欢迎批评指正

🍁网络请求权限

既然需要访问网络自然需要网络请求权限

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

除此之外还需要加上这一行,否则http的网页请求不成功

 android:usesCleartextTraffic="true"

image-20220429173308248

🌳网络请求

网络在主线程异常

这段代码是网络请求数据,http://121.4.44.56/user这是老师提前发在网上的数据文件,可通过这个网址访问,其中的数据是:

image-20220429174240291

     try {
             URL url = new URL("http://121.4.44.56/user");
             HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
             urlConnection.setRequestMethod("GET");
 ​
             
             InputStream inputStream = urlConnection.getInputStream();// 字节流
             Reader reader = new InputStreamReader(inputStream);//字符流
             BufferedReader bufferedReader = new BufferedReader(reader);// 缓存流
             String result = "";
             String temp;
             while ((temp = bufferedReader.readLine()) != null) {
                 result += temp;
             }
             Log.i("MainActivity", result);
             inputStream.close();
             reader.close();
             bufferedReader.close();
         } catch (Exception e) {
             e.printStackTrace();
         }

将这段代码放在主线程下执行会出现警告warn

 W/System.err: android.os.NetworkOnMainThreadException

网络在主线程异常-》网络请求不能在主线程

生命周期中执行的方法默认是主线程(UI线程)不能执行耗时的操作

耗时操作-》such as:

读取文件(读数据库)、网络请求

子线程网络请求

因此我们需要创建一个子线程来访问网络数据,并且在子线程中将获取到的数据显示在TextView控件上

 Thread thread=new Thread(){
 @Override
 public void run() {
     //run()方法执行的代码都是在子线程中
     super.run();
 ​
     //网络请求
     try {
         //请求服务器端
         URL url = new URL("http://121.4.44.56/user");
         HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
         urlConnection.setRequestMethod("GET");
 ​
         //获取io输入字节流->字符流->缓存流
         InputStream inputStream = urlConnection.getInputStream();// 字节流
         Reader reader = new InputStreamReader(inputStream);//字符流
         BufferedReader bufferedReader = new BufferedReader(reader);// 缓存流
 ​
         String result = "";
         String temp;
         while ((temp = bufferedReader.readLine()) != null) {
             result += temp;
         }
         Log.i("MainActivity", result);
         //在子线程中将获取到的数据显示在`TextView`控件上
         textView.setText(result);
         inputStream.close();
         reader.close();
         bufferedReader.close();
         } catch (Exception e) {
             e.printStackTrace();
         }
 ​
     }
 ​
 };
 thread.start();

运行发现虚拟机上只显示一部分数据,查看日志窗口发现:

image-20220429143453487

线程切换

出现警告:在错误的线程中执行

 W/System.err: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

原因: 对于UI控件的操作不能放在子线程中->需要放到主线程中执行->线程切换

image-20220429143614697

不能直接在主线程中设置,因为主线程和子线程是一起运行的,它们在运行时是无法人为预知的

在子线程中通过runOnUiThread(new Runnable()实现线程切换

 String finalResult=result;
 runOnUiThread(new Runnable() {
     @Override
     public void run() {
     textView.setText(finalResult);
     }
 });

image-20220429144035127

🌺JSON数据解析

JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

问题:网络请求是耗时的,那么如何一次请求到所有的数据呢?

如图一次请求到年龄,姓名和是否是学生三个数据,但是我们只需要姓名,我们就只需要通过JSON解析字符串,再通过 键名 获取到姓名

基本类型

json数据结构的特点:"key":value

 //json结构 "key":value { "age":30,"name":"张三", "isstudent":true }
 //如何从json结构中解析出数据
  try {
      //json解析字符串
      JSONObject jsonObject=new JSONObject(finalResult);
      //通过 键 获取到 值
      String name=jsonObject.getString("name");
      textView.setText(name);
      } catch (JSONException e) {
         e.printStackTrace();
      }

json字符串的数据结构可以是浮点型,整型,长整型,字符串型,布尔型 ,数组类型,josn类型,class类型等等

image-20220429185825405

json类型

json数据结构还可以是json类型的,这种情况需要嵌套解析,http://121.4.44.56/object1文件里的内容如下:

 { "age":20,"name":"张三", "isstudent":true,"class":{"grade":"18级","classname":"计算机科学与技术"} }
 //json解析字符串
 JSONObject jsonObject=new JSONObject(finalResult);
 //首先获取到嵌套的json类型,这也可以看做通过 键 找 值
 //class是键名,{"grade":"18级","classname":"计算机科学与技术"}是值
 JSONObject classObj=jsonObject.getJSONObject("class");
 //再通过 键 获取到 值
 String className=classObj.getString("classname");

image-20220429151433501

数组类型

json数据结构还可以数数组类型,http://121.4.44.56/object2文件里的内容如下:

 { "grade":"18级","classname":"计算机科学与技术","students":["张三","李四","王五"] }
 JSONObject jsonObject=new JSONObject(finalResult);
 //通过数组名获取到数组
 JSONArray jsonArray= jsonObject.getJSONArray("students");
 //遍历数组,打印日志
 for(int i=0;i<jsonArray.length();++i){
      String student=jsonArray.getString(i);//根据下标获取
      Log.i("Students","student = "+student);
 }

image-20220429152218420

json数组类型

再复杂一丢丢,json数组类型,http://121.4.44.56/object3文件里的内容如下:

 { "grade":"20级","classname":"计算机科学与技术",
 "students":[
 { "id":"001","age":30,"name":"张三", "isstudent":false },
 { "id":"002","age":25,"name":"李四111", "isstudent":true },
 { "id":"003","age":26,"name":"王1111五", "isstudent":true }
 ]}
 //通过数组名获取到数组
 JSONArray jsonArray= jsonObject.getJSONArray("students");
 //遍历数组,打印日志
  for(int i=0;i<jsonArray.length();++i){
     //根据下标获取每一个json数据
     JSONObject studentobj=jsonArray.getJSONObject(i);
     //通键名获取到每一个json数据下的值
     String name=studentobj.getString("name");
     Integer age=studentobj.getInt("age");
     //打印日志
     Log.i("MainActivity","name = "+name+" age = "+age);
   }

image-20220429153414112

学习要求:会解析json数据结构,会构json造数据结构

例如京东列表:

 {"list":[{"name":iphone12,"price":6799},{"name":iphone12 plus,"price":7988},{},{}]}

🌷第三方框架 gson

google/gson:一个Java序列化/反序列化库,用于将Java Objects转换为JSON并返回 (github.com)

优点

  1. 直接将字符串解析成对象
  2. 解析成为一个实体类

添加依赖

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

创建一个实体类

实体类要求字段的名称和类型严格一致

 { "age":30,"name":"张三", "isstudent":true }

image-20220429154517665

使用GSON框架解析

普通json数据类型类

 Gson gson=new Gson();
 Student student=gson.fromJson(finalResult,Student.class);
 Log.i("MainActivity", "run: "+student.name);

image-20220429155333562

嵌套json类型实体类

如果是json类型嵌套实体类,那么实体类应该做如下修改:

 { "age":20,"name":"张三", "isstudent":true,"class":{"grade":"18级","classname":"计算机科学与技术"} }

json数据类型对应java的类,所以嵌套的json结构,改写成实体类时也是嵌套的类(内部类),除此之外还要声明这个内部类,因为这里键名是class与java的关键字冲突,所以要注解"class",然后换个变量名

image-20220429155602202

image-20220429155816612

MainActivity的子线程中解析数据并打印

image-20220429160051343

json数组实体类

通常使用java集合类中的List可变数组定义json数据结构中的数组

image-20220429161020411

image-20220429160753936

实体类插件GsonFormatPlus

GSON框架虽然简化了我们解析数据的过程,但是需要书写一个实体类又给我们添加了麻烦,可以通过如下插件一键生成json数据结构的实体类

安装

image-20220429195001727

打开

image-20220429195050700

设置

image-20220429161429544

使用

在左侧粘贴json数据结构的字符串,然后ok即可

image-20220429195204798

MainActivity完整代码

 ​
 ​
 public class MainActivity extends AppCompatActivity {
 ​
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
 ​
         TextView textView=findViewById(R.id.textView);
 ​
         /*网络在主线程异常-》网络请求不能在主线程
         生命周期中执行的方法默认时主线程(UI线程)不能执行耗时的操作
         耗时操作-》such as:
         读取文件(读数据库)、网络请求*/
 ​
         Log.i("MainActivity", "test1 ");
         Thread thread=new Thread(){
             @Override
             public void run() {
                 //run()方法执行的代码都是在子线程中
                 super.run();
                 //网络请求
                 try {
                     //请求服务器端
                     URL url = new URL("http://121.4.44.56/user");
                     HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
                     urlConnection.setRequestMethod("GET");
 ​
                     //获取io输入字节流->字符流->缓存流
                     InputStream inputStream = urlConnection.getInputStream();// 字节流
                     Reader reader = new InputStreamReader(inputStream);//字符流
                     BufferedReader bufferedReader = new BufferedReader(reader);// 缓存流
                     //
                     String result = "";
                     String temp;
                     while ((temp = bufferedReader.readLine()) != null) {
                         result += temp;
                     }
                     Log.i("MainActivity", result);
                     Log.i("MainActivity", "test3 ");
                     //在子线程中设置文本
                     /*W/System.err: android.view.ViewRootImpl$CalledFromWrongThreadException:
                     Only the original thread that created a view hierarchy can touch its views.
                     对于UI控件的操作不能放在子线程中->需要放到主线程中执行->线程切换*/
                     //textView.setText(result);
                     //切换到主线程
                     String finalResult=result;
                     runOnUiThread(new Runnable() {
                         @Override
                         public void run() {
                             Gson gson=new Gson();
                             Student student=gson.fromJson(finalResult,Student.class);
                             //Log.i("MainActivity", "run: "+student.class1.classname);
                             Log.i("MainActivity", "run: "+student.students.get(0).name+" "+
                                     student.students.get(0).id);
 ​
                             // user
                             // json结构 本质性是一个字符串 ”key“:value { "age":30,"name":"张三", "isstudent":true }
                             //如何从json结构中解析出数据
 ​
                             /*object1
                             嵌套解析->json类型
                             { "age":20,"name":"张三", "isstudent":true,"class":{"grade":"18级","classname":"计算机科学与技术"} }
                             * */
 ​
                             /*object2
                             * { "grade":"18级","classname":"计算机科学与技术","students":["张三","李四","王五"] }
                             * 数组类型,josn类型,浮点型,整型,字符串型,布尔型 等等
                             * */
 ​
                             /*object3
                             *  { "grade":"18级","classname":"计算机科学与技术",
                             * "students":[
                             * { "id":"001","age":30,"name":"张三", "isstudent":false },
                             * { "id":"002","age":25,"name":"李四", "isstudent":true },
                             * { "id":"003","age":26,"name":"王五", "isstudent":true }
                             * ]}
                             * json数组*/
                             try {
                                 JSONObject jsonObject=new JSONObject(finalResult);
                                 String name=jsonObject.getString("name");
                                 textView.setText(name);
                                 /*JSONObject classObj=jsonObject.getJSONObject("class");
                                 String className=classObj.getString("classname");
 //                                String name=jsonObject.getString("name");
 //                                textView.setText(name);
                                 textView.setText(className);*/
 ​
                                 /*JSONArray jsonArray= jsonObject.getJSONArray("students");
                                 for(int i=0;i<jsonArray.length();++i){
                                     String student=jsonArray.getString(i);//根据下标获取
                                     Log.i("Students","student = "+student);
                                 }*/
 ​
                                 /*JSONArray jsonArray= jsonObject.getJSONArray("students");
                                 for(int i=0;i<jsonArray.length();++i){
                                     JSONObject studentobj=jsonArray.getJSONObject(i);//根据下标获取
                                     String name=studentobj.getString("name");
                                     Integer age=studentobj.getInt("age");
                                     Log.i("MainActivity","name = "+name+" age = "+age);
                                 }
 */
                             } catch (JSONException e) {
                                 e.printStackTrace();
                             }
                             //textView.setText(finalResult);
                         }
                     });
                     inputStream.close();
                     reader.close();
                     bufferedReader.close();
                 } catch (Exception e) {
                     e.printStackTrace();
                 }
             }
 ​
         };
         thread.start();
         Log.i("MainActivity", "test2 ");
     }
 }

Student类完整代码

 ​
 //{"age":30,"name":"张三", "isstudent":true}
 /*
 * 字段的名称和类型要求严格一致
 * */
 ​
 /*object1
 { "age":20,"name":"张三", "isstudent":true,"class":{"grade":"18级","classname":"计算机科学与技术"} }
  */
 ​
 /*object3
 { "grade":"20级","classname":"计算机科学与技术","students":
 [
 { "id":"001","age":30,"name":"张三", "isstudent":false },
 { "id":"002","age":25,"name":"李四111", "isstudent":true },
 { "id":"003","age":26,"name":"王1111五", "isstudent":true }
 ]}
  */
 /*
 public class Student {
     public int age;
     public String name;
     public boolean isstudent;
 ​
     //设置注解,解决关键字重复
     @SerializedName("class")
     public MyClass class1;
 ​
     public class MyClass{
         public String grade;
         public String classname;
     }
 ​
 }
 */
 ​
 public class Student{
     public String grade;
     public String classname;
 ​
     /*public MyStudent[] students;*/
     public List<MyStudent> students;
 ​
     public class MyStudent{
         public String id;
         public int age;
         public String name;
         public boolean isstudent;
     }
 }
 ​