11月10日Android学习笔记
发布时间:
本文字数:1,190 字 阅读完需:约 2 分钟
读取网络数据
获取网络请求属于耗时操作。在Android主线程当中不允许出现耗时操作,开启异步线程,解决获取网络请求的问题。
获取网络请求必须在子线程当中完成,UI控件的设置必须在主线程当中完成。
线程知识点回顾
开启新线程
- 创建Thread子类对象,调用start方法,启动新线程
- 编写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控件
步骤
-
创建一个线程间通信类,该类可以在子线程当中发送消息,然后在主线程中接收消息。
发送消息和接收消息得是同一个handler对象Handler handler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(@NonNull Message msg) { //可以接收消息的方法 return false; } });
-
在子线程发送消息
@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); }
-
在主线程接收消息
@Override public boolean handleMessage(@NonNull Message message) { //可以接收消息的方法 if (message.what == 1) { String str = (String) message.obj; binding.mainTv.setText(str); } return false; }
-
回到主线程执行任务
在子线程的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
方法把消息放置在消息队列上,然后调用handler
的dispatchMessage
分发这条消息,放置这条消息的handler能够获取到此消息, 通过handleMessage方法。
视图绑定
不用写findViewById
流程
-
build.gradle
中添加viewBinding
android { namespace 'com.xyl.app1110' compileSdk 32 viewBinding { enabled true } ... }
-
每一个view上设置id
-
在Activity类中声明
ActivityMainBinding
对象,如ActivityMainBinding binding;
-
在
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("今天是个好天气"); }
三级缓存
三级缓存:
从网络上获取图片或者复杂的数据,可以先从内存当中查找,是否有这个图片,有就显示,没有就查找本地存储文件,如果本地存储有这个图片,就读入内存,然后显示,如果没有这个图片,就上网下载这个图片,下载成功,存放到本地存储,存放到内存,显示图片。
如果下载失败,显示错误图片。