IntentService学习-Service/Handler相关

前言

Service服务是Android四大组件之一,在Android中有着举足重轻的作用。Service服务是工作的UI线程中,当你的应用需要下载一个文件或者播放音乐等长期处于后台工作而有没有UI界面的时候,你肯定要用到Service+Thread来实现。因此你需要自己在Service服务里面实现一个Thread工作线程来下载文件或者播放音乐。然而你每次都需要自己去写一个Service+Thread来处理长期处于后台而没有UI界面的任务,这样显得很麻烦,没必要每次都去构建一个Service+Thread框架处理长期处于后台的任务。Google工程师给我们构建了一个方便开发者使用的这么一个框架---IntentService(Service+Handler(Handler为HandlerThread的looper创建))

范例

IntentService本质也是Service,需要在AndroidManifest里面声明,并且创造一个无参的默认构造函数

//一个构造函数是必须的,并且你必须调用父类的IntentService(String)以传入工作线程的名字

public class TestIntentService extends IntentService {

    public TestIntentService(){
        super("TestIntentService");
    }

    public TestIntentService(String name) {
        super(name);
    }

    @Override
    public void onDestroy() {
        Log.e("weijuncheng","onDestory called");
        super.onDestroy();
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Log.e("weijuncheng","onStartCommand called, intent = "+intent);
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
         int key = intent.getIntExtra("key",0);
         String task = intent.getStringExtra("value");
         switch (key){
             case 1:
                 //模拟耗时任务1
                 try {
                     Thread.sleep(3 * 1000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 break;
             case 2:
                 //模拟耗时任务2
                 try {
                     Thread.sleep(3 * 1000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 break;
         }
         Log.e("weijuncheng", "\nthe current time is: " + System.currentTimeMillis()/1000
                + "\nthe Thread id is " + Thread.currentThread().getId()
                + "\nthe current task is " + task);
    }
}
public class MainActivity extends AppCompatActivity {

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

        Intent intent1 = new Intent(this,TestIntentService.class);
        intent1.putExtra("key",1);
        intent1.putExtra("value","task 1");
        startService(intent1);

        Intent intent2 = new Intent(this,TestIntentService.class);
        intent2.putExtra("key",2);
        intent2.putExtra("value","task 2");
        startService(intent2);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.test.weijuncheng.testintentservice">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        </service>
    </application>

</manifest>

如果没有声明Service会出现

Unable to start service Intent { cmp=com.test.weijuncheng.testintentservice/.TestIntentService (has extras) } U=0: not found;因为一开始Service是从AndroidManifest里面查找

相应log为

01-01 02:19:15.023   998   998 E weijuncheng: onStartCommand called, intent = Intent { cmp=com.test.weijuncheng.testintentservice/.TestIntentService (has extras) }
01-01 02:19:15.026   998   998 E weijuncheng: onStartCommand called, intent = Intent { cmp=com.test.weijuncheng.testintentservice/.TestIntentService (has extras) }
01-01 02:19:15.206  1268  1290 I ActivityManager: Displayed com.test.weijuncheng.testintentservice/.MainActivity: +533ms
01-01 02:19:18.024   998  1034 E weijuncheng: 
01-01 02:19:18.024   998  1034 E weijuncheng: the current time is: 1230776358
01-01 02:19:18.024   998  1034 E weijuncheng: the Thread id is 690
01-01 02:19:18.024   998  1034 E weijuncheng: the current task is task 1
01-01 02:19:21.027   998  1034 E weijuncheng: 
01-01 02:19:21.027   998  1034 E weijuncheng: the current time is: 1230776361
01-01 02:19:21.027   998  1034 E weijuncheng: the Thread id is 690
01-01 02:19:21.027   998  1034 E weijuncheng: the current task is task 2
01-01 02:19:21.030   998   998 E weijuncheng: onDestory called

IntentService源码

63public abstract class IntentService extends Service {
64    private volatile Looper mServiceLooper;
65    private volatile ServiceHandler mServiceHandler;
66    private String mName;
67    private boolean mRedelivery;
68
69    private final class ServiceHandler extends Handler {
70        public ServiceHandler(Looper looper) {
71            super(looper);
72        }
73
74        @Override
75        public void handleMessage(Message msg) {
76            onHandleIntent((Intent)msg.obj);
77            stopSelf(msg.arg1);
78        }
79    }
80
81    /**
82     * Creates an IntentService.  Invoked by your subclass's constructor.
83     *
84     * @param name Used to name the worker thread, important only for debugging.
85     */
86    public IntentService(String name) {
87        super();
88        mName = name;
89    }
90
91    /**
92     * Sets intent redelivery preferences.  Usually called from the constructor
93     * with your preferred semantics.
94     *
95     * <p>If enabled is true,
96     * {@link #onStartCommand(Intent, int, int)} will return
97     * {@link Service#START_REDELIVER_INTENT}, so if this process dies before
98     * {@link #onHandleIntent(Intent)} returns, the process will be restarted
99     * and the intent redelivered.  If multiple Intents have been sent, only
100     * the most recent one is guaranteed to be redelivered.
101     *
102     * <p>If enabled is false (the default),
103     * {@link #onStartCommand(Intent, int, int)} will return
104     * {@link Service#START_NOT_STICKY}, and if the process dies, the Intent
105     * dies along with it.
106     */
107    public void setIntentRedelivery(boolean enabled) {
108        mRedelivery = enabled;
109    }
110
111    @Override
112    public void onCreate() {
113        // TODO: It would be nice to have an option to hold a partial wakelock
114        // during processing, and to have a static startService(Context, Intent)
115        // method that would launch the service & hand off a wakelock.
116
117        super.onCreate();
118        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
119        thread.start();
120
121        mServiceLooper = thread.getLooper();
122        mServiceHandler = new ServiceHandler(mServiceLooper);
123    }
124
125    @Override
126    public void onStart(@Nullable Intent intent, int startId) {
127        Message msg = mServiceHandler.obtainMessage();
128        msg.arg1 = startId;
129        msg.obj = intent;
130        mServiceHandler.sendMessage(msg);
131    }
132
133    /**
134     * You should not override this method for your IntentService. Instead,
135     * override {@link #onHandleIntent}, which the system calls when the IntentService
136     * receives a start request.
137     * @see android.app.Service#onStartCommand
138     */
139    @Override
140    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
141        onStart(intent, startId);
142        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
143    }
144
145    @Override
146    public void onDestroy() {
147        mServiceLooper.quit();
148    }
149
150    /**
151     * Unless you provide binding for your service, you don't need to implement this
152     * method, because the default implementation returns null.
153     * @see android.app.Service#onBind
154     */
155    @Override
156    @Nullable
157    public IBinder onBind(Intent intent) {
158        return null;
159    }
160
161    /**
162     * This method is invoked on the worker thread with a request to process.
163     * Only one Intent is processed at a time, but the processing happens on a
164     * worker thread that runs independently from other application logic.
165     * So, if this code takes a long time, it will hold up other requests to
166     * the same IntentService, but it will not hold up anything else.
167     * When all requests have been handled, the IntentService stops itself,
168     * so you should not call {@link #stopSelf}.
169     *
170     * @param intent The value passed to {@link
171     *               android.content.Context#startService(Intent)}.
172     *               This may be null if the service is being restarted after
173     *               its process has gone away; see
174     *               {@link android.app.Service#onStartCommand}
175     *               for details.
176     */
177    @WorkerThread
178    protected abstract void onHandleIntent(@Nullable Intent intent);
179}

IntentService源码分析

首先观察IntentService的onCreate

111    @Override
112    public void onCreate() {
113        // TODO: It would be nice to have an option to hold a partial wakelock
114        // during processing, and to have a static startService(Context, Intent)
115        // method that would launch the service & hand off a wakelock.
116
117        super.onCreate();
118        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
119        thread.start();
120
121        mServiceLooper = thread.getLooper();
122        mServiceHandler = new ServiceHandler(mServiceLooper); //根据HandlerThread中的looper创建一个Handler
123    }

其中用到了HandlerThread

22/**
23 * Handy class for starting a new thread that has a looper. The looper can then be
24 * used to create handler classes. Note that start() must still be called.
25 */
26public class HandlerThread extends Thread {

参考 Android HandlerThread 总结使用

HandlerThread本质上就是一个普通Thread,只不过内部建立了Looper,普通线程默认是不建立looper的

HandlerThread的关键点

55    @Override
56    public void run() {
57        mTid = Process.myTid();
58        Looper.prepare(); //准备循环条件
59        synchronized (this) { //持有锁机制来获得当前线程的Looper对象
60            mLooper = Looper.myLooper();    //发出通知,当前线程已经创建mLooper对象成功,这里主要是通知getLooper方法中的wait
61            notifyAll(); 
62        }
63        Process.setThreadPriority(mPriority);
64        onLooperPrepared();  //该方法实现体是空的,子类可以实现该方法,作用就是在线程循环之前做一些准备工作,当然子类也可以不实现。
65        Looper.loop();  //启动loop
66        mTid = -1;
67    }
69    /**
70     * This method returns the Looper associated with this thread. If this thread not been started
71     * or for any reason isAlive() returns false, this method will return null. If this thread
72     * has been started, this method will block until the looper has been initialized.
73     * @return The looper.
74     */
75    public Looper getLooper() {
76        if (!isAlive()) { //如果线程不是存活的,则直接返回null
77            return null;
78        }
79
80        // If the thread has been started, wait until the looper has been created.
81        synchronized (this) {
82            while (isAlive() && mLooper == null) {
83                try {
84                    wait(); //如果线程已经启动,但是Looper还未创建的话,就等待,直到Looper创建成功
85                } catch (InterruptedException e) {
86                }
87            }
88        }
89        return mLooper;
90    }

ServiceHandler是IntentService的内部类,在重写消息处理方法handlerMessage里面调用了onHandlerIntent抽象方法去处理异步任务intent的请求,当异步任务请求结束之后,调用stopSelf方法自动结束IntentService服务


69    private final class ServiceHandler extends Handler {
70        public ServiceHandler(Looper looper) {
71            super(looper);
72        }
73
74        @Override
75        public void handleMessage(Message msg) {
76            onHandleIntent((Intent)msg.obj);
77            stopSelf(msg.arg1);
78        }
79    }

一直知道stopself是停掉Service的方法,但是却不知道什么时候停止。以为调用了stopself就会马上停止,实际上我错了。

在onStartCommond方法里面调用stopself方法时,不会马上停止,而是onStartCommond方法执行结束才会停止。

还有一点,调用stopself方法之后,service会执行onDestory方法。

另外,如果onStartCommond中启动一个线程,调用stopself,线程也不会被杀死

145    @Override
146    public void onDestroy() {
147        mServiceLooper.quit();
148    } 

looper quit,让当前工作线程HandlerThread退出当前Looper循环,进而结束线程。进而结束当前IntentService服务

参考:
Android IntentService的使用和源码分析

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容