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开发经验的积累,自己也看过不少的源码了。以后会逐渐分享出来。

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









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

相关文章推荐

Prometheus 实战于源码分析之API与联邦

在进行源码讲解关于prometheus还有一些配置和使用,需要解释一下。首先是API的使用,prometheus提供了一套HTTP的接口curl http://localhost:9090/api/v...

springMVC源码分析--HttpMessageConverter数据转化(一)

之前的博客我们已经介绍了很多springMVC相关的模块,接下来我们介绍一下springMVC在获取参数和返回结果值方面的处理。虽然在之前的博客老田已经分别介绍了参数处理器和返回值处理器:(1)spr...

Tomcat源码分析之Connector

在客户端访问tomcat的时候有一层叫做connector的东西,这其实是处理来自于客户端的协议,左边的coyote connector是对HTTP/1.1协议处理,右边的jk connector是对...

java源码分析系列一 线程池Executors

用了线程池已经有一段时间了,以前只是偶尔看看源码,了解了其中调度策略,没有深入研究。因为平常没有遇见什么问题。但是作为一个程序员要严格要求自己,做到未雨绸缪废话不说了,开始我们的源码之旅!      ...

jquery 源码分析5-ajax()ajax原理,可以加载的类型

jquery 源码分析5-ajax()ajax原理,可以加载的类型很多框架都有自己的一套ajax,其实都是封装了浏览器器提供的对像XMLHttpRequest ,XMLHttpRequest从发起一个...

java HashMap的插入操作源码分析

基础背景知识: HashMap的储存结构:采用链表储存 每一个节点的数据结构: final K key;V value;EntryK,V> next;final int hash...

ArrayList与LinkedList源码分析

本文要解决的问题: 通过对ArrayList与LinkedList的源码进行分析,以便对这两种集合有更加深入的理解。 ArrayList 也叫数组列表,底层使用的数组实现的,严格来说是动态数...

RxJava(一) create操作符的用法和源码分析

RxJava create操作符用法和源码分析 create操作符的基本使用顾名思义,Create操作符是用来创建一个Observable的。下面来看一个简单的代码段:

Openstack liberty源码分析 之 云主机的启动过程1

接触Openstack也有一段时间了,因为工作需要着重阅读了Glance、Nova、Cinder模块源码并通过搭建的devstack测试环境调试学习相关操作的执行流程。现准备陆续将相关的学习成果和心得...

Android View事件传递机制-源码分析

先通过做个一个Demo打印一下Log看看 View的事件传递机制,以下两个View的布局及具体代码: <RelativeLayout xmlns:android="http://schemas.an...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

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