Android多线程编程和handler线程间通信机制

一. 多线程编程

  1. 模拟主线程在网络上获取数据,并在主线程UI线程上展示的过程。点击按钮调用requestEmulator方法模拟从网络上获取数据的过程,其中主线程休眠一段时间表示正在响应。按钮响应使用布局响应方法。布局文件省略。

.java

public class ThreadActivity extends AppCompatActivity {

    private String requestString;
    private TextView tv_content;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thread);
        tv_content = findViewById(R.id.thread_tv_content);
    }

    public void startClick(View view) {
        requestString = requestEmulator();
        Toast.makeText(this, "获取字符串成功", Toast.LENGTH_SHORT).show();
        tv_content.setText(requestString);
    }


    public String requestEmulator(){
        String result = "";


        StringBuilder stringBuilder = new StringBuilder();

        for (int i = 0; i < 20; i++) {
            stringBuilder.append("字符串"+i);
        }

        try {
            Thread.sleep(26000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        result = stringBuilder.toString();

        return result;
    }
}
  1. 模拟使用子线程来进行网络数据的请求。但此时出现问题,子线程需要和主线程进行通信,主线程负责刷新UI,主线程如果操作UI,子线程还没请求数据完毕主线程UI就刷新了,导致数据无法更新。子线程无法直接操作UI控件,否则会引起闪退。

.java文件

public class ThreadActivity extends AppCompatActivity {

    private String requestString;
    private TextView tv_content;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thread);
        tv_content = findViewById(R.id.thread_tv_content);
    }

    public void startClick(View view) {

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                requestString = requestEmulator();
                tv_content.setText(requestString); //子线程操作UI报错
            }
        });         
        thread.start();
        //requestString = requestEmulator();
      // tv_content.setText(requestString);  //主线程操作UI无法刷新数据
        Toast.makeText(this, "获取字符串成功", Toast.LENGTH_SHORT).show();

    }


    public String requestEmulator(){
        String result = "";


        StringBuilder stringBuilder = new StringBuilder();

        for (int i = 0; i < 20; i++) {
            stringBuilder.append("字符串"+i);
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        result = stringBuilder.toString();
        return result;
    }
}

二. Handler机制线程间通信

在Android多线程编程中,子线程用于一些耗时操作,例如网络请求,后台下载等。主线程就是UI线程,负责和用户的交互以及数据的呈现。子线程无法操作刷新UI,而如果全部在主线程内操作则会引起长时间等待。因此需要开启子线程并完成线程间通信。使用Handler机制。

Handler由四部分组成,分别是Handler,Message,Looper,MessageQueue。Handler是主线程控制的,发消息是由子线程调用主线程的Handler对象向主线程的优先级消息队列发送消息。每隔固定的时间由Looper将MessageQueue中的消息循环取出,交回主线程handler对象的handleMessage方法处理,处理完成后缓存到消息池中以备复用。发送的消息可以是即时消息,也可以是延迟消息,延迟消息会被延迟一定毫秒来进行处理。消息队列中的优先级则是按照及时和延迟来进行排列的(message对象中的when来表示优先级)。

因此Handler对象可以看做是主线程子线程之间的邮差,信息被封装到Message里,MessageQueue相当于是邮箱,Looper是取信人。

以下代码模拟了handler的处理过程,点击按钮后子线程处理等待数据,处理完毕后将数据封装进message对象使用handler对象发送给主线程,主线程收到消息调用handlemessage处理。

message对象由一个标识码what来表示发信序列,obj封装信息对象。还有两个参数来封装整型数据,这四个属性都是公有的,无需调用set和get方法。

handler.java

public class HandlerActivity2 extends AppCompatActivity {

//主线程生成handler对象,并且使用主线程自己的Looper处理消息队列
 private Handler handler = new Handler(Looper.myLooper()){

     //ctrl+t 可提示方法参数的选择
     //ctrl+o 可查看内部拥有的方法,此时选择重写handleMessage方法
     @Override
     public void handleMessage(@NonNull Message msg) {
         super.handleMessage(msg);
         if (msg.what == 0){
             String info = (String) msg.obj;
             handle_tv.setText(info);
             Toast.makeText(HandlerActivity2.this, "消息传递完毕", Toast.LENGTH_SHORT).show();
         }
     }
 };
 private TextView handle_tv;
 private String requestString;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_handler2);
     handle_tv = findViewById(R.id.handle_tv);
 }
  public void handleClick(View view) {
     new Thread(new Runnable() {
         @Override
         public void run() {
             requestString = requestEmulator();
           //准备封装消息内容
             Message message = new Message();
           //Message message = Message.obtain();
             message.what = 0; //区分消息发送者
             message.obj = requestString;
             handler.sendMessage(message);
         }
     }).start();
     Toast.makeText(this, "按钮点击主线程执行完毕", Toast.LENGTH_SHORT).show();
 }
 public String requestEmulator(){
     String result = "";


     StringBuilder stringBuilder = new StringBuilder();

     for (int i = 0; i < 20; i++) {
         stringBuilder.append("字符串"+i);
     }

     try {
         Thread.sleep(3000);
     } catch (InterruptedException e) {
         e.printStackTrace();
     }result = stringBuilder.toString();

     return result;
 }
}

对于相同的 功能,此处有一个尝试,即在子线程中完成数据的请求并直接设置给textview控件,会导致app崩溃。

thread.java

public class ThreadActivity extends AppCompatActivity {

    private String requestString;
    private TextView tv_content;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thread);
        tv_content = findViewById(R.id.thread_tv_content);
    }

    public void startClick(View view) {

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                requestString = requestEmulator();
                tv_content.setText(requestString);
            }
        });thread.start();
        //requestString = requestEmulator();
        Toast.makeText(this, "获取字符串成功", Toast.LENGTH_SHORT).show();

    }


    public String requestEmulator(){
        String result = "";


        StringBuilder stringBuilder = new StringBuilder();

        for (int i = 0; i < 20; i++) {
            stringBuilder.append("字符串"+i);
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        result = stringBuilder.toString();

        return result;
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值