美文网首页Android学习
Android中的线程形态(二)(HandlerThread/I

Android中的线程形态(二)(HandlerThread/I

作者: Geeks_Liu | 来源:发表于2017-03-18 15:19 被阅读561次
    本篇提纲(二).png

    一.HandlerThread的使用与原理解析

      HandlerThread继承于Thread,所以它本质就是个Thread。与普通Thread的差别就在于,它不仅建立了一个线程,并且创立了消息队列,有自己的looper,可以让我们在自己的线程中分发和处理消息,并对外提供自己这个Looper对象的get方法。
      HandlerThread自带Looper使他可以通过消息队列,来重复使用当前线程,节省系统资源开销。这是它的优点也是缺点,每一个任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。

    1.HandlerThread使用步骤

    第一步:创建HandlerThread实例对象

    HandlerThread handlerThread = new HandlerThread("myThread");
    

    第二步:启动HandlerThread线程

    handlerThread.start();
    

    第三步:构建循环消息处理机制

     private Handler.Callback mSubCallback = new Handler.Callback() {
            //该接口的实现就是处理异步耗时任务的,因此该方法执行在子线程中
            @Override
            public boolean handleMessage(Message msg) {
                    //doSomething  处理异步耗时任务的
                    mUIHandler.sendMessage(msg1);   //向UI线程发送消息,用于更新UI等操作
            }
        };
    
    

    第四步:构建子线程中的Handler:

    //由于这里已经获取了workHandle.getLooper(),因此这个Handler是在HandlerThread线程也就是子线程中
    childHandler = new Handler(handlerThread.getLooper(), mSubCallback);
    
    button.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            //发送异步耗时任务到HandlerThread中,也就是上面的mSubCallback中
            childHandler.sendMessage(msg);
        }
    });
    

    第五步:构建UI线程Handler处理消息(如果需要更新UI的话有这一步)

        private Handler mUIHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                //在UI线程中做的事情,比如设置图片什么的
            }
        };
    

    2.HandlerThread的实现原理

      HandlerThread的实现原理比较简单,源码也比较少(只有150行代码),首先它是继承自Thread类的:

    public class HandlerThread extends Thread {
    

    因此它完全可以当一个线程来使用,就像上面我们说的:new HandlerTread().start()这样,但是!!!——我们之前在Android中的消息机制——Looper、Handler、MessageQueue与Message这篇文章中说过,在子线程中创建一个Handler,需要手动创建Looper,即先Looper.parpare(),完了再Looper.loop()
    这样,我们来看看HandlerThread中的run方法:

        @Override
        public void run() {
            mTid = Process.myTid();  //获得当前线程的id
            Looper.prepare();
            synchronized (this) {
                mLooper = Looper.myLooper();
                //发出通知,当前线程已经创建mLooper对象成功,这里主要是通知getLooper方法中的wait
                notifyAll();
            }
            //设置当前线程的优先级
            Process.setThreadPriority(mPriority);
            //该方法实现体是空的,子类可以实现该方法,作用就是在线程循环之前做一些准备工作,当然子类也可以不实现。
            onLooperPrepared();
            Looper.loop();
            mTid = -1;
        }
    

    可以看到,这个run方法里边写死了,先Looper.prepare()然后Looper.loop(),也即是说,我们的HandlerThread这个特殊的“线程”是再带Looper的,因此——我们不能再主线程使用它,毕竟主线程是自带MainLooper的,一个线程只能有一个Looper,否则就会抛出异常。
      这里我们说一下,为什么要看这个run()方法,之前我们在Android中的线程形态(一)(进程/线程/线程池)中有讲过,Thread工作的时候,不论哪种实现方法,我们都必须重写这个类的run()方法,在其中写我们自己要做的事情。可以看到,HandlerThread类的run()方法已经给我们写死了,这就注定我们只能将它用在子线程中。

      我们再来看看HandelerThread类的额另一个重要的方法——getLooper():

        //该方法主要作用是获得当前HandlerThread线程中的mLooper对象
        public Looper getLooper() {
            if (!isAlive()) {   //如果线程不是活动的,则直接返回null
                return null;
            }
    
            // If the thread has been started, wait until the looper has been created.
            //如果线程已经启动,但是Looper还未创建的话,就等待,直到Looper创建成功
            synchronized (this) {
                while (isAlive() && mLooper == null) {
                    try {
                        //这里会调用wait方法去等待,当run方法中的notifyAll方法调用之后
                        //通知当前线程的wait方法等待结束,跳出循环,获得mLooper对象的值。
                        wait();
                    } catch (InterruptedException e) {
                    }
                }
            }
            return mLooper;
        }
    

    OK,看到这里,结合上面的HandelrThread使用步骤,我们需要总结一下:

    • HandelrThread是一个自带Looper的线程,因此只能作为子线程使用
    • HandlerThread必须配合Handler使用,HandlerThread线程中做具体事情,必须要在Handler的callback接口中进行,他自己的run方法被写死了。
    • 子线程中的Handler与HandlerThread的联系是通过childHandler = new Handler(handlerThread.getLooper(), mSubCallback);这句来进行的,也就是你说,childHandler获得HandlerThread线程的Looper,这样,他们两个就在同一阵营了。这也就是在创建Handler作为HandlerThread线程消息执行者,必须在调用start()方法之后的原因——HandlerThread.start()之后,run()方法才能跑起来,Looper才能得以创建,handlerThread.getLooper()才不会出错。

    二.IntentService的使用与实现原理

      HandlerThread在Android中的一个具体的应用就是IntentService。IntentService是继承于Service并处理异步请求的一个类。注意这句话——“继承于Service”“处理异步请求”“继承于Service”表示他是一个服务,我们知道Service是存在于主线程的,它之中不能存在耗时操作,否则的话回应起ANR,因此便有了IntentService——这个封装了HandlerThread和Handler的特殊Service。

    1.IntentService的使用

    第一步:创建一个继承IntentService类的子类
    首先,通过源码我们知道,IntentService是一个继承自Service类的抽象类,因此我们必须创建一个继承它的子类才能实现相关功能:

    public class MyIntentService extends IntentService {
    
        public ServiceUpdate() {
             // 注意构造函数参数为空,这个myIntentService字符串就是IntentService中工作线程的名字
             super("myIntentService");
        }
    
        //打印生命周期
        @Override
        public void onCreate() {
            Log.i("myIntentService", "onCreate");
            super.onCreate();
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i("myIntentService", "onStartCommand");
            return super.onStartCommand(intent, flags, startId);
        }
        
        @Override
          public void onStart(Intent intent, int startId) {
              Log.i("myIntentService", "onStart");
              super.onStart(intent, startId);
          }
        
         @Override
          public IBinder onBind(Intent intent) {
            Log.i("myIntentService", "onBind");
            return super.onBind(intent);
          }
          
        //重写onHandleIntent方法,在该方法中进行我们的各种事务处理
        @Override
        protected void onHandleIntent(Intent intent) {
             //一般Intent是从Activity发过来的,携带识别参数,根据参数不同执行不同的任务
            String taskName = intent.getExtras().getString("taskName");
            switch (taskName) {
            case "task1":
                Log.i("myIntentService", "task1");
                break;
            case "task2":
                Log.i("myIntentService", "task2");
                break;
            default:
                break;
            }
        }
    
        @Override
        public void onDestroy() {
            Log.i("myIntentService", "onDestroy");
            super.onDestroy();
        }
    }
    

    第二步:在Activity中开启服务:

    public class MainActivity extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);
            ......
    
            //同一服务只会开启一个工作线程,在onHandleIntent函数里依次处理intent请求。
            Intent intent1 = new Intent(this,MyIntentService.class);
            Bundle bundle = new Bundle();
            bundle.putString("taskName", "task1");
            intent1.putExtras(bundle);
            startService(intent1);
    
            Intent intent2 = new Intent(this,MyIntentService.class);
            Bundle bundle2 = new Bundle();
            bundle2.putString("taskName", "task2");
            intent2.putExtras(bundle2);
            startService(intent2);
        }
    }
    

    第三步:在 Manifest 中注册服务

    <service android:name=".service.MyIntentService"/>
    

    上述过程中我们启动了两个IntentService服务,打印出来生命周期为:onCreate -> onStartCommand -> onStart -> onStartCommand -> onStart -> task1 -> task2 -> onDestroy,OnBinder没有执行。
      从结果可以看到,onCreate 方法只执行了一次,而 onStartCommand 和 onStart 方法执行了两次,开启了两个 工作线程(Work Thread),这就证实了之前所说的,启动多次,但IntentService 的实例只有一个,这跟传统的Service 是一样的。

    2.IntentServic源码分析

      IntentService继承Service类之后,整体的源码也就165行,跟HandlerThread差不多,我们先来看看它的实例变量:

    public abstract class IntentService extends Service {
        private volatile Looper mServiceLooper;     //服务所在线程的Looper
        private volatile ServiceHandler mServiceHandler;    //构建一个服务中的Handler
        private String mName;       //服务所在线程的名字
        private boolean mRedelivery;
    
    1).IntentService的构造方法

    上面加了注释的三个变量较为重要,我们待会会一步步分析。接下来我们看看他的构造方法:

        public IntentService(String name) {
            super();
            mName = name;
        }
    

    可以看到,上线例子中我们得

        public ServiceUpdate() {
             super("myIntentService");
        }
    

    其中“myIntentService”实际会上传到父类IntentService的构造方法中,也就是IntentService所在线程的线程名字,IntentService构造方法中的super();又会进一步调用Service类中的构造函数:

        public Service() {
            super(null);
        }
    

    再网上,就到ContextWrapper类里边去了。如果我们看过Activity的源码的话就会知道,Activity也是继承自ContextWrapper类的,而ContextWrapper类继承自Context!也就说,Service和Activity实际上都是一个Context!ContextWrapper有一个比较特殊的地方是,他的实例化的地方是在ActivityThread中由系统完成的,我们无权插手。因此,在StartService或者StartActivity的时候,系统就会自动帮我们实例化这两个组件。这样,Service的构造方法为空也就很好解释了。

    2).OnCreat()

      看上面的生命周期流程图我们知道,IntentService第一步指定的onCreat方法:

        @Override
        public void onCreate() {
            super.onCreate();
            HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
            thread.start();
    
            mServiceLooper = thread.getLooper();
            mServiceHandler = new ServiceHandler(mServiceLooper);
        }
    

    首先,super.onCreate();,这个我们看到Service的onCreat()方法是空的,所以不管,接着看。HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");看到了吧,这里重量级人物——HandlerThread出场了,新建了一个HandlerThread实例,上面我们说过HandlerThread()的构造方法传进去的是HandlerThread所在线程的名字,然后thread.start();,嗯,非常符合我们上半片文章讲的HandlerThread使用流程。
      mServiceLooper = thread.getLooper();这里通过mServiceLooper获取到HandlerThread线程的Looper。然后mServiceHandler = new ServiceHandler(mServiceLooper);,我们接着看源码:

        private final class ServiceHandler extends Handler {
            public ServiceHandler(Looper looper) {
                super(looper);
            }
    
            @Override
            public void handleMessage(Message msg) {
                onHandleIntent((Intent)msg.obj);
                stopSelf(msg.arg1);
            }
        }
    

    可以看到,ServiceHandler是IntentService类的一个内部类,并且继承自Handler,重写了handleMessage方法。很显然,这个类是用来处理startService(intent);传递来的消息的。这个消息处理的过程分为两步,第一调用onHandleIntent((Intent)msg.obj);方法;第二步stopSelf(msg.arg1);消息处理完后自动终止服务。所以我们接着而看onHandleIntent((Intent)msg.obj);:

        @WorkerThread
        protected abstract void onHandleIntent(Intent intent);
    

    呦呵?这是一个抽象方法,抽象类中的抽象方法意味着我们需要在子类中重写这个方法,也就是说,这个方法才是我们处理消息的地方。可以看到,这个方法上面加了注解,是位于工作线程(子线程)中的。

    3).onStartCommand()/onStart()

      好了,OnCreat()方法终于执行完了,onStartCommand()方法,它和onStart()方法实际上是连在一起的:

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            onStart(intent, startId);
            return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
        }
    

    直接看onStart():

        @Override
        public void onStart(Intent intent, int startId) {
            //Service 启动后直接就向 mServiceHandler 发送消息,则马上就会执行 handleMessage方法
            Message msg = mServiceHandler.obtainMessage();
            msg.arg1 = startId;
            msg.obj = intent;
            mServiceHandler.sendMessage(msg);
        }
    

    这个方法中,mServiceHandler这个Handler的子类,调用了obtainMessage()方法,该方法实际上上到Handler类中调用的是:

        public final Message obtainMessage(){
            return Message.obtain(this);
        }
    

    这个方法中,Message.obtain(this);这个方法我们复习一下,实际上就是Handler从消息池中取出一个消息,避免Message类重复创建的一个方法。回到IntentSerice的onStart()方法中,Message msg = mServiceHandler.obtainMessage();,这句就是获取一个Message对象,然后mServiceHandler.sendMessage(msg);通过Handler发送消息,添加到Looper中的MessageQuenue队列中。

    4).onDestroy()
        @Override
        public void onDestroy() {
            mServiceLooper.quit();
        }
    

    安全退出Looper()

    3.IntentService中的一些坑

    1).为什么不建议通过 bindService() 启动 IntentService?
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    

      IntentService 源码中的 onBind() 默认返回 null;不适合 bindService() 启动服务,如果你执意要 bindService() 来启动 IntentService,可能因为你想通过 Binder 或 Messenger 使得 IntentService 和 Activity 可以通信,这样那么 onHandleIntent() 不会被回调,相当于在你使用 Service 而不是 IntentService。

    2).为什么多次启动 IntentService 会顺序执行事件,停止服务后,后续的事件得不到执行?

      IntentService 中使用的 Handler、Looper、MessageQueue 机制把消息发送到线程中去执行的,所以多次启动 IntentService 不会重新创建新的服务和新的线程,只是把消息加入消息队列中等待执行,而如果服务停止,会清除消息队列中的消息,后续的事件得不到执行。

    站在巨人的肩膀上摘苹果:
    IntentService 示例与详解
    Service 和 IntentService 的区别

    相关文章

      网友评论

      本文标题:Android中的线程形态(二)(HandlerThread/I

      本文链接:https://www.haomeiwen.com/subject/nkgunttx.html