现在的位置: 首页 > 移动开发 > Android > 正文

Android 源码系列之<七>从源码的角度深入理解IntentService及HandlerThread

2016年05月29日 Android ⁄ 共 11311字 ⁄ 字号 暂无评论

        转载请注明出处:http://blog.csdn.net/llew2011/article/details/51373243

        提起Service大家都很熟悉,它乃Android四(si)大(da)组(jing)件(gang)之一。但是说起IntentService有童靴或许有点陌生,看名字感觉和Service有关连。不错,不仅有关联而且关系还不一般,IntentService是Service的子类,所以它也是正宗的Service,由于IntentService借助了HandlerThread,我们今天就从源码的角度巴拉一下IntentService及HandlerThread,看看它们是何方神圣,如果你对它们非常熟悉,请跳过本文(*^__^*)
……

        开始巴拉IntentService源码之前我们先看看它的基本用法,既然IntentService是正宗的Service,那它的用法就和Service一样。IntentService也是一个抽象类,需要实现其抽象方法onHandleIntent()。我们先定义BackgroundService,使之继承IntentService并实现其抽象方法onHandleIntent(),然后重写IntentService的生命周期方法并打印日志,代码如下:

public class BackgroundService extends IntentService {

	private static final String TAG = BackgroundService.class.getSimpleName();

	public BackgroundService() {
		super("TT");
		Log.e(TAG, "BackgroundService()    " + Thread.currentThread());
	}

	@Override
	public void onCreate() {
		Log.e(TAG, "onCreate()             " + Thread.currentThread());
		super.onCreate();
	}

	@Override
	public void onStart(Intent intent, int startId) {
		Log.e(TAG, "onStart()              " + Thread.currentThread());
		super.onStart(intent, startId);
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.e(TAG, "onStartCommand()       " + Thread.currentThread());
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public void onDestroy() {
		Log.e(TAG, "onDestroy()            " + Thread.currentThread());
		super.onDestroy();
	}

	@Override
	public IBinder onBind(Intent intent) {
		Log.e(TAG, "onBind()               " + Thread.currentThread());
		return super.onBind(intent);
	}

	@Override
	protected void onHandleIntent(Intent intent) {
		Log.e(TAG, "onHandleIntent()       " + Thread.currentThread());
	}
}

        我们在重写的部分方法中添加了日志,主要打印当前方法名和方法执行时所在的线程名称。然后在配置文件manifest.xml中配置BackgroundService,代码如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.llew.wb.source.intentservice"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.llew.wb.source.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name="com.llew.wb.source.BackgroundService" >
        </service>
    </application>

</manifest>

        最后在MainActivity的布局文件activity_layout.xml中添加一个button按钮,该按钮用来启动BackgroundService。布局文件代码如下:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:onClick="startService"
        android:text="测试IntentService" />

</FrameLayout>

        定义完布局文件后,在MainActivity中添加startIntentService()方法,代码如下:

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}

	public void startService(View view) {
		Intent action = new Intent(this, BackgroundService.class);
		startService(action);
	}
}

        OK,一切就绪,我们运行一下看看打印结果。运行程序,输出结果如下图所示:

        根据打印结果可以确定IntentService的生命周期函数执行顺序是onCreate()→onStartCommon()→onStart()→onHandleIntent()→onDestroy()。再观察打印的线程信息发现只有onHandleIntent()的线程信息和其他函数的线程信息是不同的,也就是说onHandleIntent()所在的线程和其他函数所在的不是同一个线程,我们知道其他函数都是在主线程中执行的。所以onHandleIntent()的执行是在子线程中进行的。还有一点onDestroy()方法是在onHandleIntent()方法执行结束后才执行,因为我们并没有主动的调用停止Service的相关方法,所以可以猜测使IntentService停止的操作一定是在和onHandleIntent()方法所在的线程中操作的。

        基于猜测,我们继续做实验,既然onHandleIntent()方法是在子线程中执行的,那我们就可以利用线程休眠来模拟后台比较耗时的操作,修改onHandleIntent()方法,代码如下:

@Override
protected void onHandleIntent(Intent intent) {
	Log.e(TAG, "onHandleIntent()       " + Thread.currentThread());
	try {
		Thread.sleep(3000);
		Log.e(TAG, "sleep finish           " + Thread.currentThread());
	} catch (Exception e) {
		e.printStackTrace();
	}
}

        在onHandleIntent()方法中让其所在的线程休眠了3秒钟,然后运行程序,输出结果如下:

        根据输出结果看到,在onHandleIntent()方法中先是打印了第一句log,等待3秒钟后又把第二句log内容打印出来了,log打印完之后是执行Service的onDestroy()方法。我们继续做实现,刚刚只是在onHandleIntent()的方法中模拟做了一个耗时任务,现在我们启动多个IntentService,每一次启动时都传递进来一个参数来表示每一个任务,继续修改startService()方法,代码如下:

public void startService(View view) {
	Intent action1 = new Intent(this, BackgroundService.class);
	action1.putExtra("params", "task 1");
	startService(action1);
	
	Intent action2 = new Intent(this, BackgroundService.class);
	action2.putExtra("params", "task 2");
	startService(action2);
	
	Intent action3 = new Intent(this, BackgroundService.class);
	action3.putExtra("params", "task 3");
	startService(action3);
	
}

        我们在startService()方法中启动了3次BackgroundService,并在启动时传递了参数。然后修改onHandleIntent()方法,代码如下:

@Override
protected void onHandleIntent(Intent intent) {
	String params = intent.getStringExtra("params");
	Log.e(TAG, params + " in onHandleIntent()   " + Thread.currentThread());
	try {
		Thread.sleep(3000);
		Log.e(TAG, params + " is finished           " + Thread.currentThread());
	} catch (Exception e) {
		e.printStackTrace();
	}
}

        运行程序,日志打印结果如下图所示:

        观察输出结果发现onHandleIntent()的执行是有序的,当所有的模拟耗时任务都结束后该Service才销毁。也就是说我们可以方便的使用IntentService来执行一些有序的并且非常耗时的异步操作,当所有的任务都执行完毕后该IntentService会主动销毁自己,我们无需关心IntentService的销毁。

        好了,现在我们清楚了IntentService的执行流程,那接下来我们就从源码的角度来巴拉一下IntentService,看看其内部流程,首先看一下官网对其的说明:

        IntentService是一个继承Service的用来处理异步请求的类,客户端通过调用startService(Intent)发送请求,Service服务就会在必要的时候启动然后在工作线程中依次处理每一个Intent,当工作线程执行完毕后Service服务就关闭自己。

        这个"工作队列处理器"是将任务从一个应用的主线程中做分离的最常用的模式,IntentService类就是该模式的经典代表。为了使用IntentService需要先继承IntentService然后实现其抽象方法onHandleIntent(),它在工作线程中接收发送来的所有Intent并在适当的时候结束自己。

        所有的请求都会在一个单一的工作线程中被接收,工作线程可以随意耗时而不会阻塞主线程,但是在同一时刻只能处理一个请求。

        知晓了IntentService的说明后我们继续往下看代码,首先看一下IntentService的定义的成员变量有哪些,代码如下:

// 提供消息队列和
private volatile Looper mServiceLooper;
// 处理消息
private volatile ServiceHandler mServiceHandler;
// 表示工作线程的名字
private String mName;
// 是否重新发送Intent
private boolean mRedelivery;

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

        IntentService中仅仅定义了4个成员变量,其中Looper类型的mServiceLooper童靴们应该很熟悉了(不熟悉也无妨我会在后续文章中从源码的角度出发讲解Android消息机制之Handler, Looper, Message和MessageQueue等),还定义了ServiceHandler类型的mServiceHandler,ServiceHandler继承Handler,并在handleMessage()方法中调用了抽象方法onHandleIntent()方法和结束Service的stopSelf()方法。

        了解完IntentService的成员变量后我们紧接着看一下构造方法,源码如下:

/**
 * Creates an IntentService.  Invoked by your subclass's constructor.
 *
 * @param name Used to name the worker thread, important only for debugging.
 */
public IntentService(String name) {
    super();
    mName = name;
}

        IntentService的构造方法要求必须传递进来一个String类型的值(该值表示的是工作线程的名称),把name赋值给了其成员变量mName。看完了构造方法后接着看IntentService的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);
}

        在onCreate()方法中首先调用了父类的onCreate()方法,接着创建了一个HandlerThread的实例thread,看到这里或许有的童靴会有疑问了,HandlerThread又是何方神圣了?不必担心,我们进入HandlerThread的源码看看它到底是何方神圣,其源码如下:

/**
 * Handy class for starting a new thread that has a looper. The looper can then be 
 * used to create handler classes. Note that start() must still be called.
 */
public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    
    /**
     * Call back method that can be explicitly over ridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }

    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;
    }
    
    /**
     * Ask the currently running looper to quit.  If the thread has not
     * been started or has finished (that is if {@link #getLooper} returns
     * null), then false is returned.  Otherwise the looper is asked to
     * quit and true is returned.
     */
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
    
    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}

 
      哦,看完HandlerThread的源码我们就可以放心了,原来HandlerThread继承自Thread,那也就是说HandlerThread也是一个正宗的线程类。HandlerThread类有三个成员变量,mPriority表示当前线程的优先级(默认值为0),mTid表示线程的标识符,mLooper的作用为当前线程添加消息队列并循环读取消息。

        HandlerThread的成员变量了解之后,看一下其重写的run()方法,在run()方法中先调用Process.myTid()给mTid赋值,接着调用Looper.prepare()方法为当前线程创建一个消息队列,创建完消息队列后为成员变量mLooper赋值并唤醒可能处于等待状态的锁机制,紧接着又设置了当前线程的优先级,最后进入Looper.loop()的方法中。

        总的来看HandlerThread核心就是对外提供一个带有Looper功能的线程,当我们创建完HandlerThread实例之后要立即调用其start()方法,如果不调用start()方法,当我们需要使用HandlerThread中的Looper时该线程就会处于挂起状态,因为在调用HandlerThread实例对象的getLooper()方法时,如果当前线程是isAlive()并且mLooper为null,那么该线程就将一直wait()下去,所以在创建完HandlerThread后要立即调用其start()方法。

        看完HandlerThread源码后我们接着看IntentService的onCreate()方法,在该方法中实例化了一个HandlerThread类型的thread,紧接着调用该thread的start()方法,然后再调用thread的getLooper()方法为mServiceLooper赋值,最后利用mServiceLooper完成mServiceHandler的初始化工作。

       看完IntentService的onCreate()方法,接着看onStartCommand()方法,源码如下:

/**
 * You should not override this method for your IntentService. Instead,
 * override {@link #onHandleIntent}, which the system calls when the IntentService
 * receives a start request.
 * @see android.app.Service#onStartCommand
 */
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

        onStartCommand()函数中调用了onStart()函数,然后通过对mRedelivery的判断决定返回START_REDELIVER_INTENT或START_NOT_STICKY,需要注意在onStartCommand()函数中只能返回以下三种类型中的一种:

  • START_NOT_STICKY
            如果该服务在onStartCommand()方法返回后被系统杀死,那么知道接收到新的Intent对象,该服务才会重新创建。这是安全的选项,用来避免在你不需要的时候运行里的服务。
  • START_STICKY
            如果该服务在onStartCommand()方法返回后被系统杀死,那么系统就会重新创建这个服务并且尝试调用onStartCommand()方法,但是系统不会重新传递最后的Intent对象,系统会用一个null的Intent对象来调用onStartCommand()方法。在这个情况下除非有一些被发送的Intent对象在等待启动服务。这适用于不执行命令的媒体播放器(或类似的服务),它只是无限期的运行着并等待工作的到来。
  • START_REDELIVER_INTENT
            如果该服务在onStartCommand()方法返回后被系统杀死,那么系统会重新创建这个服务,并且用发送给这个服务的最后一个Intent对象来调用onStartCommand()方法。任意等待的Intent对象会依次被发送,这适合于那些应该立即恢复正在执行工作的服务,例如下载文件。

        然后我们看一下onStart()方法,源码如下:

@Override
public void onStart(Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}

        在onStart()方法中利用mServiceHandler发送消息并把传递进来的Intent等参数也一同打包发送。该消息最后在文章开头看到的在handleMessage()方法中被处理:

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

        handleMessage()方法中调用了onHandleIntent()方法,等其执行完毕后调用stopSelf()方法关闭服务。

        总的来说IntentService适合在后台执行比较耗时的,有序的异步操作并且无需我们关心何时结束该服务。HandlerThread不仅是标准的Thread而且对外提供了Looper功能,需要注意的是当创建了HandlerThread后需要立即执行其start()方法,否则该线程可能一直处于挂起状态。

        好了,到这里有关IntentService和HandlerThread的讲解就告一段落,感谢观看。