IntentServices源码分析

原创 2015年11月18日 22:28:59

最近工作实在是太忙了。有很久没有更新博客。在我看来,写博客的目的是为了记录自己学习的过程,通过写下来的方式,加深印象。今天为啥要分析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开发经验的积累,自己也看过不少的源码了。以后会逐渐分享出来。

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









版权声明:本文为博主原创文章,未经博主允许不得转载。

Java字符串:StringBuffer使用详解及源码分析

1 使用方法  StringBuffer和StringBuilder功能基本相同,他们的区别在于StringBuffer是线程安全的而StringBuilder不是线程安全的。他们的关系和HashMa...

Mangos源码分析(9):服务器公共组件实现之环形缓冲区

消息队列锁调用太频繁的问题算是解决了,另一个让人有些苦恼的大概是这太多的内存分配和释放操作了。频繁的内存分配不但增加了系统开销,更使得内存碎片不断增多,非常不利于我们的服务器长期稳定运行。也许我们可以...

Weka算法Classifier-tree-RandomForest源码分析(二)代码实现

Weka算法Classifier-tree-RandomForest源码分析(二)代码实现

Android中从源码分析关于AsyncTask的使用

Android在框架层提供了异步任务类,AysncTask,用于执行后台任务,并将执行结果更新到UI线程。为什么要用异步任务呢?这是因为如果某个任务太耗时间的话,会阻塞UI主线程,而我们知道UI线程如...

Guava缓存器源码分析——CacheBuilder

CacheBuilder作为LoadingCache 与 Cache实例的创建者,具有以下特征: 1、自动载入键值至缓存;         2、当缓存器溢出时,采用最近最少使用原则进行替换。...

spring boot实战(第十篇)Spring boot Bean加载源码分析

前言 前面的文章描述了Application对应Bean的创建,本篇将阐述spring boot中bean的创建过程 refresh 首先来看SpringApplication#run方法中...

gdb和gdbserver源码架构分析

http://www.cnblogs.com/linucos/archive/2013/03/20/2971287.html

微信(WeChat)电脑端多开分析+源码

0x00 前言不知道大家有没有多个微信号,我反正有一两三个。现在电脑端微信使用频率也比较高,主要用于大文件传输,或者手机电脑文件互传等等,除了不能收红包和看朋友圈,貌似电脑端没其他毛病。哦,还有个毛病...
  • angelxf
  • angelxf
  • 2017年05月14日 16:07
  • 2685

【erlang学习 】开源nosql kai 的源码分析

由于工作关系,上司要每个月交一份小论文。这个月我分到了一个叫 KAI的数据库,分析下咯。 KAI                                          ...
  • dp0304
  • dp0304
  • 2012年04月06日 12:01
  • 3404

owncloud源码分析1--部分修改Demo

最近做了修改owncloud云盘的项目。 owncloud本身是一个私有云盘,用到本地目录,项目要求是存储到FTP后转存到FAST,所以需要修改很多相关的东西。 一、首先是进入文件列表时的选择根目录,...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:IntentServices源码分析
举报原因:
原因补充:

(最多只允许输入30个字)