Android开发系列4——Service详解

前言

  Service是Android的四大组件之一,在Android开发过程中是一个必不可少的组件。

  Service是一种可在后台执行长时间运行操作而不提供界面的应用组件。Service可以由Activity、Context等多种组件启动,而且还可以通过其他应用组件启动,而且即使用户切换到其他应用,Activity页面切换,Service一直都会保持在后台运行服务。此外,组件可以通过绑定到服务与之进行交互,甚至是执行进程间通信(IPC)。例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。

一、Service的生命周期

  Service的生命周期比Activity的生命周期简单,但是需要注意Service几个状态(创建、销毁等),因为Service一直处于后台,避免一直在后台运行。

Service的生命周期从创建到销毁遵循两种方式:

  • 启动服务
    启动服务在其他组件调用startService()时创建,然后无限期运行,必须通过调用stopSelf()来自行停止运行,或者是通过其他组件通过调用stopService() 来停止服务。服务停止后,系统会将其销毁。

  • 绑定服务
    该服务在其他组件(客户端)调用 bindService() 时创建。然后,客户端通过 IBinder 接口与服务进行通信。客户端可通过调用 unbindService() 关闭连接。多个客户端可以绑定到相同服务,而且当所有绑定全部取消后,系统即会销毁该服务。(服务不必自行停止运行。)

Service对应的两种启动方式的生命周期如下所示:

Service生命周期
从上边的图上可以简单的了解到Service的生命周期的每个阶段,接下来会说明一下每个阶段对应的功能,在每个阶段怎么使用。

回调描述
onCreate()当服务通过onStartCommand()和onBind()被第一次创建的时候,系统调用该方法。如果service已处于运行中,调用startService()不会执行onCreate()方法。也就是说,onCreate()只会在第一次创建service时候调用,多次执行startService()不会重复调用onCreate(),此方法适合完成一些初始化工作
onStartCommand()其他组件(如活动)通过调用startService()来请求启动服务时,系统调用该方法。如果多次执行了Context的startService()方法,那么Service的onStartCommand()方法也会相应的多次调用
onBind当其他组件想要通过bindService()来绑定服务时,系统调用该方法。如果你实现该方法,你需要返回IBinder对象来提供一个接口,以便客户来与服务通信。你必须实现该方法,如果你不允许绑定,则直接返回null。
onUnbind()当客户中断所有服务发布的特殊接口时,系统调用该方法。
onRebind()当新的客户端与服务连接,且此前它已经通过onUnbind(Intent)通知断开连接时,系统调用该方法。
onDestroy()当服务不再有用或者被销毁时,系统调用该方法。你的服务需要实现该方法来清理任何资源,如线程,已注册的监听器,接收器等。

二、Service的应用

不管是哪一种的 service ,也都需要在 AndroidManifest.xml中声明,所以需要把自定义的Service配置如下:

<service android:name=".MyService"
            android:enabled="true"
            android:exported="true"
            android:icon="@drawable/background_blue"
            android:label="string"
            android:process="string"
            android:permission="string">
 </service>
名称介绍
android:exported表示是否允许除了当前程序之外的其他程序访问这个服务
android:enabled表示是否启用这个服务
android:permission是权限声明
android:process是否需要在单独的进程中运行,当设置为android:process=”:remote”时,代表Service在单独的进程中运行。注意“:”很重要,它的意思是指要在当前进程名称前面附加上当前的包名,所以“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote。
android:isolatedProcess设置 true 意味着,服务会在一个特殊的进程下运行,这个进程与系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务的API(bind and start)。

1.启动服务

重写一个新的Service子类MyService,重写对应的方法。(其中IBinder onBind(Intent intent)方法并没有实现任何方法,但必须重写。)接下来用一个简单的例子来呈现启动服务

import android.app.Service;
import android.util.Log;
import android.content.Intent;
import android.os.IBinder;

public class MyService extends Service {

    @Override
    public void onCreate() {
        Log.i("Wds","onCreate - Thread ID = " + Thread.currentThread().getId());
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("Wds", "onStartCommand - startId = " + startId + ", Thread ID = " + Thread.currentThread().getId());
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i("Wds", "onBind - Thread ID = " + Thread.currentThread().getId());
        return null;
    }

    @Override
    public void onDestroy() {
        Log.i("Wds", "onDestroy - Thread ID = " + Thread.currentThread().getId());
        super.onDestroy();
    }
}

Activity中通过启动MyService–>销毁MyService,输出在整个Service的运行过程的流程。

public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.i("Wds", "Thread ID = " + Thread.currentThread().getId());
        Log.i("Wds", "before StartService");

        //连续启动Service
        Intent intentOne = new Intent(this, MyService.class);
        startService(intentOne);
        Intent intentTwo = new Intent(this, MyService.class);
        startService(intentTwo);
        Intent intentThree = new Intent(this, MyService.class);
        startService(intentThree);

        //停止Service
        Intent intentFour = new Intent(this, MyService.class);
        stopService(intentFour);

        //再次启动Service
        Intent intentFive = new Intent(this, MyService.class);
        startService(intentFive);

        Log.i("Wds", "after StartService");
    }
}

Activity在运行中打印出来Service的Log输出:

I/Wds: Thread ID = 1
I/Wds: before StartService
I/Wds: after StartService
I/Wds: onCreate - Thread ID = 1
I/Wds: onStartCommand - startId = 1, Thread ID = 1
I/Wds: onStartCommand - startId = 2, Thread ID = 1
I/Wds: onStartCommand - startId = 3, Thread ID = 1
I/Wds: onDestroy - Thread ID = 1
I/Wds: onCreate - Thread ID = 1
I/Wds: onStartCommand - startId = 1, Thread ID = 1

从上述的输出可以看到:

  • 1.Thread ID = 1说明是主线程,
  • 2.Service在通过Activity启动,Service的运行在主线程上。
  • 3.通过多次调用startService,onCreate只调用一次。每次调用startService都会再次回调onStartCommand方法,多次调用startService生成的startId是不同的Id值。

所以,在使用Service过程中,运行Service过程需要创建一个子线程,把需要的操作放在子线程中执行(避免主线程的堵塞)。

2.绑定服务

绑定服务(client-server模式):

  • 调用者是client
  • service则是server端

绑定服务有一些特征:

  • 1.service只有一个,但绑定到service上面的client可以有一个或很多个。这里所提到的client指的是组件,比如某个Activity。
  • 2.client可以通过IBinder接口获取Service实例,从而实现在client端直接调用Service中的方法以实现灵活交互,这在通过startService方法启动中是无法实现的。
  • 3.bindService启动服务的生命周期与其绑定的client息息相关。当client销毁时,client会自动与Service解除绑定。当然,client也可以明确调用Context的unbindService()方法与Service解除绑定。当没有任何client与Service绑定时,Service会自行销毁。

接下来会用一些例子详细说明绑定服务的特征:

BindService的对象

import android.app.Service;
import android.os.IBinder;
import android.util.Log;
import android.content.Intent;
import android.os.Binder;
import java.util.Random;

public class BindService extends Service {

    //client 可以通过Binder获取Service实例
    public class MyBinder extends Binder {
        public BindService getService() {
            return BindService.this;
        }
    }

    //通过binder实现调用者client与Service之间的通信
    private MyBinder binder = new MyBinder();

    private final Random generator = new Random();

    @Override
    public void onCreate() {
        Log.i("Wds","BindService - onCreate - Thread = " + Thread.currentThread().getName());
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("Wds", "BindService - onStartCommand - startId = " + startId + ", Thread = " + Thread.currentThread().getName());
        return START_NOT_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i("Wds", "BindService - onBind - Thread = " + Thread.currentThread().getName());
        return binder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i("Wds", "BindService - onUnbind - from = " + intent.getStringExtra("from"));
        return false;
    }

    @Override
    public void onDestroy() {
        Log.i("Wds", "BindService - onDestroy - Thread = " + Thread.currentThread().getName());
        super.onDestroy();
    }

    //getRandomNumber是Service暴露出去供client调用的公共方法
    public int getRandomNumber() {
        return generator.nextInt();
    }
}

**Client对象 A **

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private BindService BBservice = null;
    private boolean isBind = false;
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            isBind = true;
            BindService.MyBinder myBinder = (BindService.MyBinder) service;

            BBservice = myBinder.getService();
            Log.i("Wds", "MainActivity - onServiceConnected");

            int num = BBservice.getRandomNumber();
            Log.i("Wds", "MainActivity - getRandomNumber = " + num);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBind = false;
            Log.i("Wds", "MainActivity - onServiceDisconnected");
        }
    };


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


        findViewById(R.id.nextBtn).setOnClickListener(this);
        findViewById(R.id.bindService).setOnClickListener(this);
        findViewById(R.id.unBindService).setOnClickListener(this);
        findViewById(R.id.finishBtn).setOnClickListener(this);

    }
	 @Override
    protected void onDestroy() {
        super.onDestroy();
		Log.i("Wds",
                        "-----------Main-------onDestroy-------------------");
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.bindService){
            //单击了“bindService”按钮
            Intent intent = new Intent(this, BindService.class);

            intent.putExtra("from", "MainActivity");
            Log.i("Wds", "----------------------------------------------------------------------");
            Log.i("Wds", "MainActivity 执行 bindService");
            bindService(intent, conn, BIND_AUTO_CREATE);

        } else if (v.getId() == R.id.unBindService){

            if (isBind) {
                Log.i("Wds",
                        "----------------------------------------------------------------------");
                Log.i("Wds", "MainActivity 执行 unbind Service");
                unbindService(conn);
            }

        } else if (v.getId() == R.id.finishBtn){

            //单击了“Finish”按钮
            Log.i("Wds",
                    "----------------------------------------------------------------------");
            Log.i("Wds", "MainActivity 执行 finish");
            this.finish();

        } else{
            Log.i("Wds",
                    "----------------------------------------------------------------------");
            Log.i("Wds", "MainActivity 启动 NextActivity");
            //下一个页面
            Intent intent = new Intent(MainActivity.this,NextActivity.class);
            startActivity(intent);
        }
    }
}

Client对象的页面XML文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:id="@+id/root"
    >

    <Button
        android:id="@+id/nextBtn"
        android:layout_width="200sp"

        android:layout_height="50sp"
        android:layout_marginStart="59dp"
        android:layout_marginTop="20dp"
        android:onClick="onClickNextBtn"
        android:text="@string/MainBtn"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/bindService"

        android:layout_marginStart="100dp"
        android:layout_marginTop="100dp"
        android:text="Bind Service"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        ></Button>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/unBindService"

        android:layout_marginStart="100dp"
        android:layout_marginTop="180dp"
        android:text="unBind Service"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"

        ></Button>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/finishBtn"

        android:layout_marginStart="100dp"
        android:layout_marginTop="260dp"
        android:text="Finish"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"

        ></Button>
</androidx.constraintlayout.widget.ConstraintLayout>

测试情况1:

  • 1.点击MainActivity的bindService按钮
  • 2.点击MainActivity的unbindService按钮

执行出来的log

I/Wds: ----------------------------------------------------------------------
I/Wds: MainActivity 执行 bindService
I/Wds: BindService - onCreate - Thread = main
I/Wds: BindService - onBind - Thread = main
I/Wds: MainActivity - onServiceConnected
I/Wds: MainActivity - getRandomNumber = -2070009866
I/Wds: ----------------------------------------------------------------------
I/Wds: MainActivity 执行 unbind Service
I/Wds: BindService - onUnbind - from = MainActivity
I/Wds: BindService - onDestroy - Thread = main

从结果打印出来的log可以得出:

  • 1.Client执行bindService(), 如果Service不存在,则Service执行onCreate(),onBind().
  • 2.Client实例ServiceConnection执行onServiceConnected()方法。
  • 3.Client实例解除绑定,Service检测是否还有其他client与其连接,如果没有Service执行onUnbind()和onDestroy()

测试情况2:

  • 1.点击MainActivity 绑定bindService按钮
  • 2.点击MainActivity的Finish按钮

执行出来的log

I/Wds: ----------------------------------------------------------------------
I/Wds:MainActivity 执行 bindService
I/Wds: BindService - onCreate - Thread = main
I/Wds: BindService - onBind - Thread = main
I/Wds: MainActivity - onServiceConnected
I/Wds:MainActivity - getRandomNumber = 9346330
I/Wds: ----------------------------------------------------------------------
I/Wds: MainActivity 执行 finish
I/Wds:-----------Main-------onDestroy------------------- 
I/Wds: BindService - onUnbind - from = MainActivity
I/Wds: BindService - onDestroy - Thread = main

从结果打印出来的log可以得出:

  • 如果Client销毁,那么Client上的ServiceConnection被销毁,ServiceConnection会自动与Service解除绑定

Client B的文件

public class NextActivity extends AppCompatActivity implements View.OnClickListener{

    private BindService BBservice = null;
    private boolean isBind = false;
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            isBind = true;
            BindService.MyBinder myBinder = (BindService.MyBinder) service;

            BBservice = myBinder.getService();
            Log.i("Wds", "NextActivity - onServiceConnected");

            int num = BBservice.getRandomNumber();
            Log.i("Wds", "NextActivity - getRandomNumber = " + num);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBind = false;
            Log.i("Wds", "NextActivity - onServiceDisconnected");
        }
    };

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


        findViewById(R.id.bindService).setOnClickListener(this);
        findViewById(R.id.unBindService).setOnClickListener(this);
        findViewById(R.id.finishBtn).setOnClickListener(this);

    }
	
	@Override
    protected void onDestroy() {
        super.onDestroy();
		Log.i("Wds",
                        "-----------Next-------onDestroy-------------------");
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.bindService){
            //单击了“bindService”按钮
            Intent intent = new Intent(this, BindService.class);

            intent.putExtra("from", "NextActivity");
            Log.i("Wds", "----------------------------------------------------------------------");
            Log.i("Wds", "NextActivity 执行 bindService");
            bindService(intent, conn, BIND_AUTO_CREATE);

        } else if (v.getId() == R.id.unBindService){

            if (isBind) {
                Log.i("Wds",
                        "----------------------------------------------------------------------");
                Log.i("Wds", "NextActivity 执行 unbind Service");
                unbindService(conn);
            }

        } else{

             //单击了“Finish”按钮
            Log.i("Wds",
                    "----------------------------------------------------------------------");
            Log.i("Wds", "NextActivity 执行 finish");
            this.finish();
        }

    }
}

Client B页面XML文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".NextActivity"
    android:id="@+id/nextActivity"
    >


    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/bindService"

        android:layout_marginStart="100dp"
        android:layout_marginTop="100dp"
        android:text="Bind Service"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        ></Button>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/unBindService"

        android:layout_marginStart="100dp"
        android:layout_marginTop="180dp"
        android:text="unBind Service"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"

        ></Button>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/finishBtn"

        android:layout_marginStart="100dp"
        android:layout_marginTop="260dp"
        android:text="Finish"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"

        ></Button>
</androidx.constraintlayout.widget.ConstraintLayout>

测试情况3:
多个Client,对应一个Service。

  • 1.点击MainActivity的bindService按钮
  • 2.点击MainActivity跳转到NextActivity页面
  • 3.点击NextActivity的bindService按钮
  • 4.点击NextActivity的unbind Service按钮
I/Wds: ----------------------------------------------------------------------
I/Wds: MainActivity 执行 bindService
I/Wds: BindService - onCreate - Thread = main
I/Wds: BindService - onBind - Thread = main
I/Wds: MainActivity - onServiceConnected
    MainActivity - getRandomNumber = 1658998008
I/Wds: ----------------------------------------------------------------------
I/Wds: MainActivity 启动 NextActivity

I/Wds: ----------------------------------------------------------------------
I/Wds: NextActivity 执行 bindService
I/Wds: NextActivity - onServiceConnected
I/Wds: NextActivity - getRandomNumber = -1479193691
I/Wds: ----------------------------------------------------------------------
I/Wds: NextActivity 执行 unbind Service

I/Wds: ----------------------------------------------------------------------
I/Wds: NextActivity 执行 finish

从上边三个简单的例子,总结可以得出如下图的 “绑定服务” 的流程

绑定服务生命流程

总结

  之前的Android开发序列已经讲述了Android的四大组件Activity和Intent,Activity的工作主要可以呈现给用户,可以看到的。Intent就像一个连接器,连接了底层环境,连接了各种组件,以及提供组件之间的数据。

  Service就一个默默在后台功能强大具有生命周期的“Activity”,其中Service还有很多扩展的服务,例如音乐播放、视频播放等等都是Service。Service用途非常广泛,以上只是大概总结了一下Service的使用,以及简单的列举了一下Service的使用例子。其中有很多方面都没有讲到。本篇只是抛砖引玉……

持续更新中……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值