关闭

IntentServices源码分析

标签: IntentServices源码HandlerThread线程Services
188人阅读 评论(0) 收藏 举报
分类:

最近工作实在是太忙了。有很久没有更新博客。在我看来,写博客的目的是为了记录自己学习的过程,通过写下来的方式,加深印象。今天为啥要分析Intentservices呢,其实做android开发

以来我都没有使用过Intentservices。但是我在其他的一些开源项目中看到的,刚刚和项目中的使用场景类似,于是就更进一步,分析一下原理。


在分析之前,我们要搞清楚两个问题。

1. services和thread到底是什么关系?我的结论是没有关系。之所以有人认为他们之间有关系,是因为我们经常会说services在后台运行,以为services就能干一些耗时的操作,其实android中的

四大组件都运行于主线程,要处理一些耗时操作,同样需要开启子线程进行处理。那有人就会说,那为啥不直接创建子线程呢。我觉得services作为四大组件之一,非常便于和activity通信,同时生命周期我们也可以控制,之所以说后台,是指services运行时,没有界面。

2. 如果我们有一个大任务需要分解成多个小任务,每个小任务都需要按顺序执行。对于这种需求,我们的常规思路就是创建多个线程,挨个执行。另外一种思路就是在一个线程中,顺序执行任务逻辑代码。


对于以上提出的两个问题。IntentServices可以完美解决我们的需求。IntentServices有哪些优点呢。

1. IntentServices使用方法和普通的services使用方法是一样的。要么通过startService或者bindService。但是startServices启动多次,每次都可以对应一个任务。按照启动的顺序,挨着触发回调方法onHandleIntent(Intent),我们可以通过传递过来的Intent来区分不同任务。这样就可以达到按顺序执行目的了。

2. IntentServices执行完毕以后,能自我销毁。



基本的使用方法太过简单了,只要是做过android开发的,我相信都能明白了。我这里就不再举例了。接下来,重点分析IntentServices原理吧。

我们首先看看onCreate方法吧。

 @Override
    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

首先是创建了HandlerThread的线程对象。这个HandlerThread是个什么鬼呢。这个类的代码不长,我们可以分析一下。原来是Thread的一个子类。对于我们来说,我们重点看两个方法即可。

 @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    
    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread 
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
调用start方法以后就会执行run方法。其中 Looper.prepare();是创建当前线程的looper对象。常规思路就是创建线程的looper对象以后,就是调用 Looper.loop();进行消息循环。可是为啥还有同步代码块呢。主要原因是在IntentService的onCreate方法中,thread.start()以后,直接调用thread.getLooper()这个时候,HandlerThread的线程run方法可能还没有执行到 Looper.prepare(),或者是说没有执行完。这个时候getLooper肯定是返回null了。为了解决这种时间差的问题,就添加了同步代码块的处理了。说白了HandlerThread就是一个线程而已。(这一块的理解,可能需要读者对Handler、MessageQueue、Looper有一定的理解)。

那我们回去继续看onCreate方法。在第9行获取到在HandlerThread线程对应的Looper对象。用这个Looper对象创建Handler对象,并且重写handleMessage方法。我们看看onstart方法。这个方法的实现代码,我们太熟悉了。就是通过handler发送message。由于在HandlerThread的run方法中已经进入消息循环。这时,我们不妨看看Looper中looper方法吧。

public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }
重点看第27行代码。由于当前的loop方法是在HandlerThread线程中调用的,那么msg.target.dispatchMessage(msg)这一句肯定也是在HandlerThread线程中的。具体dispatchMessage方法中message是如何分发,今天先不讲,但最终会调用重写的handleMessage方法。在这个方法中看到了我们前面提到的使用IntentServices优点,在这里得到了体现。

 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);
        }
    }

如果我们要处理一下耗时操作,我们就可以重写onHandleIntent方法,其实intent参数就是我们startservices的intent。这样就达到顺序执行多个任务。并且通过stopSelf结束自己。


总结:经过IntentServices源码简单,但是通过阅读代码,还是有收获的,而且加深了对IntentServices的理解。随着android开发经验的积累,自己也看过不少的源码了。以后会逐渐分享出来。

还是那句话,代码是最好的学习方式。









0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:15643次
    • 积分:557
    • 等级:
    • 排名:千里之外
    • 原创:42篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条