android初学遇到的坑(1):程序崩溃的原因: 客户端UI避免耗时操作/联网操作 (下)(handler的使用)

本篇正式介绍安卓UI线程与其他子线程交互的正确方法。

相比于PC客户端,安卓客户端的UI线程对耗时操作更加敏感,因此像上文提到的ArrayBlockingQueue,因为需要用到阻塞方法,所以不建议在安卓中使用。本篇将介绍 android提供的轻量级的线程交互方式——Handler,并将用Handler实现子线程通知UI线程更新、UI线程通知子线程给服务器发消息的模拟例子。

Handler的基础知识

Handler、MessageQueue、Looper,很多文章中,这些词几乎都是同时出现的。

接下来我尝试尽量用形象的语言来表述他们之间的关系。

如果把要一个线程比喻成一个生产队(这个比喻可能有些不恰当,因为一个线程只能干一个工人的活,硬要理解的话,可以理解为只有一个工人的生产队),那么Handler就是车间主任, Looper是车间主任的助理,以消息队列(MessageQueue)形式记录生产队的工作任务,而Message,自然就是这个施工队所要干的工作。

以上表述实际上还隐含了一个对应关系——一个Handler对应一个Looper,而一个Looper中会有多个Message

那么也就意味着,我们要新建一个Handler的时候,必然还要给Handler准备一个Looper(主线程除外),好让他们得以配合。

好了,让我们看一下整体的流程:让我们调用车间主任,分配给这个生产队一些任务(Message)。车间主任收到任务,因为自己还在处理手头上的其他任务,所以让助理先把这个分配的任务记录下来(MessageQueue)。每当车间主任手头上的任务干完,助理就会提示他下一条任务(Looper.loop())。好了,车间主任读到了我们给的那条任务,根据自己头脑中的固定行事方式(handleMessage)分析这条任务,然后就根据这样的理解,把自己分析完的更细化的任务分配给生产队去做。

具体实现

同样的 为了实现发送和接收的异步,我们需要一个发送线程和一个接收线程

接收线程

import android.os.Message;
import android.util.Log;
public class ClientRecvThread extends Thread{
    @Override
    public void run() {
        try {
            Thread.sleep(2000); //等待服务器消息的阻塞语句
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.d("RECV", "接收到了来自服务器的消息 ");
        Message msg= new Message();
        msg.what=1;
        msg.obj="服务器传来的数据";
        MainActivity.mHandler.sendMessage(msg);//通知UI更新
    }
}

发送线程

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
public class ClientSendThread extends Thread{
    Looper looper;
    Handler handler;
    @Override
    public void run() {
        Looper.prepare();
        looper=Looper.myLooper();
        handler=new Handler(looper){
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what){
                    case 2:
                        Log.d("SEND", "已向服务器发送消息:"+msg.obj);;//收到了UI线程的消息,在这里发送消息给服务器
                        break;
                    default:break;
                }
            }
        };
        /*
        *在这里完善run方法
        * */
        Looper.loop();//loop()方法一定要放在最后
    }
}

这里一定注意:因为Looper.loop()方法是阻塞的,所以一定要放在最后,保证前面的语句都能得到执行。

UI线程

import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    static Handler mHandler;
    TextView textView;
    Button button;
    ClientRecvThread crt;
    ClientSendThread cst;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView);
        button = findViewById(R.id.button);
        crt=new ClientRecvThread();
        cst=new ClientSendThread();
        crt.start();
        cst.start();
        //处理UI信息
        mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what){
                    case 1:
                        textView.setText((String)msg.obj);//UI收到了接收子线程的消息,在这                                                        
                                                                                  里更新
                        break;
                    default:break;
                }
            }
        };
        try {
            Thread.sleep(20);//在这里进行短暂sleep,防止发送子进程中的looper没有准备好
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //注册向服务器发送数据的方法
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //向服务器发送数据
                Message msg = new Message();
                msg.what =2 ;
                msg.obj ="要向服务器发送的消息" ;
                cst.handler.sendMessage(msg);
            }
        });
    }

}

其中sleep的时间不能太长,否则会影响交互!

结果

以上是本篇全部内容,欢迎交流指正!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值