Android入门之在子线程中更新UI
16lz
2021-01-26
1. 前言
和很多其他的GUI库一样,Android的UI线程也是不安全的,也就是说,要想更新应用程序中的U元素,必须在主线程中进行,否则就会报错。一个应用程序启动之后,系统会默认的创建一个"main"的主线程,这个线程是应用于界面工具包(android.widgeth和android.view)交互的地方。在很多时候,进行开发工作的时候,会进行一些耗时的操作,如网络连接、数据库查询等,如果这些工作全部在主线程中进行,就有可能造成拥堵,导致界面进程就被阻塞,影响客户体验。耗时的任务需要在子线程,然后根据任务的结果来更新相应的UI控件。对于这种情况,Android提供了一套异步消息处理机制,完美地解决了在子线程中进行UI操作的问题。2. 方法总结
这是我个人使用过的方法,如果有什么缺漏或者补充,可以联系我指出 1) Activity.runOnUIThread(Runnable) 2) Handler 3) AsyncTask3. Activity.runOnUIThread(Runnable)
这一种方法是最简单的,只需要在子线程中需要更新子线程的地方直接调用就好,这会直接将子线程切回到主线程,由于不是新线程,所以尽量不在里面进行过多的耗时操作。下面是一个简单的例子 new Thread(new Runnable() { @Override public void run() { //耗时操作 runOnUiThread(new Runnable() { @Override public void run() { //进行UI操作 } }); } }).start();
4. Hander
Android中的异步消息处理主要由4个部分组成:Message、Handler、MessageQueue和Looper。 Message: 是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程中交换数据,what可以用来携带标记,arg1和arg2可以携带一些整型数据,使用onj字段可以携带一个Object队形对象 Hander: 主要用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法,而发送的消息经过一系列地辗转处理后,最终会传递到Handler的handlMessage()方法中 MessageQueue: 就是消息队列,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中直到i被处理。每个线程中只会有一个MessageQueue对象 Looper: Looper管理着每个线程中的MessageQueue,调用Looper的loop()方法后,会进入到一个无限循环中,不断取出MessageQueue中存在的消息,传递到Handler中的handleMessage()方法中进行处理。每个线程也只会有一个Looper 异步处理消息的基本过程为,首先在主线程中创建一个Handler对象,并重写handleMessage()方法。然后当子线程需要进行UI操作时,创建一个Message对象并通过Handler发送出去。之后这条消息会被添加到MessageQueue的队列中等待被处理,Looper会一直尝试从MessageQueue中取出待处理消息,分发到Handler的handleMessge()中进行处理。由于Handler对象是在主线程中进行,所以可以大胆地进行UI操作,下面是一个流程图Handler.sendMessage例子
private static final int SUCESS = 1; private static final int FAIL = 2; Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case SUCESS: //UI操作 break; case FAIL: //UI操作 break; default: break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(new Runnable() { @Override public void run() { //耗时操作 //发送消息 Message message = handler.obtainMessage(); message.what = 1; handler.sendMessage(message); } }).start(); }
Handler.postdelayed()方法可以用来实现一个循环操作,同样可以进行UI操作,下面是简单的示例代码
Handler handler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Runnable runnable = new Runnable() { @Override public void run() { //UI操作 //循环执行 handler.postDelayed(this, 2000); } }; handler.postDelayed(runnable, 2000); }
5. AsyncTask
AsyncTask是对异步消息处理机制很好的封装,使得即使对异步处理消息机制完全不了解的同学也可以简单地从子线程切换到主线程 AsyncTask是一个抽象类,需要创建一个子类去继承它,继承时可以为其指定3个泛型参数,Params、Progress、Result Params: 在执行AsyncTask时需要传入的参数,可用于在后台任务中使用 Progress: 在后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位 Resutl: 当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型 下面对方法进行简单的介绍 onPreExecute(): 这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作 doInBackground(Params..): 这个方法中的所有代码都会在子线程中进行,所有的耗时操作都应该在这里处理。一旦任务完成可以通过return语句将任务的执行结果返回。请注意,这个方法中是不能进行UI操作的,如果需要更新UI状态,可以调用publishProgress(Progress...)方法来完成 onProgressUpdate(Progress..): 当后台任务中调用了publishProgress(Progress...)方法后,onProgressUpdate(Progress..)方法会快就被调用了,该方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的操作 onPostExecute(Result): 但后台任务执行完毕并通过return语句进行返回时,这个方法很快就被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据进行一些UI操作,比如提醒任务执行的结果等。 下面是简单的示例代码public class ExampleTask extends AsyncTask{ @Override protected void onPreExecute() { //初始化UI super.onPreExecute(); } @Override protected Boolean doInBackground(Void... params) { //耗时操作,不能直接操作UI,可以通过publishProgress(Progress...)更新UI return null; } @Override protected void onPostExecute(Boolean aBoolean) { //处理doInBackground(Params...)更新请求 super.onPostExecute(aBoolean); } @Override protected void onProgressUpdate(Integer... values) { //根据doInBackground(Params...)处理结果更新UI super.onProgressUpdate(values); }}
6. 参考书籍
《 第一行代码 ——Android》 郭霖更多相关文章
- Android小項目之---時間線程應用(附源碼)
- android单元测试中的多线程以及handler消息传递
- 让JNI告诉你 你的应用为什么被卸载
- Android(安卓)面试题002 android的Handler机制
- Android:Service知识总结
- 《Android开发艺术探索》读书笔记----第二章:Android(安卓)IPC 简
- Android(安卓)App开发之ANR异常的原因分析及处理总结
- android MVP——mvp架构的应用和优化
- Android通过Okhttp3实现socket长连接