前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android四大组件之Service

Android四大组件之Service

作者头像
下码看花
发布2019-09-02 16:38:02
8230
发布2019-09-02 16:38:02
举报
文章被收录于专栏:AndroidStudio初识
前言

Hi,大家好,上一期我们讲了如何使用BroadcastReceiver,这一期我们讲解Android四大组件之Service相关知识。每天一篇技术干货,每天我们一起进步。

耐心专注不仅仅是美德,更是一笔财富。

1.简介与定义
简介

Service是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。Service可由其他应用组件启动,而且即使用户切换到其他应用,Service仍将在后台继续运行。此外,组件可以绑定到Service,以与之进行交互,甚至是执行进程间通信 (IPC)。例如,Service可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。

定义

Service是一个专门在后台处理长时间任务的 Android组件。

1. Service不是一个单独的进程;

2. Service也不是一个单独的线程;

3. Service是一个单独的Android组件,Service运行在主线程上,如果想在Service中处理很占时间的操作时,必须在Service中开线程,以降低Activity没有响应的风险;

4. Service不提供用户界面;

它有两种启动方式:startServicebindService

2.用途

Service有三个常见用途。

1.功能调度:Service接收指定的广播信息,从而进一步分析和处理事件,最后修改数据、更新界面或者进行其他相关的操作,调度整个应用使其保持正确的状态。

2.功能提供:Service并不会接收任何的广播,只接收指定的广播提供状态数据,这时需要绑定Service,绑定Service时要管理好Service,一般在Activity的onStop函数里进行解绑unBindService操作。

3.远程调用:定义AIDL服务,跨进程调用Service,先定义一个远程调用接口,然后为该接口提供一个IBinder实现类,客户端获取了远程的Service的IBinder对象的代理后,通过该IBinder对象去回调远程Service的属性或方法。

3.应用场景

如果某个程序组件需要在运行时向用户呈现界面,或者程序需要与用户交互,就需要用Activity,否则就应该考虑使用Service。

4.Service与Activity对比

相似点:

1.都是单独的Android组件;

2.都拥有独立的生命周期;

3.都是Context的派生类,所以可以调用Context类定义的如 getResources()getContentResolver()等方法;

4.都拥有自己生命周期回调方法;

不同点:

1.Activity运行于前台有图形用户界面,负责与用户交互;Service通常位于后台运行,不需要与用户交互,也没有图形用户界面。

5.Service的生命周期

随着应用程序启动Service方式不同,Service的生命周期也略有差异,如下图:

如果应用程序通过startService()方法来启动Service,Service的生命周期如上图左半部分所示。

通过调用startService() 方法启动Service:当其他组件调用startService()方法时,Service被创建,并且无限期运行,其自身必须调用stopSelf()方法或者其他组件调用stopService() 方法来停止Service,当Service停止时,系统将其销毁。

如果应用程序通过bindService()方法来启动Service,Service的生命周期如上图右半部分所示。

通过bindService() 方法启动Service:当其他组件调用bindService()方法时,Service被创建。接着客户端通过IBinder接口与Service通信。客户端通过unbindService() 方法关闭连接。多个客户端能绑定到同一个Service,并且当他们都解除绑定时,系统将销毁Service(Service不需要被停止)

特别说明:当Activity调用bindService()绑定一个已通过startService()启动的Service时,系统只是把Service内部的IBinder对象传给Activity,并不会把该Service生命周期完全绑定到该Activity,因而当Activity调用unBindService()方法取消与该Service的绑定时,也只是切断该Activity与Service之间的关联,并不能停止该Service组件。要停止该Service组件,还需调用stopService()方法。

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

4个手动调用的方法

手动调用方法

作用

startService()

启动服务

stopService()

关闭服务

bindService()

绑定服务

unbindService()

解绑服务

5个自动调用的方法

内部自动调用的方法

作用

onCreate()

创建服务

onStartCommand()

开始服务

onDestroy()

销毁服务

onBind()

绑定服务

onUnbind()

解绑服务

6.Service的使用

当我们开始使用 Service的时候当然是启动一个Service了,启动Service的方法和启动Activity很类似,都需要借助Intent来实现,下面我们就通过一个具体的例子来看一下。

代码语言:javascript
复制
public class MyService extends Service {  

    public static final String TAG = "MyService";  

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

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

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

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

}

要创建一个这样的Service,你需要让该类继承Service类,然后重写以下方法:

  • onCreate() 1.如果service没被创建过,调用startService()后会执行onCreate()和onStartCommand()方法;2.如果service已处于运行中,调用startService()不会执行onCreate()方法,只执行onStartCommand()方法。也就是说,onCreate()只会在第一次创建service时候调用,多次执行startService()不会重复调用onCreate(),此方法适合完成一些初始化工作。
  • onStartCommand() 如果多次执行了Context的startService()方法,那么Service的onStartCommand()方法也会相应的多次调用。onStartCommand()方法很重要,我们在该方法中根据传入的Intent参数进行实际的操作,比如会在此处创建一个线程用于下载数据或播放音乐等。
  • onBind() Service中的onBind()方法是抽象方法,Service类本身就是抽象类,所以onBind()方法是必须重写的,即使我们用不到。
  • onDestroy() 在销毁的时候会执行Service的该方法。

这几个方法都是回调方法,且在主线程中执行,由Android操作系统在合适的时机调用。

注意:每个 Service必须在 manifest中 通过来声明。

代码语言:javascript
复制
<service android:name="com.demo.service.MyService" > 
  ... 
</service>

现在我们通过继承 Service的方式定义了我们自己的 MyService类,并且在 manifest中声明了我们的 MyService,接下来我们应该启动我们自己的服务。

启动Service

第一种方式:我们是通过一个 Intent对象,并调用 startService()方法来启动 MyService

代码语言:javascript
复制
Intent startIntent = new Intent(this, MyService.class);  
startService(startIntent);

注意:假如我们是通过点击 Button执行上面的代码,那么第一次点击的时候回执行其中的 onCreate()onStartCommand()方法,但是当我们第二次点击的时候就只会执行 onStartCommand()方法。

为什么会这样呢?这是由于 onCreate()方法只会在 Service第一次被创建的时候调用,如果当前 Service已经被创建过了(第一次点击创建了 MyService),不管怎样调用 startService()方法, onCreate()方法都不会再执行。

第二种方式:通过 bindService启动 Service

bindService启动服务特点:1. bindService启动的服务和调用者之间是典型的 client-server模式。调用者是 clientservice则是 server端。service只有一个,但绑定到 service上面的 client可以有一个或很多个。这里所提到的 client指的是组件,比如某个 Activity

2. client可以通过 IBinder接口获取 Service实例,从而实现在 client端直接调用 Service中的方法以实现灵活交互,这在通过 startService()方法启动中是无法实现的。

3. bindService启动服务的生命周期与其绑定的 client息息相关。当 client销毁时, client会自动与 Service解除绑定( client会有 ServiceConnectionLeaked异常,但程序不会崩溃)。当然, client也可以明确调用 ContextunbindService()方法与 Service解除绑定。当没有任何 clientService绑定时, Service会自行销毁。

启动了之后,当我们想停止服务的时候该怎么做呢?

停止Service

第一种方式:我们也是通过一个 Intent对象,并调用 stopService()方法来停止 MyService

代码语言:javascript
复制
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);

第二种方式:调用 unbindService(conn)方法来停止 MyService

代码语言:javascript
复制
unbindService(ServiceConnection conn)
Service和Activity通信

在上面我们高高兴兴的启动了 Service了,但是细心的你可能发现了,貌似我们仅仅只是启动了而已, ActivityService并没有多少"交流",下面我们就让 ActivityService交流一下。

代码语言:javascript
复制
public class MyService extends Service {

    public static final String TAG = "MyService";

    private MyBinder mBinder = new MyBinder();

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

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

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

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

    class MyBinder extends Binder {

        public void startDownload() {
            Log.d("TAG", "startDownload() executed");
            // 执行具体的下载任务
        }

    }

}

接下来我们在 MainActivity中通过 Button来绑定 Service和解除绑定

代码语言:javascript
复制
public class MainActivity extends Activity implements OnClickListener {

    private Button bindService;

    private Button unbindService;

    private MyService.MyBinder myBinder;

    private ServiceConnection connection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myBinder = (MyService.MyBinder) service;
            myBinder.startDownload();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindService = (Button) findViewById(R.id.bind_service);
        unbindService = (Button) findViewById(R.id.unbind_service);
        bindService.setOnClickListener(this);
        unbindService.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.bind_service:
            Intent bindIntent = new Intent(this, MyService.class);
            bindService(bindIntent, connection, BIND_AUTO_CREATE);
            break;
        case R.id.unbind_service:
            unbindService(connection);
            break;
        default:
            break;
        }
    }

}

可以看到,这里我们首先创建了一个 ServiceConnection的匿名类,在里面重写了 onServiceConnected()方法和 onServiceDisconnected()方法,这两个方法分别会在 ActivityService建立关联和解除关联的时候调用。在 onServiceConnected()方法中,我们又通过 向下转型 得到了 MyBinder的实例,有了这个实例, ActivityService之间的关系就变得非常紧密了。现在我们可以在 Activity中根据具体的场景来调用 MyBinder中的任何 public方法,即实现了 Activity指挥 Service干什么 Service就去干什么的功能。

当然,现在 ActivityService其实还没关联起来了呢,这个功能是在Bind Service按钮的点击事件里完成的。可以看到,这里我们仍然是构建出了一个 Intent对象,然后调用 bindService()方法将 ActivityService进行绑定。bindService()方法接收三个参数,第一个参数就是刚刚构建出的 Intent对象,第二个参数是前面创建出的 ServiceConnection的实例,第三个参数是一个标志位,这里传入 BIND_AUTO_CREATE表示在 ActivityService建立关联后自动创建 Service,这会使得 MyService中的 onCreate()方法得到执行,但 onStartCommand()方法不会执行(只有当我们通过 startService()方法请求启动服务时,调用此方法)。

解除Activity和Service之间的关联,调用

代码语言:javascript
复制
unbindService(connection);
关于销毁Service说明
  • MyService的内部通过 stopSelf()方法来销毁的;
  • 一个Service必须要在既没有和任何Activity关联又处理停止状态的时候才会被销毁;
  • 在Service的onDestroy()方法里去清理掉那些不再使用的资源,防止在Service被销毁后还会有一些不再使用的对象仍占用着内存;
7.IntentService

IntentService是Service的子类,在介绍IntentService之前,先来了解使用Service时需要注意的两个问题

  • Service 不会专门启动一个线程执行耗时操作,所有的操作都是在主线程中进行的,以至于容易出现ANR,所以需要手动开启一个子线程;
  • Service 不会自动停止,需要调用 stopSelf()方法 或者 是 stopService() 方法停止;

使用 IntentService不会出现这两个问题,因为 IntentService在开启 Service时,会自动开启一个新的线程来执行它,另外,当 Service运行结束后,会自动停止。

8.如何保证服务不会被杀死

第一种方式,返回 START_STICKYSTART_REDELIVER_INTENT

Service因内存不足而被系统kill后,一段时间后内存再次空闲时,系统将会尝试重新创建此 Service,一旦创建成功后将回调 onStartCommand方法,但其中的 Intent将是 null,除非有挂起的 Intent,如 pendingintent,这个状态下比较适用于不执行命令、但无限期运行并等待作业的媒体播放器或类似的服务。

代码语言:javascript
复制
/**
     * 返回 START_STICKY 或 START_REDELIVER_INTENT
     * @param intent
     * @param flags
     * @param startId
     * @return
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //return super.onStartCommand(intent, flags, startId);
        return START_STICKY;
    }

第二种方式,提高service的优先权

代码语言:javascript
复制
<service  
    android:name="com.demo.UploadService"  
    android:enabled="true" >  
    <intent-filter android:priority="1000" >  
        <action android:name="com.demo.MyService" />  
    </intent-filter>  
</service>
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 下码看花 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 1.简介与定义
    • 简介
      • 定义
      • 2.用途
      • 3.应用场景
      • 4.Service与Activity对比
      • 5.Service的生命周期
      • 6.Service的使用
        • 启动Service
          • 停止Service
            • Service和Activity通信
              • 关于销毁Service说明
              • 7.IntentService
              • 8.如何保证服务不会被杀死
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档