Android多线程:Handler runOnUiThread 异步消息处理机制

目录

一,Android中的多线程问题

1.模拟耗时工作

2.Android开启子线程 

 二,在子线程中更新UI

1.异步消息处理机制 Handler

2.使用runOnUiThread更新UI


一,Android中的多线程问题

        Android用户界面是与用户交互的接口,对于用户的操作,Android迅速响应用户输入(200ms内)是一个重要目标。因此,一些耗时操作(如:后台下载,异步加载图片等)需要放在子线程中运行,否则会导致主线程阻塞。

1.模拟耗时工作

        例如下面这段访问百度界面的代码,如果在主线程中运行的话就会出现android.os.Network-OnMainThreadException的报错,也就是在主线程中请求了网络操作,这是一种耗时操作。为了解决这个问题,就需要把操作放在子线程中运行。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    findViews();

    setListeners();
}
private void setListeners() {
    btn_baidu.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
                try {
                    //获取百度链接
                    URL url = new URL("https://www.baidu.com/");
                    //获取输入流
                    InputStream inputStream = url.openStream();
                    byte[] bytes = new byte[1024];
                    //存储输入的信息
                    StringBuffer buffer = new StringBuffer();
                    while((inputStream.read(bytes)) != -1){
                        String str = new String(bytes, 0, bytes.length);
                        buffer.append(str);
                    }
                    Log.i("baidu", buffer.toString());
                    //关闭流
                    inputStream.close();
                } catch (MalformedURLException e) {
                    throw new RuntimeException(e);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
        }
    });
}

2.Android开启子线程 

         在Android中开启线程的操作与在Java中一致,继承Thread类或实现Runnable接口,不了解的话可以阅读博客:Java线程基础:Thread Runnable 多线程 Synchronized 死锁...-CSDN博客

例如下面用实现Runnable接口的方法来开启子线程,访问百度:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    findViews();

    setListeners();
}
private void setListeners() {
    btn_baidu.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //获取百度链接
                        URL url = new URL("https://www.baidu.com/");
                        //获取输入流
                        InputStream inputStream = url.openStream();
                        byte[] bytes = new byte[1024];
                        //存储输入的信息
                        StringBuffer buffer = new StringBuffer();
                        while((inputStream.read(bytes)) != -1){
                            String str = new String(bytes, 0, bytes.length);
                            buffer.append(str);
                        }
                        //在子线程中更改Ui界面,使用runOnUiThread
                        Log.i("baidu", buffer.toString());
                        //关闭流
                        inputStream.close();
                    } catch (MalformedURLException e) {
                        throw new RuntimeException(e);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }).start();
        }
    });
}

运行并查看日志,可以发现成功访问: 

 二,在子线程中更新UI

        使用子线程解决异步执行又会带来新问题,那就是在Android中,只有UI线程(也叫主线程)可以更新UI界面,子线程不能更新。为了在子线程中更新UI,我们需要使用Android异步消息处理机制

1.异步消息处理机制 Handler

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

  1. Message:在线程之间传递的消息,Message中可以封装一些数据如:what(int型,表示Message的编号),obj(封装的Object对象),此外还有int型的arg1,arg2等;
  2. Handler:用于在线程间发送和处理消息,发送消息使用sendMessage()方法,处理消息使用handleMessage()方法;
  3. MessageQueue:消息队列,用于存放Handler发送的消息,这些消息直到被处理前,会一直存放在消息队列中。每个线程只会有一个MessageQueue对象;
  4. Looper:Looper是每个线程中MessageQueue的管家,调用Looper的loop方法后,会进入无限循环,每当发现MessageQueue中存在一条消息,就会将其取出,并传递到Handler的handleMessage()方法中,每个线程只会有一个Looper对象; 

 异步消息处理机制的基本流程为:

(1)首先在主线程中创建一个Handler对象,并重写handleMessage方法。

(2)当子线程需要更改UI时,就创建一个Message对象,并通过Handler将Message发送出去,Message消息会被添加到MessageQueue中等待处理,Looper会一直尝试从消息队列中取出消息,并传给Handler的handleMessage方法。

(3)Handler的构造器中我们传入了Looper.getMainLooper,所以handleMessage方法中的代码会在UI线程中运行,我们就可以放心地进行UI操作。

下面是代码实例(获取网络图片):

private void getImg() {
    //1.在主线程中创建一个Handler对象,并重写handleMessage方法。
    Handler handler = new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what){
                case 114514:
                    Bitmap bitmap = (Bitmap) msg.obj;
                    iv_img.setImageBitmap(bitmap);
                    Log.i("114514", "获取图片成功!");
                    break;
            }
        }
    };
    //设置监听
    btn_getimg.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //2.当子线程需要更改UI时,就创建一个Message对象
                        URL url = new URL("https://profile-avatar.csdnimg.cn/8e4c56733fdd4dda90854384976d4bb0_ih_lzh.jpg!1");
                        InputStream inputStream = url.openStream();
                        Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                        Message msg = handler.obtainMessage();
                        //封装bitmap对象和设置对象编号
                        msg.obj = bitmap;
                        msg.what = 114514;
                        //3.通过Handler将Message发送出去
                        handler.sendMessage(msg);
                        inputStream.close();
                    } catch (MalformedURLException e) {
                        throw new RuntimeException(e);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }).start();
        }
    });
}

2.使用runOnUiThread更新UI

        runOnUiThread,在UI线程上运行指定的操作。如果当前线程是UI线程,则执行操作,如果当前线程不是UI线程,操作将被提交到UI线程的消息队列MessageQueue中。runOnUiThread只能在Activity中使用。

public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);//提交到消息队列
        } else {
            action.run();//操作执行
        }
    }

还是上面获取图片的例子,将Handler改为使用runOnUiThread更改UI:

private void getImg() {
    //设置监听
    btn_getimg.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        URL url = new URL("https://profile-avatar.csdnimg.cn/8e4c56733fdd4dda90854384976d4bb0_ih_lzh.jpg!1");
                        InputStream inputStream = url.openStream();
                        Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                iv_img.setImageBitmap(bitmap);
                            }
                        });
                    } catch (MalformedURLException e) {
                        throw new RuntimeException(e);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }).start();
        }
    });
}
  • 37
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Android消息处理机是指Android系统中用于处理异步任务的一种机。它基于事件驱动的方式,通过消息队列和线程来实现异步任务的处理。 在Android中,消息处理机主要包括Handler、Message和Looper三个核心组件: 1. HandlerHandler消息处理的核心,它用于发送和处理消息。每个Handler对象都关联一个特定的线程,并且在该线程中处理消息。通过Handler,我们可以发送消息消息队列,或者处理消息队列中的消息。 2. Message:Message是消息的载体,用于在不同线程之间传递信息。每个Message对象包含了一个标识符(what)、一个可选的整数参数(arg1和arg2)、一个可选的对象参数(obj)以及一个可选的Handler对象(target)。通过这些属性,我们可以传递需要处理的信息和相关的数据。 3. Looper:Looper是一个消息循环器,它用于不断地从消息队列中取出消息,并将其分发给对应的Handler进行处理。每个线程只能有一个Looper对象,它通过轮询机实现不断地从消息队列中取出消息。 当我们需要在某个线程中处理异步任务时,可以创建一个Handler对象,并将其关联到该线程的Looper。然后,我们可以通过Handler发送消息消息队列,并在对应的Handler中处理这些消息。这样就可以实现在不同线程之间进行通信和任务处理。 总的来说,Android消息处理机提供了一种简单而有效的方式来处理异步任务,使得我们能够更好地进行多线程编程和实现UI更新等操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值