Android:Service详解

这篇文章主要是讲解Service

前言:Service非常适用于去执行那些不需要和用户交互而且还要长期运行在后台的任务。Service默认线程为UI线程,不要在Service中执行耗时的操作,除非你在Service中创建了子线程来完成耗时操作.
Service的运行不依赖于任何用户界面,即使程序被切换到后台或者用户打开另一个应用程序,Service仍然能够保持正常运行,这也正是Service的使用场景。当某个应用程序进程被杀掉时,所有依赖于该进程的Service也会停止运行.

一、Service的基本用法

普通的Service的生命周期很简单,分别为onCreate、onStartCommand、onDestroy这三个。当我们startService()的时候,首次创建Service会回调onCreate()方法,然后回调onStartCommand()方法,再次startService()的时候,就只会执行onStartCommand()。服务一旦开启后,我们就需要通过stopService()方法或者stopSelf()方法关闭服务,这时就会回调onDestroy()

  • 创建服务类

创建一个服务非常简单,只要继承Service,并实现onBind()方法

public class BackGroupService extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        Log.e("Service","onBind");
        return null;
    }

    @Override
    public void onCreate() {
        Log.e("Service","onCreate");
        super.onCreate();
    }

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

    @Override
    public void onDestroy() {
        Log.e("Service","onDestroy");
        super.onDestroy();
    }
}
  • 配置AndroidManifest
    Service也是四大组件之一,所以必须在AndroidManifest中配置
    <service android:name=".BackGroupService" />
  • 启动服务和停止服务
    我们通过两个按钮分别演示启动服务和停止服务,通过startService()开启服务,通过stopService()停止服务
Intent startServiceIntent = new Intent(this, BackGroupService.class);
startService.setOnClickListener((v)-> {
                //启动服务
                startService(startServiceIntent );
        });
        stopService.setOnClickListener((v)-> {
                //停止服务
                stopService(startServiceIntent);
        });

运行程序后,我们点击开始服务,然后关闭服务。我们以Log信息来验证普通Service的生命周期:onCreate->onStartCommand->onDestroy
当你开启服务后,还有一种方法可以关闭服务,在设置中,通过应用->找到自己应用->停止

二、Service和Activity进行通信

Service和Activity进行通信指前台页面(Activity)可以调用后台服务的方法,实现步骤是和普通服务实现步骤是一样的,区别在于启动的方式和获得Service的代理对象

  • 创建服务类

这里和普通Service不同,在onBind中返回一个Binder 对象,Activity可以获取该Binder 对象从而执行后台服务的方法.

class MyBind extends Binder {
    public void startDownload() {
        Log.d("Service", "开始下载");
        // 执行具体的下载任务
    }
}
    @Override
    public IBinder onBind(Intent intent) {
        Log.e("Service", "onBind");
        return new MyBind(); //这里返回服务代理对象
    }
    @Override
    public boolean onUnbind(Intent intent) {
        Log.e("Service", "onUnbind");
        return super.onUnbind(intent);
    }
  • 绑定服务和解除绑定服务
    我们通过两个按钮分别演示绑定服务和解除绑定服务,通过bindService()开启服务,通过unbindService()停止服务
bindService.setOnClickListener((view)-> {
                bindService(startServiceIntent,serviceConnection,BIND_AUTO_CREATE);
        });
unbindService.setOnClickListener((view->) {
         unbindService(serviceConnection);
        });
  }
    /**
     * 创建ServiceConnection
     */
    final ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //拿到后台服务代理对象
            final MyBind myBind = (MyBind) iBinder;
            myBind.startDownload();
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
        }
    };

这里和startService的区别在于多了一个ServiceConnection对象,该对象是用户绑定后台服务后,可获取后台服务代理对象的回调,我们可以通过该回调,拿到后台服务的代理对象,并调用后台服务定义的方法,也就实现了后台服务和前台的交互

  • 运行代码
    运行程序后,我们点击绑定服务,然后一段时间后解除绑定服务。我们以Log信息来验证Service的生命周期:onCreate->onBind->onUnBind->onDestroy,其中onbind开启服务重复开启不会执行任何方法,其中也可以看到我们调用后台服务的方法开始下载了....


  • 混合性交互的后台服务
    如果我们既点击了Start Service按钮,又点击了Bind Service按钮会怎么样呢?这个时候你会发现,不管你是单独点击Stop Service按钮还是Unbind Service按钮,Service都不会被销毁,必要将两个按钮都点击一下,Service才会被销毁。也就是说,点击Stop Service按钮只会让Service停止,点击Unbind Service按钮只会让Service和Activity解除关联,一个Service必须要在既没有和任何Activity关联又处理停止状态的时候才会被销毁。


三、Service和Thread的关系
Service和Thread到底有什么关系呢?什么时候应该用Service,什么时候又应该用Thread?
答案是Service和Thread之间没有任何关系!

之所以有不少人会把它们联系起来,主要就是因为Service的后台概念。Thread我们大家都知道,是用于开启一个子线程,在这里去执行一些耗时操作就不会阻塞主线程的运行。而Service我们最初理解的时候,总会觉得它是用来处理一些后台任务的,一些比较耗时的操作也可以放在这里运行,这就会让人产生混淆了。但是Service其实就是运行在主线程里的,那要Service又有何用呢?其实大家不要把后台和子线程联系在一起就行了,这是两个完全不同的概念。Android的后台就是指,它的运行是完全不依赖UI的。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行。比如说一些应用程序,始终需要与服务器之间始终保持着心跳连接,就可以使用Service来实现。在Service中再创建一个子线程,然后在这里去处理耗时逻辑就没问题了。既然在Service里也要创建一个子线程,那为什么不直接在Activity里创建呢?这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。

四、前台服务
Service几乎都是在后台运行的,一直以来它都是默默地做着辛苦的工作。但是Service的系统优先级还是比较低的,当系统出现内存不足情况时,就有可能会回收掉正在后台运行的Service。如果你希望Service可以一直保持运行状态,而不会由于系统内存不足的原因导致被回收,就可以考虑使用前台Service。前台Service和普通Service最大的区别就在于,它会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。当然有时候你也可能不仅仅是为了防止Service被回收才使用前台Service,有些项目由于特殊的需求会要求必须使用前台Service.

  • 创建服务类
    前台服务创建很简单,其实就在Service的基础上创建一个Notification,然后使用Service的startForeground()方法即可启动为前台服务
private void showNotification() {
        final Notification.Builder mBuilder = new Notification.Builder(this)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle("前台服务")
                .setContentText("我是前台服务");
        final Intent intent = new Intent(this, MainActivity.class);
        //创建任务栈Builder
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addParentStack(MainActivity.class);
        stackBuilder.addNextIntent(intent);
        PendingIntent pendingIntent = stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);
        //设置跳转Intent到通知中
        mBuilder.setContentIntent(pendingIntent);
        //获取通知服务
        NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        //构建通知
        Notification notification = mBuilder.build();
        //显示通知
        nm.notify(0, notification);
        //启动为前台服务
        startForeground(0, notification);
    }
  • 配置服务
    <service android:name=".ForegroundService" />
  • 启动和结束服务
    现在重新运行一下程序,并点击Start Service或Bind Service按钮,ForegroundService就会以前台Service的模式启动了,并且在系统状态栏会弹出一个通栏图标,下拉状态栏后可以看到通知的详细内容,如下图所示。


五、IntentService
IntentService是专门用来解决Service中不能执行耗时操作这一问题的,创建一个IntentService也很简单,只要继承IntentService并覆写onHandlerIntent函数,在该函数中就可以执行耗时操作了,执行完耗时操作后自动结束服务.

public class TheIntentService extends IntentService {

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

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

推荐阅读更多精彩内容