【从零到一】Android线程间通讯(二) | (Android中的线程、Handler、AsyncTask)

本文详细介绍了Android中UI线程和工作线程的概念,以及如何在工作线程中更新UI。重点讲解了Handler的原理、作用、相关类及工作流程,并给出了代码示例。同时对比分析了AsyncTask的使用,阐述了在Service中使用线程的原因,特别是IntentService的优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


1. Android中的线程

 1.1 什么是UI线程、工作线程?

  UI线程其实就是主线程。在启动一个Android程序时,会启动一个DVM虚拟机,创建一个新的进程,在这个进程中创建一个线程,这个线程就是主线程。因为主线程是与UI界面交互的线程,因此也称为UI线程

  Android框架中这种在单条线程中进行事件分发及UI交互的机制被称为Android单线程模型

 单线程模型的两条规则:

  1. 不允许在UI线程中进行耗时操作(影响用户体验);
  2. 不允许在UI线程外操作界面控件(界面控件是非线程安全的,只能在UI线程中操作UI控件);

  工作线程其实就是子线程。为了不阻塞UI线程,耗时操作应该在工作线程中进行。
  Android系统同时也提供了一些工具,用于在工作线程中更新UI


2. Android中更新UI的几种方式

 2.1 在工作线程中更新UI

  • 使用Activity对象中的runOnUiThread()方法
  • 使用View对象及子类对象的post()和postDelayed()方法

代码演示:

public class MainActivity extends AppCompatActivity {
    Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = findViewById(R.id.Button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Executors.defaultThreadFactory().newThread(new Runnable() {
                    @Override
                    public void run() {
                        //1.使用Activity对象的runOnUiThread()方法更新UI
                        MainActivity.this.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                button.setText("使用Activity对象的runOnUiThread()方法更新UI");
                            }
                        });

                        //2.1使用View对象的post()方法更新UI
                        button.post(new Runnable() {
                            @Override
                            public void run() {
                                button.setText("使用View对象的post()方法更新UI");
                            }
                        });

                        //2.2使用View对象的postDelayed()方法更新UI
                        button.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                button.setText("使用View对象的postDelayed()方法更新UI");
                            }
                        },1000);
                    }
                }).start();
            }
        });
    }
}

 2.2 使用Handler

  2.2.1 什么是Handler

  Handler是用于线程间传递信息的工具类,用来处理异步消息,是Android消息处理机制的重要角色。

  子线程可以通过Handler更新UI,也可以通过Handler与主线程进行通讯

  2.2.2 Handler的作用

  1. Handler可以发送并立即执行一个消息
  2. Handler可以发送并延迟在指定时间执行一个消息

  应用场景:

  1. 在当前线程中调度任务
  2. 在其他线程中为当前线程安排任务

  2.2.3 Handler的相关类及其方法

  知道什么是Handler后,我们还需要了解一下Handler的相关类,及它们之间的相互作用,如下表:

作用备注
Handler(处理者)发送与处理Message①添加MessageMessageQueue;②处理Looper分派的Message
Message(消息)存储需要操作的通讯信息MessageHandler接受和处理的消息对象(即线程间通讯的数据单元)
MessageQueue(消息队列)先进先出地管理Message一种数据结构,初始化Looper时会自动创建一个MessageQueue
Looper(循环器)管理MessageQueue,不断地从MessageQueue中取出Message,然后交给Handler处理每个线程只能有一个Looper,每个Looper可以绑定多个线程的Handler(即多个线程中的Handler可以往一个线程中Looper管理的MessageQueue中添加Message),这样就为多线程通讯提供了可能

  
  相关类的常用方法,如下各表:

Handler(处理者)

常用方法作用
obtainMessage():Message返回一个Message对象
obtainMessage(int what, int arg1, int arg2, Object obj):Message返回一个Message对象,返回的Message上设置what,obj,arg1和arg2值
sendMessage(Message msg):boolean将Message传入MessageQueue的最后面
sendMessageAtFrontOfQueue(Message msg):boolean将Message传入MessageQueue的最前面,以便在消息循环的下一次迭代中处理
sendMessageDelayed(Message msg,long delayMillis):boolean在一定延迟后,将Message传入MessageQueue的最后
sendEmptyMessage(int what):booleansendMessage()方法的简约版本,把传入的Message换成what
post(Runnable r):booleansendMessage()方法的简约版本,Runnable对象会被包装成一个Message对象,这个Message对象中的callback属性值也就是Runnable对象
hasMessages(int what):boolean判断消息队列中是否有指定的消息对象
removeCallbacks(Runnable r):void在消息队列中,删除待处理的 Runnabler
removeMessages(int what):void在消息队列中,删除指定what的Message
removeCallbacksAndMessages(Object token):void删除所有待处理的发送消息为 token 的 callbacks 和 message;如果 token 为 null,则所有的 callbacks 和 message 都将被删除

  obtainMessage()的一系列重载方法,内部其实是调用了Messageobtain()方法,并把调用obtainMessage()方法的Handler对象传入到obtain()方法中。
  

Message(消息):

常用方法作用
obtain():Message创建一个不包含任何初始数据的Message对象
obtain(Message orig):Message拷贝一个Message对象
obtain(Handler h):Message为创建的Message对象绑定一个Handler
obtain(Handler h, Runnable callback):Message为创建的Message对象创建一段可执行代码
obtain(Handler h, int what, int arg1, int arg2):Message与obtain()方法相同,但设置target,what, arg1和arg2成员的值
setTarget(Handler target):void设置Message对象对应的Handler
sendToTarget():void将Message发送到对应的Handler中

  

  2.2.4 Handler的工作流程

在这里插入图片描述
  当主线程创建时,会自动创建Looper对象(其他线程需手动创建)。而Looper对象创建时会自动创建一个MessageQueue

  在主线程创建一个HandlerHandler发送Message后,MessageQueue存放Handler发送过来的Message

  Looper用来维护MessageQueue,不断地从MessageQueue中取出Message,然后交给Handler处理。Looper是与线程绑定的,每个线程只能有一个Looper,也只能有一个MessageQueueMessageQueue中能有多个Message)。

  创建Handler时(每个线程可以有多个Handler),Handler会跟线程中的Looper绑定,由于Looper也是与线程绑定的,所以Handler同样与所在线程绑定。当使用Handler在其他线程发消息时,最终也会回到原线程处理,从而达到所谓的异步效果
  
  

  2.2.5 Handler的应用

  1. 创建消息:
    	//创建主线程Handler
    	Handler handler = new Handler();
        //创建消息对象
        Message message = Message.obtain();
        //拷贝消息对象
        Message message1 = Message.obtain(message);
        //创建消息对象,并为为消息对象设置可执行代码
        Message message2 = Message.obtain(handler, new Runnable() {
            @Override
            public void run() {
                //do something
            }
        });
        //创建一个与handler对象绑定的消息对象
        handler.obtainMessage();
    
  1. 发送消息:
    	//创建主线程Handler
    	Handler handler = new Handler();
		//发送消息
        Message message = Message.obtain();
        handler.sendMessage(message);
        //基于android.os.SystemClock#uptimeMillis
        handler.sendMessageAtTime(message,1000);
        handler.sendMessageDelayed(message,1000);
        //发送消息到消息队列最前端
        handler.sendMessageAtFrontOfQueue(message);
        //sendMessage系列方法的简约版本
        handler.sendEmptyMessage(1);

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                //do something
            }
        };
        Object token = new Object();
        //sendMessage系列方法的简约版本
        handler.post(runnable);
        //基于android.os.SystemClock#uptimeMillis
        handler.postAtTime(runnable,token,1000);

        //判断消息队列中是否有指定的消息对象
        handler.hasMessages(1);
        handler.hasMessages(1,token);

        //移除what值为指定值的消息对象
        handler.removeMessages(1);
        //如果token为null,移除所有what值为指定值的消息对象
        handler.removeMessages(1,token);

        //移除callback值为指定值的消息对象
        handler.removeCallbacks(runnable);
        //如果token为null,移除callback值为指定值的消息对象
        handler.removeCallbacks(runnable,token);

        //移除所有Object值为指定值的消息对象,如果token为null,移除消息队列中所有消息对象
        handler.removeCallbacksAndMessages(token);
  1. 接收与处理消息:

  handler.post(Runnable r)Runnable对象的处理方式:

  Runnable对象在发送前会被打包成一个Message对象,把Runnable对象赋值给Message对象的包访问权限属性callback
  在Looper处理消息时,调用Message对象对应的HandlerdispatchMessage(Message msg)派发消息,对于callback属性不为空的Message对象,它的callbackrun()方法将被直接调用执行,完成消息的处理。

源码:

	//1.post方法
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    
    //2.getPostMessage()方法
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

	//3.dispatchMessage(Message msg)方法
	public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

	//4.handleCallback(Message message)方法
    private static void handleCallback(Message message) {
        message.callback.run();
    }

方法演示:

	Handler handler = new Handler()
	
	handler.post(new Runnable() {
		@Override
			public void run() {
				Toast.makeText(DemoActivity.this,"Run",Toast.LENGTH_SHORT).show();
                }
        });

  ②重写handleMessage(Mesage msg)方法处理消息:

  在Looper处理消息时,调用Message对象对应的HandlerdispatchMessage(Message msg)派发消息,对于callback属性为空,mCallback也为空的Message对象,直接调用handleMessage(Mesage msg)方法,由于handleMessage(Mesage msg)方法是空的,所以需要重写这个方法进行消息处理。

源码:

	//1.dispatchMessage(Message msg)方法
	public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

	//2.handleMessage(Message msg)方法
    public void handleMessage(Message msg) {
    }

方法演示:

    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 1){
                Toast.makeText(DemoActivity.this,"what is 1",Toast.LENGTH_SHORT).show();
            }
        }
    };

  ③实现Handler.Callback接口处理消息:

  在Looper处理消息时,调用Message对象对应的HandlerdispatchMessage(Message msg)派发消息,对于callback属性为空,mCallback不为空的Message对象,调用mCallbackhandleMessage(Mesage msg)方法,如果返回值为ture则dispatchMessage(Message msg)运行结束,返回值为false则调用需要重写的handleMessage(Mesage msg)方法。

源码:

	//1.dispatchMessage(Message msg)方法
	public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

	//2.Callback的handleMessage(Message msg)方法
    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        public boolean handleMessage(Message msg);
    }

	//3.handleMessage(Message msg)方法
    public void handleMessage(Message msg) {
    }

方法演示:

    Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == 1){
                Toast.makeText(DemoActivity.this,"First handle",Toast.LENGTH_SHORT).show();
            }
            return false;//false 需要消息进一步处理
        }
    }){
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 1){
                Toast.makeText(DemoActivity.this,"what is 1",Toast.LENGTH_SHORT).show();
            }
        }
    };
  1. 防止内存泄漏:

如下代码所示:

    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
        	//do something
        }
    };

  Handler应为静态内部类才不会导致内存泄漏的发生,为什么呢?

  因为一个非静态内部类默认持有一个外部类的引用,当我们使用Handler发送一个消息时,Messager会默认持有一个Handler的引用(即持有外部类的引用),如果这个Message对象是延迟执行的,当它被发送到主线程LooperMessageQueue中,这时候从当前界面退出,主线程的Looper并未因此关闭,由于Looper中还有当前界面的引用,这将导致当前界面不能正常销毁,从而导致内存泄漏

  2.2.6 HandlerThread

  1. 创建工作线程Handler

  创建工作线程Handler前必须先为工作线程创建Looper对象,让我们先了解一下Looper

常用方法作用
prepare():void为当前线程创建Looper对象(内部使用ThreadLocal实现 )
loop():void线程阻塞到MessageQueue上,等待Message执行,后面不会执行其他代码
quit():voidLooper立即终止执行
quitSafely():void等待MessageQueue中的Message执行完毕后,让Looper终止执行
myLooper():Looper获取当前线程的Looper对象
getThread():Thread获取当前Looper对象绑定的线程
getMainLooper():Looper获取主线程的Looper对象
isCurrentThread():Thread判断当前线程是否是Looper绑定的线程

代码演示:

    class Task extends Thread{
        Handler handler;
        @Override
        public void run() {
            Looper.prepare();//为当前线程创建Looper对象
            
            handler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    //process message
                }
            };
            Looper.loop();//线程阻塞到MessageQueue上,等待Message执行,后面不会执行其他代码
        }
  1. HandlerThread的使用

   是Thread的子类,用于方便地实现一个带有Looper的线程。

常用方法作用
构造函数:HandlerThread(String name)指定线程名字的HandlerThread
构造函数:HandlerThread(String name, int priority)指定线程名字,线程优先级的的HandlerThread
getLooper():Looper获取HandlerThread中的Looper
getThreadId():int获取HandlerThread中的线程Id
quit():voidHandlerThread中的Looper立即终止执行
quitSafely():void等待MessageQueue中的Message执行完毕后,让HandlerThread中的Looper终止执行
onLooperPrepared():voidLooperprepare()loop()之间调用,可执行自己想要的操作

代码演示:

public class MainActivity2 extends AppCompatActivity {

    HandlerThread handlerThread = new HandlerThread("HanlderThread"){
        @Override
        protected void onLooperPrepared() {
            System.out.println("Looper prepared");
        }
    };
    Handler handler;

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

        Button button = (Button) findViewById(R.id.button2);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if(handler==null){
                    handlerThread.start();
                    handler = new Handler(handlerThread.getLooper());
                }
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("Send from Main"+
                         " and run on "+Thread.currentThread().getName());
                    }
                });
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        handlerThread.quit();
    }
}

 2.3 使用AsyncTask

  在这里插入图片描述
  AsyncTask(即异步任务)是Android为我们提供的一个轻量级的异步任务类

  AsyncTask相当于一个多线程编程框架,介于Thread和Handler之间。通过AsyncTask我们可以轻松解决多线程之间的通讯问题。
  

AsyncTask抽象类:

  在使用AsyncTask之前,我们需要先定义一个类继承AsyncTask<Params, Progress, Result>,并指定3个泛型参数:

  • Params:传递给异步任务执行时的参数类型
  • Progress:更新进度的参数类型
  • Result:返/回结果的参数类型
常用方法作用
doInBackground(Params… params):Result执行在工作线程中需要执行的任务(唯一一个运行在工作线程中的方法)
cancel(boolean mayInterruptIfRunning):boolean指定异步任务执行时是否可以中断
execute(Runnable runnable):AsyncTask<Params, Progress, Result>方便在工作线程中执行一段代码(是在主线程中调用的)
get():Result返回异步任务执行的结果,属于阻塞方法(在doInBackground执行结束后才返回结果)
getStatus():AsyncTask.Status获取异步任务的状态
isCancelled():boolean判断异步任务是否被取消

计数器小Demo:

public class AsyncTask extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_async_task);
        final EditText input = (EditText) findViewById(R.id.TextView);
        final Button countBtn = (Button) findViewById(R.id.count);
        countBtn.setOnClickListener(new View.OnClickListener() {
            TimeCountTask timeCountTask;
            @Override
            public void onClick(View v) {
                String inputText = input.getText().toString();
                if(countBtn.getTag()==null){
                    timeCountTask = new TimeCountTask();
                    if(!TextUtils.isEmpty(inputText)){
                        //开始计时
                        timeCountTask.execute(Long.valueOf(inputText));
                    }else{
                        //开始计时
                        timeCountTask.execute();
                    }
                }else{
                    timeCountTask.cancel(true);
                }
            }
        });
    }

    class TimeCountTask extends android.os.AsyncTask<Object, String, String> {

        TextView showText;
        Button countBtn;
        EditText inputEdt;

        @Override
        protected void onPreExecute() {
            showText = (TextView) findViewById(R.id.show);
            countBtn = (Button) findViewById(R.id.count);
            inputEdt = (EditText) findViewById(R.id.TextView);

            inputEdt.setEnabled(false);
            countBtn.setTag(0);
            countBtn.setText("取消");
        }

        @Override
        protected String doInBackground(Object... params) {
            if(params.length==1&&params[0] instanceof Long){
                long inputTime = (long) params[0];
                for(;inputTime>0;inputTime--){
                    publishProgress("剩余:"+inputTime+"秒");
                    System.out.println(">>>:"+inputTime);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        return "";
                    }
                }
            }else{
                AsyncTask.this.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(AsyncTask.this, "参数错误", Toast.LENGTH_LONG).show();
                    }
                });
                cancel(true);
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(String... values) {
            showText.setText(values[0]);
        }

        @Override
        protected void onPostExecute(String s) {
            showText.setText("计时已完成");

            inputEdt.setEnabled(true);
            countBtn.setTag(null);
            countBtn.setText("计时");
        }

        @Override
        protected void onCancelled(String s) {
            showText.setText("计时已取消");

            inputEdt.setEnabled(true);
            countBtn.setTag(null);
            countBtn.setText("计时");
        }
    }
}

运行结果:

在这里插入图片描述


3. 在Service中使用线程

 3.1 为什么要在Service中使用线程

  在Service中运行线程可以保证线程所在进程至少具有服务进程级别的优先级。

  避免随着它们所在的进程变成较低优先级的进程,线程执行任务的完整得不到保证。
  

  Android中进程的分类及其优先级(优先级从上到下逐级递减):

  1. 前台进程
  2. 可见进程
  3. 服务进程
  4. 后台进程
  5. 空进程

  详细介绍推荐观看:Android的进程优先级

 3.2 使用IntentService

  IntentService是一个抽象类。在使用是需要先实现onHandleIntent(Intent intent)方法,然后在需要使用它的组件中调用startService(Intent intent)方法发送指令调用它。

  当然IntentService也有它的优缺点

  1. 优点:使用方便,不需要自己管理线程的创建和Service的销毁;
  2. 缺点:只有单个线程,所有任务需要排队执行,不适合多数的多任务情况。

计数器小Demo:

public class IntentServiceDemo extends AppCompatActivity {
    MyBroadcastReceiver myBroadcastReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_intent_service_demo);
        final EditText input1 = (EditText) findViewById(R.id.input1);
        final EditText input2 = (EditText) findViewById(R.id.input2);
        final Button startBtn1 = (Button) findViewById(R.id.start1);
        final Button startBtn2 = (Button) findViewById(R.id.start2);

        startBtn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String inputStr = input1.getText().toString();
                if(TextUtils.isEmpty(inputStr)){
                    Toast.makeText(IntentServiceDemo.this,"请输入",Toast.LENGTH_LONG).show();
                }else{
                    input1.setEnabled(false);
                    startBtn1.setEnabled(false);
                    Intent intent = new Intent(IntentServiceDemo.this, TimeCountService.class);
                    intent.putExtra(TimeCountService.START_TIME, Long.valueOf(inputStr));
                    intent.putExtra(TimeCountService.TARGET, R.id.show1);
                    startService(intent);
                }
            }
        });

        startBtn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String inputStr = input2.getText().toString();
                if(TextUtils.isEmpty(inputStr)){
                    Toast.makeText(IntentServiceDemo.this,"请输入",Toast.LENGTH_LONG).show();
                }else{
                    input2.setEnabled(false);
                    startBtn2.setEnabled(false);
                    Intent intent = new Intent(IntentServiceDemo.this, TimeCountService.class);
                    intent.putExtra(TimeCountService.START_TIME, Long.valueOf(inputStr));
                    intent.putExtra(TimeCountService.TARGET, R.id.show2);
                    startService(intent);
                }
            }
        });
        myBroadcastReceiver = new MyBroadcastReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(TimeCountService.ACTION_TIME_COUNT);
        registerReceiver(myBroadcastReceiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        unregisterReceiver(myBroadcastReceiver);
    }

    class MyBroadcastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if(TimeCountService.ACTION_TIME_COUNT.equals(intent.getAction())){
                String result = intent.getStringExtra(TimeCountService.RESULT);
                int target = intent.getIntExtra(TimeCountService.TARGET, -1);
                if(result!=null&&target!=-1){
                    TextView show = (TextView) findViewById(target);
                    show.setText(result);
                }
            }
        }
    }
}

Github项目地址:AndroidThreadDemo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值