关于Service,IntentService的理解

1. 生命周期 常用方法

  • 官方说明图
image

在Service的生命周期里,常用的有:

4个手动调用的方法

手动调用方法 作用
startService() 启动服务
stopService() 关闭服务
bindService() 绑定服务
unbindService() 解绑服务

内部自动调用的方法 作用
onCreat() 创建服务
onStartCommand() 开始服务
onDestroy() 销毁服务
onBind() 绑定服务
onUnbind() 解绑服务

使用

  • startService
public class MyService extends Service {
    /**
     * 创建参数
     */

    int count;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.w("TAG", "---onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        /** 创建一个线程,每秒计数器加一,并在控制台进行Log输出 */
        //耗时操作  不用再单独的开启线程
        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(1000);
                        count++;
                        Log.w("TAG", "Count is" + count);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);


    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.w("TAG", "---onDestroy");

    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

Intent intent = new Intent(MainActivity.this, MyService.class);
startService(intent);
在需要停止的時候調用stopService

  • bindService (一定要记着这个是要获得,链接的对象) 可用于和Activity之間的通訊,BindService可以在Activity里面获得Service的实例,绑定获取实例是异步的过程

bindService启动流程

context.bindService() ——> onCreate() ——> onBind() ——> Service running ——> onUnbind() ——> onDestroy() ——> Service stop

onBind()将返回给 客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service的实例、运行状态或其他操作。这个时候把调用者 (Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用 onUnbind->onDestroy相应退出。

所以调用bindService的生命周期为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind --> onDestory。

在Service每一次的开启关闭过程中,只有onStart可被多次调用(通过多次startService调用),其他onCreate,onBind,onUnbind,onDestory在一个生命周期中只能被调用一次。


/** 通过bindService和unBindSerivce的方式启动和结束服务 */
public class MyBindService extends Service {
    /**
     * 进度条的最大值
     */
    public static final int MAX_PROGRESS = 100;
    /**
     * 进度条的进度值
     */
    private int progress = 0;

    /**
     * 更新进度的回调接口
     */
    private OnProgressListener onProgressListener;

    public interface OnProgressListener {
        void onProgress(int progress);
    }

    /**
     * 注册回调接口的方法,供外部调用
     *
     * @param onProgressListener
     */
    public void setOnProgressListener(OnProgressListener onProgressListener) {
        this.onProgressListener = onProgressListener;
    }

    /**
     * 增加get()方法,供Activity调用
     *
     * @return 下载进度
     */
    public int getProgress() {
        return progress;
    }

    /**
     * 模拟下载任务,每秒钟更新一次
     */
    public void startDownLoad() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (progress < MAX_PROGRESS) {
                    progress += 5;
                    //进度发生变化通知调用方,需要和切换到主线程
                    if (onProgressListener != null) {
                        Log.w("TAG", "---thread"+Thread.currentThread().getName());
                        onProgressListener.onProgress(progress);
                    }

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        }).start();
    }


    /**
     * 返回一个Binder对象
     */
    @Override
    public IBinder onBind(Intent intent) {
        Log.w("TAG", "---onBind");
        return new MsgBinder();
    }

    //此方法是为了可以在Acitity中获得服务的实例   
    public class MsgBinder extends Binder {
        /**
         * 获取当前Service的实例
         *
         * @return
         */
        public MyBindService getService() {
            return MyBindService.this;
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.w("TAG", "---onCreate");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        /** 服务停止时 */
        Log.w("TAG", "---onDestroy");

    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.w("TAG", "---onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }
}

在Activity使用

/** 进入Activity开始服务 */
  Intent intent = new Intent(MainActivity.this, MyBindService.class);
           bindService(intent, conn, Context.BIND_AUTO_CREATE);


ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {

        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.w("TAG", "onServiceConnected---------");
            //返回一个MsgService对象
            msgService = ((MyBindService.MsgBinder) service).getService();
            // 绑定成功后获取到服務开始下载

            msgService.startDownLoad();
            //注册回调接口来接收下载进度的变化
            msgService.setOnProgressListener(new MyBindService.OnProgressListener() {
                @Override
                public void onProgress(int progress) {
                    Log.w("TAG", "progress---------" + progress);
                    Log.w("TAG", "---thread----" + Thread.currentThread().getName());

                }
            });

        }
    };

    protected void onDestroy() {
        super.onDestroy();
        this.unbindService(conn);
        Log.v("MainStadyServics", "out");
    }

IntentService

public class MyIntentService extends IntentService {
    private static final String TAG = "TAG";

    public MyIntentService() {
        super("MyIntentService");
        Log.d(TAG, "MyIntentService: " + Thread.currentThread().getName());

    }


    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {

        Log.d(TAG, "onStart: ");
        super.onStart(intent, startId);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        //耗时操作  不用再单独的开启线程
        Log.d(TAG, "MyIntentService: " + Thread.currentThread().getName());
        String path = intent.getStringExtra("path");
        downloadTask(path);

    }

    private void downloadTask(String path) {
        Log.d(TAG, "downloadTask: ");
        try {
            Thread.sleep(3 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    /**
     * 当某个请求需要处理时,这个方法会在工作者线程被调用
     * 一次仅仅会有一个请求被处理,但是处理过程会运行在工作者线程(独立于其他应用程序逻辑运行)。
     * 因此,如果某段代码需要执行很长时间,它会阻塞住其他提交到该IntentService的请求
     * ,但是不会阻塞住其他任何东西。当所有的请求被处理完成之后,IntentService会停止它自身,
     * 因此你不应该手动调用stopSelf()方法。
     */

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy: ");
        super.onDestroy();
    }

}

使用

  Intent intent = new Intent(MainActivity.this, MyIntentService.class);
                intent.putExtra("path", "http://www:xxx.xxx");
                startService(intent);
                startService(intent);
                startService(intent);

//执行结果
D/TAG: MyIntentService: main

D/TAG: onStartCommand: 
D/TAG: onStart: 
D/TAG: MyIntentService: IntentService[MyIntentService
D/TAG: downloadTask: 

D/TAG: onStartCommand: 
D/TAG: onStart: 
D/TAG: onStartCommand: 
D/TAG: onStart: 
D/TAG: MyIntentService: IntentService[MyIntentService
D/TAG: downloadTask: 
D/TAG: MyIntentService: IntentService[MyIntentService
D/TAG: downloadTask: 

D/TAG: onDestroy: 

若启动IntentService 多次,那么 每个耗时操作 则 以队列的方式 在 IntentService的 onHandleIntent回调方法中依次执行,所有任务执行完自动结束回调onDestroy

IntentService的onHandleIntent方法是一个抽象方法,所以我们在创建IntentService时必须实现该方法,
通过上面一系列的分析可知,onHandleIntent方法也是一个异步方法。这里要注意的是如果后台任务只有一个的 话,onHandleIntent执行完,服务就会销毁,但如果后台任务有多个的话,onHandleIntent执行完最后一个任务时,服务才销毁。最后我们要知道每次执行一个后台任务就必须启动一次IntentService,而IntentService内部则是通过消息的方式发送给HandlerThread的,然后由Handler中的Looper来处理消息,而Looper是按顺序从消息队列中取任务的,也就是说IntentService的后台任务时顺序执行的,当有多个后台任务同时存在时,这些后台任务会按外部调用的顺序排队执行,我们前面的使用案例也很好说明了这点。


image.png

接下来,我们将通过 源码分析 解决以下问题:

  • IntentService 如何单独开启1个新的工作线程
    主要分析内容 = IntentService源码中的 onCreate()方法
@Override
public void onCreate() {
    super.onCreate();
    
    // 1. 通过实例化andlerThread新建线程 & 启动;故 使用IntentService时,不需额外新建线程
    // HandlerThread继承自Thread,内部封装了 Looper
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();
  
    // 2. 获得工作线程的 Looper & 维护自己的工作队列
    mServiceLooper = thread.getLooper();

    // 3. 新建mServiceHandler & 绑定上述获得Looper
    // 新建的Handler 属于工作线程 ->>分析1
    mServiceHandler = new ServiceHandler(mServiceLooper); 
}


   /** 
     * 分析1:ServiceHandler源码分析
     **/ 
     private final class ServiceHandler extends Handler {

         // 构造函数
         public ServiceHandler(Looper looper) {
         super(looper);
       }

        // IntentService的handleMessage()把接收的消息交给onHandleIntent()处理
        @Override
         public void handleMessage(Message msg) {
  
          // onHandleIntent 方法在工作线程中执行
          // onHandleIntent() = 抽象方法,使用时需重写 ->>分析2
          onHandleIntent((Intent)msg.obj);
          // 执行完调用 stopSelf() 结束服务
          stopSelf(msg.arg1);

    }
}

   /** 
     * 分析2: onHandleIntent()源码分析
     * onHandleIntent() = 抽象方法,使用时需重写
     **/ 
      @WorkerThread
      protected abstract void onHandleIntent(Intent intent);
  • IntentService 如何通过onStartCommand() 将Intent 传递给服务 & 依次插入到工作队列中

/** 
  * onStartCommand()源码分析
  * onHandleIntent() = 抽象方法,使用时需重写
  **/ 
  public int onStartCommand(Intent intent, int flags, int startId) {

    // 调用onStart()->>分析1
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

/** 
  * 分析1:onStart(intent, startId)
  **/ 
  public void onStart(Intent intent, int startId) {

    // 1. 获得ServiceHandler消息的引用
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;

    // 2. 把 Intent参数 包装到 message 的 obj 发送消息中,
    //这里的Intent  = 启动服务时startService(Intent) 里传入的 Intent
    msg.obj = intent;

    // 3. 发送消息,即 添加到消息队列里
    mServiceHandler.sendMessage(msg);
}

总结
从上面源码可看出:IntentService本质 = Handler + HandlerThread:
1、 onCreate()方法,通过HandlerThread 单独开启1个工作线程:IntentService
创建1个内部 Handler :ServiceHandler
2,通过 onStartCommand() 传递服务intent 到ServiceHandler 、依次插入Intent到工作队列中 & 逐个发送给 onHandleIntent()

若一个任务正在IntentService中执行,此时你再发送1个新的任务请求,这个新的任务会一直等待直到前面一个任务执行完毕后才开始执行、

原因:

  • 由于onCreate()只会调用一次 = 只会创建1个工作线程;
  • 当多次调用 startService(Intent)时(即 onStartCommand()也会调用多次),其实不会创建新的工
  • 作线程,只是把消息加入消息队列中 & 等待执行。
    所以,多次启动 IntentService 会按顺序执行事件
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,050评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,538评论 1 306
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,673评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,622评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,047评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,974评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,129评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,893评论 0 209
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,654评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,828评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,297评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,619评论 3 262
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,326评论 3 243
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,176评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,975评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,118评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,909评论 2 278