11月10日Android学习笔记

标签

Android

读取网络数据

Thread

Handler

缓存

发布时间:

本文字数:1,190 字 阅读完需:约 2 分钟

读取网络数据

获取网络请求属于耗时操作。在Android主线程当中不允许出现耗时操作,开启异步线程,解决获取网络请求的问题。
获取网络请求必须在子线程当中完成,UI控件的设置必须在主线程当中完成。

线程知识点回顾

开启新线程

  1. 创建Thread子类对象,调用start方法,启动新线程
  2. 编写Runnable实现类,创建Runnable子类的对象,new Thread(new MyRunnable).start()

创建新线程对象

    //创建新的线程对象
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            
        }
    });
    //创建线程对象的方法2
    Thread t2 = new Thread(){
        @Override
        public void run() {
            super.run();
        }
    };
    //启动线程
    thread.start();

线程的生命周期

线程的生命周期: 新建----->>就绪----->>运行<==>阻塞------>>消亡

当调用t1.start()方法时,让线程处于就绪状态,有cpu的执行资格,但是没有执行权力。

原生读取网络数据

配置网络访问权限

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:usesCleartextTraffic="true"
···

读取流

    private String getStringByConnection(String path) {
        //内存流
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            //1. 将网址转换成资源对象
            URL url = new URL(path);
            //2. 开始连接网络
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            //3. 获取网页信息输入流
            InputStream is = conn.getInputStream();
            //4. 读取流,写入内存
            int hasRead = 0;
            byte[] buf = new byte[1024];
            while ((hasRead = is.read(buf))!=-1){
                baos.write(buf, 0, hasRead);
            }
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return baos.toString();
    }

线程中开启读取方法

    private void loadDatas() {
        //创建新的线程对象
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                //编写获取网络数据的过程
                String msg = getStringByConnection(url);
                Log.i("lsh", "run: msg====" + msg);
            }
        });
        t1.start();

    }

UI的更新(线程间通信)

只有UI线程才能更新控件内容,在子线程当中更新UI会出现bug
但是从子线程获取网络数据,然后通过线程间通信,可以将数据传递给主线程,然后在主线程更新UI控件

步骤

  1. 创建一个线程间通信类,该类可以在子线程当中发送消息,然后在主线程中接收消息。
    发送消息和接收消息得是同一个handler对象

        Handler handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(@NonNull Message msg) {
                //可以接收消息的方法
                return false;
            }
        });
    
  2. 在子线程发送消息

         @Override
         public void run() {
             //表示让当前线程可以作为loop线程,此时该子线程可以创建Handler对象,发送消息
             Looper.myLooper();
             Looper.loop();
             //编写获取网络数据的过程
             String msg = HttpUtils.getStringByConnection(url);
             //将要发送的数据封装到message当中
             Message message = handler.obtainMessage();
             //区别消息的标识号
             message.what = 1;
             message.obj = msg;
             handler.sendMessage(message);
             Log.i("lsh", "run: msg====" + msg);
         }
    
  3. 在主线程接收消息

            @Override
            public boolean handleMessage(@NonNull Message message) {
                //可以接收消息的方法
                if (message.what == 1) {
                    String str = (String) message.obj;
                    binding.mainTv.setText(str);
                }
                return false;
            }
    
  4. 回到主线程执行任务
    在子线程的run方法中执行runOnIoThread()即可

    //回到主线程执行任务
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            adapter.notifyDataSetChanged();
        }
    });
    
    Handler handler2 = new Handler();
    new Thread(){
        @Override
        public void run() {
            String json = "ksdfjalkjf";
            handler2.post(new Runnable() {
                @Override
                public void run() {
                    //回到主线程执行
                }
            });
        }
    }.start();
    

Handler,Looper,Message,MessageQueue之间的关系

答:从应用的角度举例,可以把地铁当中的安检机看作是一个线程,然后安检机中的传送带就就当中Looper对象,因为有这个传送带,安检机才能无限循环进行安检,接受安检的人就相当于handler对象,在传送带上放置包裹即 Message,然后在传送带到达指定地点后,提示给放置包裹的人接受包裹。

从源码的角度讲,线程当中可以通过Looper.prepare()方法,在线程当中定义looper对象,使线程能够循环。然后通过调用Looper.loop()的方法使他循环起来。然后Looper上有一个封装的MessageQueue对象,用来处理循环消息,调用handler的sendMessage方法,把message消息的target属性设置为当前handler,然后在调用messagequeue当中enqueueMessage方法把消息放置在消息队列上,然后调用handlerdispatchMessage分发这条消息,放置这条消息的handler能够获取到此消息, 通过handleMessage方法。

视图绑定

不用写findViewById

流程

  1. build.gradle中添加viewBinding

     android {
     namespace 'com.xyl.app1110'
     compileSdk 32
     viewBinding {
         enabled true
     }
     ...
     }
    
  2. 每一个view上设置id

  3. 在Activity类中声明ActivityMainBinding对象,如

    ActivityMainBinding binding;
    
  4. onCreate()方法中获取绑定对象

    binding = ActivityMainBinding.inflate(getLayoutInflater());

    binding = ItemFoodlvBingding.bind(view)
    ---在adapter的用于getView()ViewHolder中使用

      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        View rootView = binding.getRoot();
        setContentView(rootView);
        binding.mainTv.setText("今天是个好天气");
    }
    

三级缓存

三级缓存:
从网络上获取图片或者复杂的数据,可以先从内存当中查找,是否有这个图片,有就显示,没有就查找本地存储文件,如果本地存储有这个图片,就读入内存,然后显示,如果没有这个图片,就上网下载这个图片,下载成功,存放到本地存储,存放到内存,显示图片。
如果下载失败,显示错误图片。