欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

了解Android核心:Looper,Handler和HandlerThread

程序员文章站 2022-06-10 10:38:07
...

了解Android核心:Looper,Handler和HandlerThread

 

Android中的主线程由 looper 和 Handlers 组成。所以了解创建无阻碍的响应式 UI 很重要。

 

了解Android核心:Looper,Handler和HandlerThread

  1. MessageQueue 是一个队列,其中包含消息任务。
  2. Handler 在 MessageQueue 中以任务形式排队,Looper 在任务出现时执行它们 MessageQueue.
  3. Looper 使线程保持活动状态,循环 MessageQueue 并向相应 Handler 的进程发送消息。
  4. Thread 通过调用 Looper 的 quit() 方法终止。

 

Handler 及其组件

  • Handler:框架的重要对象,它具有双重责任。它绑定到给定线程的特定 Looper,并提供了将消息发送到相关 MessageQueue 的方法。该处理器还负责在实际执行消息的内容。
  • Message:封装有关在特定线程上执行的操作信息。
  • Runnable:抽象一个线程可以执行的任何操作的接口。
  • MessageQueue:表示线程消耗的消息队列。
  • Looper:负责循环的对象,循环检查 MessageQueue 以查看是否有消息要运行并将其发送到特定的 Handler。每个线程只能有一个 Looper。
  •  

现在,开始编写代码了。

 

例子1:

 

使用 Thread:

创建主线程处理程序

Handler handler = new Handler(Looper.getMainLooper());
 thread = new Thread(new Runnable() {
            @Override
            public void run() {
                handler2 = new Handler(Looper.getMainLooper());
                handler2.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this,"hello world",Toast.LENGTH_SHORT).show();
                    }
                });
            }
       });

使用 HandlerThread:

handlerThread = new HandlerThread("name");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
handler.post(new Runnable() {
      @Override
      public void run() {
          Toast.makeText(MainActivity.this,"世界你好",Toast.LENGTH_SHORT).show();
      }
});

注意:使用完 HandlerThread 后,要调用 quit() 方法退出。

 

例子2:

 

进度条

 

了解Android核心:Looper,Handler和HandlerThread

MainActivity.java
public class MainActivity extends AppCompatActivity {

    Handler handler;
    Thread thread;
    Button botton;
    int MAX = 100;
    TextView textView;
    HandlerThread handlerThread;
    ProgressBar progressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initThread();
    }
    private void initView(){
        botton = (Button)findViewById(R.id.start_progress);
        progressBar = (ProgressBar)findViewById(R.id.progressBar);
        textView = (TextView)findViewById(R.id.textView);
        progressBar.setMax(MAX);
    }
    private void initThread(){
        handler = new Handler(){
            @Override
            public void handleMessage(Message message){
                super.handleMessage(message);
                textView.setText(message.what+"");
            }
        };
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i=0;i<100;i++){
                    progressBar.setProgress(i);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Message msg = new Message();
                    msg.what = i;
                    msg.obj = "obj";
                    handler.sendMessage(msg);
                }
            }
        });
    }
    public void onClick(View view){
        switch (view.getId()){
            case R.id.start_progress:
                thread.start();
                break;
             default:
                 break;
        }
    }
    public void onDestroy(){
        super.onDestroy();
        handlerThread.quit();
    }
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true" />

    <Button
        android:id="@+id/start_progress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_below="@+id/progressBar"
        android:layout_marginLeft="24dp"
        android:layout_marginStart="24dp"
        android:layout_marginTop="62dp"
        android:onClick="onClick"
        android:text="Start" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/start_progress"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_marginEnd="85dp"
        android:layout_marginRight="85dp"
        android:textSize="16sp"
        android:text="Count 1"
        android:layout_alignTop="@+id/start_progress" />

</RelativeLayout>

例子3:

 

看到这一点,您可能不太了解,也许会感觉到我在哪里。
接下来,我们使用一个简单的示例来验证和解决我们内心的疑虑。

 

了解Android核心:Looper,Handler和HandlerThread

 

在这里,我们放置一个按钮,单击后更改TextView的值。

 

还设置延迟以模拟要处理的大量数据。

 

public class MainActivity extends AppCompatActivity {

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

    public void buttonClick(View view)
    {

        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        TextView myTextView = (TextView)findViewById(R.id.myTextView);
        myTextView.setText("Button Pressed");

    }
}

 

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <TextView
        android:text="hello world"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"
        android:id="@+id/myTextView" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="click me"
        android:id="@+id/button"
        android:layout_below="@+id/myTextView"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="45dp"
        android:onClick="buttonClick" />

</RelativeLayout>

 

运行后发现,延迟了6秒后才更新视图,这在实际开发中,会严重影响用户体验,造成一种严重卡顿的现象。

 

要解决这个问题,我们可以使用多线程的方法,修改后如下。

    public void buttonClick(View view)
    {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(6000);
                    Log.d("thread:","hello world");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        thread.start();
    }

在主线程以外的线程中更新用户界面元素违反了Android开发的关键规则。因此,为了更新用户界面,有必要实现线程的处理程序(Handler)。

    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg){
            super.handleMessage(msg);
            TextView myTextView = (TextView)findViewById(R.id.myTextView);
            myTextView.setText("Button click");
        };
    };

再次修改 buttonClick() 中的内容

    public void buttonClick(View view)
    {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(6000);
                    Log.d("thread:","hello world");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                handler.sendEmptyMessage(0);
            }
        });
        thread.start();
    }

拓展内容:handler 几种方法

方法 描述
sendMessage 将消息推送到消息队列的末尾。
sendMessageDelayed 将消息推送到消息队列的末尾。
sendMessageAtTime 将消息推送到消息队列的末尾。
sendEmptyMessage 发送仅包含单个int代码的消息。
sendEmptyMessageDelayed 在指定的时间过后发送要传递的消息。
sendEmptyMessageAtTime 发送消息以在指定的绝对时间传递。

 

 

 

 

 

 

 

 

使用 messaga 对象保存数据

 

    public void buttonClick(View view)
    {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(6000);
                    Log.d("thread:","hello world");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Message msg = handler.obtainMessage();
                Bundle bundle = new Bundle();
                bundle.putString("name","anny");
                msg.setData(bundle);
                handler.sendMessage(msg);
            }
        });
        thread.start();
    }

obtainmessage() 是从消息池中拿来一个msg 不需要另开辟空间new

取出数据并赋值给 TextView 

    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg){
            super.handleMessage(msg);
            Bundle bundle = msg.getData();
            String name = bundle.getString("name");
            TextView myTextView = (TextView)findViewById(R.id.myTextView);
            myTextView.setText(name);
        };
    };

 

例子4:

 

为线程创建Looper和MessageQueue:

 

public class Test extends Thread{
    public Handler handler;
    public void run(){
        Looper.prepare();
        handler = new Handler(){
            @Override
            public void handleMessage(Message message){
                super.handleMessage(message);
                Log.d("obj:","hello world");
            }
        };
        Looper.loop();
    }
}

 

 

仔细看你会发现,我们在这里使用了 Looper.prepare()和 Looper.loop()

 

为什么前面的示例不起作用?为什么在这里使用它?

 

1:在默认线程下,主线程系统将自动创建Looper对象并启动消息循环。

2:在主线程中mainHandler = new Handler()等同于new Handler(Looper.myLooper())

 

将消息发送到 MessageQueue

 

第一种方式:Message

Message msg = new Message();
msg.obj = "Ali send message";
handler.sendMessage(msg);

第二种方式:Runnable

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        // this will run in the main thread
    }
});

 

拓展:

方法 描述
post 立即让Runnable排队执行。
postAtTime 使Runnable排队以在以毫秒为单位的绝对时间执行。
postDelayed 使Runnable排队以指定的毫秒延迟后执行。
postAtFrontOfQueue 立即将Runnable排队到要执行的前端。

 

 

 

 

 

 

 

 Android 提供了 HandlerThread (线程的子类) 来简化过程。在内部,它以健壮的方式执行与我们相同的操作,因此,请始终使用 HandlerThread。

 

public class Test extends HandlerThread {
    public Handler handler;
    public Test(String name) {
        super(name);
    }
    @Override
    protected void onLooperPrepared(){
        handler = new Handler(getLooper()){
            @Override
            public void handleMessage(Message message){
                super.handleMessage(message);
                //接收消息
            }
        };
    }
}
 

我们在调用 onLooperPrepared 时实例化了 Handler。因此,Handler 可以将其与 Looper 关联。

 

1:仅在 start() 调用 HandlerThread 之后,即线程运行之后,才准备 Looper。

2:只有在准备好之后,Handler 才能与 HandlerThread Looper 关联。

 

另一种方式创建 HandlerThread 的方式(之前提过):

HandlerThread handlerThread = new HandlerThread("myHandlerThread");
handlerThread.start();
Looper looper = handlerThread.getLooper();
Handler handler = new Handler(looper);

重要说明:请记住,在完成后台线程或活动onDestroy()方法之后调用handlerThread.quit()。

相关标签: Android