Android面试系列之异步消息处理相关

我们在平时的项目开发中,肯定会遇到处理异步任务的场景。因为Android中的UI线程是不安全的,我们需要更新ui的话就必须在ui线程上进行操作。否则就会抛异常。
这个时候我们就需要用到异步消息处理了

比如,在子线程中请求数据,拿到数据后告诉ui线程去更新界面,
在子线程下载文件告诉ui线程下载进度,ui线程去更新进度等等。

我们常用的方式就是通过Handler, AnyncTask或者IntentService来处理异步消息。

Handler


什么是Handler
handler是Android中用来处理消息的一种机制,我们可以简单的理解为handler就是用来更新ui的。

handler的使用方法
1.handler.post(runnable)**

1.在ui线程中创建Handler,注意Handler在哪个线程中创建,就会与哪个线程进行绑定。所以我们一般都在主线程上创建Handler。

private static Handler handler = new Handler();

2.在子线程中,通过handler.post()来更新ui

  class WorkThread extends Thread {
        @Override
        public void run() {
      /*      runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    tv.setText("aaa");
                }
            });

            tv.post(new Runnable() {
                @Override
                public void run() {
                    tv.setText("ccc");
                }
            });*/
            handler.post(new Runnable() {
                @Override
                public void run() {
                    tv.setText("bbb");
                }
            });

        }
    }

2.handler.send(Message)

1.在ui线程中创建Handler并重写handleMessage方法,根据msg.what处理消息

   private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:

                    tv.setText("aaa");
                    break;
            }
        }
    };

2.再需要传递消息的地方创建Message,并通过handler.sendMessage()发送消息

 Message msg = new Message();
 msg.what = 1;
 handler.sendMessage(msg);

handler机制的原理
实际上在我们在创建Handler的时候,就已经把handler和所在线程绑定到一起了。然后我们通过handler发送的消息就会在该线程中获取到消息并进行处理。

来看看源码:
这是Handler构造方法的源码。
这里写图片描述
可以看到,Handler在构造方法中通过Looper.myLooper()获取了一个Looper对象,并通过Looper对象获取到一个消息队列。

再来看看myLooper()中的源码:
这里写图片描述

可以看到,myLooper()返回的是当前线程的Looper对象,注意:每一个线程只有一个Looper对象。

而myQueue返回了Looper中的MessageQueue.可以看到消息队列实在Looper的构造方法中被创建出来的。

那么问题来了
这个Looper对象是什么时候放进去的呢?
我们来看看Loopper类中的这个方法
这里写图片描述
可以看到,实际上是在perpare这个方法中设置了Looper

这么一来,就清楚是怎么回事儿了。
Handler在那个线程中创建,就会跟哪个线程绑定,并且与Looper和Message相关联。

要记住的方法:

Lopper.prepaer()
Looper.loop() 实际上源码就是创建了个死循环,去消息队列中获取消息。获取到消息就通过handler.dispatchMessage(msg)将消息发送出去。
handleMessage: 处理接收到的消息

handler引起的内存泄漏及解决办法

原因:静态内部类持有外部类的匿名引用,导致外部的activity无法释放。

解决办法:将handler声明为static即可,这样的话,handler对象由于是静态类型无法持有外部activity的引用,但是这样Handler不再持有外部类的引用,导致程序不允许在Handler中操作activity中的对象了,这时候我们需要在Handler对象中增加一个对activity的弱引用;
如果使用的是handler.post()的话,就在activity的onDestory()方法中调用 handler.removeCallbacks();

 static class MyHandler extends Handler {
        WeakReference<MainActivity> mActivityReference;

        MyHandler(MainActivity activity) {
            mActivityReference = new WeakReference<MainActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            final MainActivity activity = mActivityReference.get();
            if (activity != null) {
               activity.tv.setText("aaa");

            }
        }
    }
    
    private MyHandler handler = new MyHandler(this);


AsyncTask


什么是AsyncTask
AsyncTask是Android提供的一个轻量级的异步任务处理类,本质是对线程池和Handler的封装,方便我们使用。


AsyncTask的使用方法
AsyncTask本身是一个抽象类,我们使用时直接继承自AsyncTask并实现相应的方法即可。

AsyncTask定义了三种泛型类型 Params,Progress和Result。


Params			 启动任务执行的输入参数,
Progress 	     后台任务执行的百分比。
Result 			 后台执行任务最终返回的结果

AsyncTask有五个常用方法:

onPreExecute()     				在耗时操作执行之前调用,主要是用来做一些初始化操作,实在ui线程上调用。
doInBackground(Params…)      	 在onPreExecute之后调用,用来执行耗时操作,在工作线程中执行
publicProgress(Progress…)       在doInBackground中调用,可以用来更新任务的进度。
onProgressUpdate(Progress…)  	 运行在主线程,可以用来更新进度
onPostExecute(Result)    		 运行在主线程,返回任务执行的结果
onCancelled()            		 取消任务
package com.yzq.handler;

import android.os.AsyncTask;

/**
 * Created by yzq on 2017/5/26.
 */

public class task extends AsyncTask<Integer, Integer, String> {


    /*任务执行前调用  运行在主线程 */
    @Override
    protected void onPreExecute() {

        super.onPreExecute();
    }

    /*执行耗时任务  运行在后台线程*/
    @Override
    protected String doInBackground(Integer... params) {
        publishProgress();//更新进度
        return null;
    }

    /*运行在主线程  更新进度*/
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
    }

    /*运行在主线程  返回执行完成后的结果*/
    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
    }

    
    /*取消任务时调用*/
    @Override
    protected void onCancelled() {
        super.onCancelled();
    }
}


AsyncTask的机制原理
1.AsyncTask本质上是一个静态的线程池,其派生出来的子类可以实现不同的异步任务,这些任务都会提交到线程池中去执行
2.耗时操作是在doInBackground中执行的
3.当任务状态(pendding,running,finished)改变后,工作线程会向ui线程发送消息,AsyncTask内部的InternalHandler会响应这些消息并执行相关回调函数

AsyncTask注意事项
1、AsyncTask不适合特别耗时的任务
AsyncTask的生命周期和Activity的生命周期不同步,Activity销毁了但是AsyncTask中的任务还是会继续执行完毕,一个最典型的例子就是Activity的横竖屏切换,AsyncTask中引用的Activity不是当前的Activity,onPostExecute()中执行的仍然是上一个Activity。还有一个原因是因为AsyncTask在执行长时间的耗时任务时也会持有一个Activity对象,即使这个Activity已经不可见了,Android也无法对这个Activity进行回收,导致内存泄露。

2、AsyncTask只能在主线程中创建以及使用
AsyncTask被用于执行异步任务,然后更新UI,所以最后的onPostExecute()方法执行在创建该AsyncTask对象的线程中,如果不在主线程中创建以及使用,就达不到更新UI的目的。

3、一个AsyncTask对象只能执行一次
一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常

4、AsyncTask在不同的Android版本下的并行和串行问题
关于AsyncTask的并行和串行问题,在不同的API下是有不同的。在Android1.6之前,AsyncTask是串行执行任务的;到了Android1.6时,开始采用线程池来并行执行任务;在Android3.0之后的版本中,AsyncTask又开始用一个线程串行执行任务。虽然Android3.0之后采用串行方式执行任务,但我们可以通过AsyncTask的executeOnExecutor(exe,params),自定义一个线程池来并行执行任务。


HandlerThread


什么是HandlerThread
在Android我们经常会遇到耗时操作,一般我们都是开启一个子线程执行耗时操作。执行完成后GC会自动销毁线程,如果再有耗时操作的话再去创建线程,然后再销毁线程。这样是很消耗系统资源的。
还有Android中,子线程默认是没有开启Looper循环的,也就是说,如果我们想在子线程中使用Handler的话,我们需要手动调用Looper.prepare()启用Looper。这样一来我们就需要手动去维护Looper,很麻烦。
所以,谷歌公司给我们封装好了一个类就是HandlerThread以便于我们使用。

HandlerThread本质上是一个线程类,继承自Thread。
HandlerThread本身有自己的Looper对象。
讲HandlerThread中的Looper对象获取后传给Handler,就可以在Handler中的HandleMessage中处理异步消息。

优点与缺点
优点:不会有阻塞,减少对性能的消耗
缺点:不能同时进行多任务处理,效率不高。HandlerThread是一个串行队列,内部只有一个线程。

HandlerThread的作用
一般是用在子线程跟子线程进行通信


IntentService##


IntentService是什么
IntentService本质上就是继承自Service,其内部封装了HandlerThread,IntentService和Service的生命周期一样,且启动方式也一样,当IntentService中的耗时任务执行完毕后,IntentService会自动停止,多次启动IntentService时。耗时任务会以队列的形式存在于IntentService中,并在onHandlerIntent方法中一个一个的执行。

使用方法
1,继承自IntentService并重写其构造方法和onHandleIntent()方法
2.在HandlerIntent()方法中处理耗时操作


public class MyIntentService extends IntentService {


    public MyIntentService() {
        super("yzq");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {

        L.i("onHandleIntent"+intent.getStringExtra("i"));
    }
}

3.启动service,注意要在清单配置文件中生命service

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        for (int i=0;i<10;i++){
            Intent intent=new Intent(MainActivity.this,MyIntentService.class);
            intent.putExtra("i",i+"");
            startService(intent);

        }

    }
}

如果你觉得本文对你有帮助,麻烦动动手指顶一下,算是对本文的一个认可,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

喻志强(Xeon)

码字不易,鼓励随意。

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

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

打赏作者

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

抵扣说明:

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

余额充值