了解Android核心:Looper,Handler和HandlerThread

 

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

 

  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:

 

进度条

 

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:

 

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

 

 

在这里,我们放置一个按钮,单击后更改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()。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一拖再拖 一拖再拖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值