2、Service

Service和Thread的区别

既然是长耗时的操作,那么Thread也可以完成啊。没错,在程序里面很多耗时工作我们也可以通过Thread来完成,那么还需要Service干嘛呢?

  • Service可以重写onStartCommand,使得Service被杀掉之后,Service能自动重启,Thread做不到。
  • Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。
Service与Activity怎么实现通信
  • 通过Binder对象(AIDL):Activity调用bindService()方法,绑定一个继承Service的引用对象MyService。通过实例化ServiceConnection接口内部类监听的方法获取MyService中的Binder对象。如果想实现主动通知Activity的,还可以在MyService中添加回调方法。(例子代码如下)
  • 通过广播:Activity调用registerReceiver(BroadcastReceiver recevier, IntentFilter filter)注册广播接收器,通过startService(Service service)启动一个集成Service的应用对象MsgService,之后MsgService可通过sendBoadcast(Intent intent)更新Actvity内容信息。
public class MyService extends Service {

    private DownloadBinder mBinder = new DownloadBinder();

    class DownloadBinder extends Binder {
        public void startDownload() {
            Log.d("MyService", "startDownload executed");
            downloadCore();
        } // 在Service中自定义startDownload方法,后续在Activity中调用此方法

        public int getProgress() {
            Log.d("MyService", "getProgress executed");
            return 0;
        } // 在Service中自定义getProgress方法,后续在Activity中调用此方法

        public MyService getService() {
            return MyService.this;
        } // 在Service中自定义getService方法,后续在Activity中调用此方法,获得MyService对象以设置回调
    }

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

    private void downloadCore() {
        if (null != listener) {
            listener.notifyProgress(0);
        }
    }

    private OnProgressListener listener;

    public void setOnProgressListener(OnProgressListener listener) {
        this.listener = listener;
    }

    interface OnProgressListener {
        void notifyProgress(int progress);
    }
    ......
}
public class MainActivity extends AppCompatActivity {

    private MyService.DownloadBinder downloadBinder;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            downloadBinder = (MyService.DownloadBinder) service;
            downloadBinder.startDownload();
            downloadBinder.getProgress();

            MyService myService = downloadBinder.getService();
            myService.setOnProgressListener(new MyService.OnProgressListener() {
                @Override
                public void notifyProgress(int progress) {
                    Log.d("MyService", "notifyProgress executed");
                }
            });
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button bindServiceBtn = findViewById(R.id.button1);
        Button stopServiceBtn = findViewById(R.id.button2);
        bindServiceBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, MyService.class);
                bindService(intent, connection, Context.BIND_AUTO_CREATE);
            }
        });
        stopServiceBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                unbindService(connection);
            }
        });
    }
}
Service的生命周期,两种启动方式(startService、bindService)的区别
生命周期.png
  • 被启动的服务的生命周期:如果一个Service被某个Activity调用Context.startService方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService方法多次启动,那么onCreate方法只会调用一次,onStartCommand将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。
  • 被绑定的服务的生命周期:如果一个Service被某个Activity调用Context.bindService方法绑定启动,不管调用bindService调用几次,onCreate和onBind方法都只会调用一次(不调用unbindService),同时onStartCommand方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService断开连接或者之前调用bindService的Context不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。
  • 被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStartCommand便会调用多少次。必须调用unbindService和stopService来停止服务。
  • 当服务被停止时清除服务:当一个Service被终止(①调用stopService;②调用stopSelf;③不再有绑定的连接(没有被启动))时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。

startService、bindService的区别:举一个简单的例子,最常用的Service就是音乐播放,如果你要startService的话,应用退出了,音乐还在播放。用bindService的话,程序退出了,音乐也就停了。

bindService和startService混合使用的生命周期以及怎么关闭

举两个混用的例子,未加粗的是Activity中的调用,加粗的是Service的生命周期:

  • startService → onCreateonStartCommand → bindService → onBind → stopService → unbindService → onUnbindonDestroy
  • bindService → onCreateonBind → startService → onStartCommand → unbindService → onUnbind → stopService → onDestroy

只有当stopService和unbindService都被调用,Service才会调用onDestroy,Service的生命周期才会结束。

Service的onStartCommand方法有几种返回值?各代表什么意思?

在Service中重写onStartCommand方法,可以使得Service被杀死(比如杀掉apk)之后自动重启。

  • START_STICKY 粘性的:如果Service被杀死之后,系统会自动重启该服务,但是不会保留之前传递过来的intent对象。
  • START_NOT_STICKY 非粘性的:Service被杀死之后,系统不会自动重启该Service。
  • START_REDELIVER_INTENT 重传intent:Service被杀死之后,系统会自动重启该服务,并且保留之前的intent传入(就是比START_STICKY多了一个intent)。
  • START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但是不保证Service一定能重启。
IntentService是什么、原理、应用场景及其与Service的区别
  • 是什么:如果直接把耗时操作放在Service的onStartCommand()中,很容易引起ANR。IntentService是继承于 Service 并处理异步请求的一个类,在IntentService内有一个子线程来处理耗时操作,通过 startService(intent)启动服务,当任务执行完后,IntentService会自动停止,而不需要我们去手动控制或stopSelf()。
    另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且,每次只会执行一个子线程,执行完第一个再执行第二个,以此类推。
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        for (int i = 1; i <= 20; i++) {
            Intent intent = new Intent(this, MyIntentService.class);
            startService(intent);
        }
    }

    ......
}
  • 原理:IntentService本质是采用Handler & HandlerThread方式:(源码如下)
    ①通过HandlerThread单独开启一个名为IntentService的线程。
    ②创建一个名为ServiceHandler的Handler。
    ③把内部Handler与子线程HandlerThread的Looper进行绑定。
    ④通过onStartCommand()传递给服务intent,依次插入到工作队列中,并逐个发送给onHandleIntent()。
    ⑤通过onHandleIntent()来依次处理所有Intent请求对象所对应的任务。
    注意:多次启动IntentService不会重新创建新的服务和新的线程,而是把消息加入消息队列中等待执行,而如果服务停止,会清除消息队列中的消息,后续的事件便不会执行。
    @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);
    }
  • 应用场景:解决多个耗时任务需要顺序依次执行的问题。而如果仅用Service,开多个线程去执行耗时操作,就很难管理。例如排队下载,一个下载完的时候需要自动去下载下一个,下载完后自动关闭任务。
  • 区别:
    ①IntentService是继承自Service类,创建了自己的特有方法onHandleIntent:每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,执行完第一个再执行第二个.
    ②每次IntentService执行完任务都会自动停止,而不需要我们手动去控制或stopSelf()。
    ③IntentService每次执行都开启一个子线程。(连续执行耗时操作除外)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容