zoukankan      html  css  js  c++  java
  • Android异步消息处理机制(多线程)

      当我们需要执行一些耗时操作,比如说发起一条网络请求时,考虑到网速等其他原因,服务器未必会立刻响应我们的请求,如果不将这类操作放在子线程里去执行,就会导致主线程被阻塞住,从而影响用户对软件的正常使用。

      

      线程的基本用法

      Android多线程编程其实并不比Java多线程编程特殊,基本都是使用相同的语法。比如说,定义一个线程只需要新建一个类继承自Thread,然后重写父类的run()方法,并在里面编写耗时逻辑即可。

    class MyThread extends Thread {
        @Override
        public void run(){
            //处理具体的逻辑
        }
    } 

      启动线程只需要调用它的start()方法,这样run()方法中的代码就会在子线程当中运行了。

    new MyThread().start();

      使用继承的方式耦合性有点高,更多的时候我们都会选择使用实现Runnable接口的方式来定义一个线程。

    class MyThread implements Runnable {
        @Override
        public void run(){
            //处理具体的逻辑
        }
    }

      使用这种写法,启动线程的方法也需要进行相应的改变。

    MyThread myThread = new MyThread();
    new Thread(myThread).start();

      也可以使用匿名类的方式。

    new Thread(new Runnable(){
        @Override
        public void run(){
            //处理具体的逻辑
        }
    }).start();

      在子线程中更新UI

      和许多其他的GUI库一样,Android的UI也是线程不安全的。也就是说,如果想要更新应用程序里的UI,则必须在主线程中进行,否则就会出现异常。

      新建一个AndroidThreadTest项目,修改activity_main,xml中的代码。

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <Button
            android:id="@+id/change_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Change Text"/>
    
        <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            android:layout_centerInParent="true"
            android:textSize="20sp"/>
    
    </RelativeLayout>

      布局文件中定义了两个控件,TextView用于在屏幕的正中央显示一个Hello world字符串,Button用于改变TextView中显示的内容,希望的结果是点击Button后可以把TextView中显示的字符串改成Nice to meet you。

      修改MainActivity的代码

    public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    
        private TextView text;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            text = (TextView) findViewById(R.id.text);
            Button changeText = (Button) findViewById(R.id.change_text);
            changeText.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                case R.id.change_text:
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            text.setText("Nice to meet you");
                        }
                    }).start();
                    break;
                default:
                    break;
            }
        }
    }

      在Change Text按钮的点击事件里面开启了一个子线程,然后在子线程中调用TextView的setText()方法将显示的字符串改成Nice to meet you。运行程序,点击Change Text按钮,程序就会崩溃。

      

      由此可见,Android是不允许在子线程中进行UI操作的。但有些时候,我们需要在子线程里去执行一些耗时任务,然后根据任务的执行结果来更新相应的UI控件。对于这种情况,Android提供了一套异步消息处理机制,完美地解决了在子线程中进行UI操作的问题。

    public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    
        public static final int UPDATE_TEXT = 1;
        
        private TextView text;
        
        private Handler handler = new Handler(){
            public void handleMessage(Message msg){
                switch (msg.what){
                    case UPDATE_TEXT:
                        text.setText("Nice to meet you");
                        break;
                    default:
                        break;
                }
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            text = (TextView) findViewById(R.id.text);
            Button changeText = (Button) findViewById(R.id.change_text);
            changeText.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                case R.id.change_text:
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            Message message = new Message();
                            message.what = UPDATE_TEXT;
                            handler.sendMessage(message);
                        }
                    }).start();
                    break;
                default:
                    break;
            }
        }
    }

      这里定义了一个整型常量UPDATE_TEXT,用于表示更新TextView这个动作。然后新增一个Handler对象,并重写父类的handleMessage()方法,在这里对具体的Message进行处理。如果发现Message的what字段的值等于UPDATE_TEXT,就将TextView显示的内容改成Nice to meet you。

      再看一下点击事件中的代码,这次没有在子线程里直接进行UI操作,而是创建了一个Message(android.os.Message)对象,并将它的what字段的值指定为UPDATE_TEXT,然后调用Handler的sendMessage()方法将这条Message发送出去。Handler会收到这条Message,并在handleMessage()方法中对它进行处理。此时handleMessage()方法中的代码就是在主线程当中运行的,所以可以在这里进行UI操作。接下来对Message携带的what字段的值进行判断,如果等于UPDATE_TEXT,就将TextView显示的内容改成Nice to meet you。重新运行程序,点击按钮,Hello World就会被替换成Nice to meet you。

      

      这就是Android异步处理的基本用法,使用这种机制可以出色地解决掉在子线程中更新UI的问题。下面来分析一下Android异步消息处理机制的工作原理。

      解析异步消息处理机制

      Android中的异步消息处理主要由4个部分组成:Message、Handler、MessageQueue和Looper。

        ♦ Message:线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。上面我们用到了Message的what字段,除此之外还可以使用arg1和arg2字段来携带一些整型数据,使用obj字段携带一个Object对象。

        ♦ Handler:处理者,它主要是用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法,而发出的消息经过一系列地辗转处理后,最终会传递到Handler的handleMessage()方法中。

        ♦ MessagQueue:消息队列,主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。

        ♦ Looper:每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中也只会有一个Looper对象。

      了解了Message、Handler、MessageQueue和Looper的基本概念后,再把异步消息处理的整个流程梳理一遍,首先需要在主线程当中创建一个Handler对象,并重写handleMessage()方法。然后当子线程需要进行UI操作时,就创建一个Message对象,并通过Handler将这条消息发送出去。之后这条消息会被添加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理的消息,最后分发回Handler的handleMessage()方法中。由于Handler是在主线程中创建的,所以此时handleMessage()方法中的代码也会在主线程中运行,于是我们在这里就可以安心地进行UI操作了。整个异步消息处理机制的流程示意图如图:

      

      一条Message经过这样一个流程的辗转调用后,也就从子线程进入到主线程,从不能更新UI变成了可以更新UI,整个异步消息处理的核心思想就是如此。

  • 相关阅读:
    LoadRunner脚本转化成Jmeter脚本
    LoadRunner场景运行错误解决方法
    IntelliJ IDEA lombok插件的安装和使用
    js瀑布流加载数据
    js获取浏览器高度和宽度值(多浏览器 图解经典) 转载
    MPMoviePlayerViewController和MPMoviePlayerController
    NSString的内存管理问题
    cocos2D icon
    Singleton 单例模式
    Cocos2d中各种坐标位置关系
  • 原文地址:https://www.cnblogs.com/weilan/p/7388015.html
Copyright © 2011-2022 走看看