面试:Service详解

目录

一、Service的分类

二、Service的应用场景,以及和Thread的区别

2.1、Service是什么

2.2、Service和Thread的区别

三、开启Service的两种方式以及区别

3.1、总结

3.2、startService()启动Service

3.2.1、通过 startService() 来启动方式的生命周期如下:

3.2.2、使用Service的步骤如下:

3.2.3、特点:

3.2.4、代码实例

3.3、通过 bindService() 来启动Service

3.3.1、通过 bindService() 来启动方式的生命周期如下:

3.3.2、使用Service的步骤如下:

3.3.3、特点:

3.3.4、代码实例


相关文章:面试题三:组件之Service系列

Android Service两种启动方式详解(总结版)(阅读量8w,讲解很详细)

service两种启动方式的区别

【Android一般进阶】startService和bindService混合使用分析

Service:在后台运行,默默地为用户提供功能,进行调度和统筹,如果一棵树的地上部分是Activity的话,它庞大的根须就是Service。

一、Service的分类

Service分为本地服务(LocalService)和远程服务(RemoteService):

1、本地服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外本地服务因为是在同一进程因此不需要IPC,也不需要AIDL。相应bindService会方便很多。主进程被Kill后,服务便会终止。

2、远程服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。

按使用方式可以分为以下三种:
1、startService 启动的服务:主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService;
2、bindService 启动的服务:该方法启动的服务可以进行通信。停止服务使用unbindService;
3、startService 同时也 bindService 启动的服务:停止服务应同时使用stepService与unbindService。

二、Service的应用场景,以及和Thread的区别

2.1、Service是什么

Service是一个可以在后台执行长时间运行操作而没有用户界面的应用组件。

提示:Service是运行在主线程的,所以它一定不能进行耗时操作。

2.2、Service和Thread的区别

Thread是程序执行的最小单元,它是分配CPU的基本单位。所以可以用它来执行一些异步操作,它是相对独立的。
而Service是一种android的机制,本地服务是依托于主线程运行的(相比Thread,Service并不是那么独立。),不能进行耗时操作,如果需要进行耗时操作,也要在Service新建一个子线程。也许有的同学会问,如果有耗时操作的话,我干嘛不直接在Activity中新建一个子线程呢?这是因为,当Activity被销毁后,就无法对新建的子线程进行操控了,而service则不存在这个问题,它可以一直对子线程进行控制。
应用场景上:耗时操作,新建一个Thread。需要长时间在后台运行且不需要交互的工作,就要交给service,例如:播放音乐、统计数据等。
严格来讲,Service和Thread没有任何联系。

三、开启Service的两种方式以及区别

3.1、总结

  • startService():开启Service,调用者退出后Service仍然存在。调用者不能调用服务里面的方法。
  • bindService():开启Service,调用者退出后Service也随即退出。调用者可以调用服务里面的方法。

Service生命周期:

  • 只是用startService()启动服务:onCreate() -> onStartCommand() -> onDestory
  • 只是用bindService()绑定服务:onCreate() -> onBind() -> onUnBind() -> onDestory
  • 同时使用startService()启动服务与bindService()绑定服务:onCreate() -> onStartCommnad() -> onBind() -> onUnBind() -> onDestory

3.2、startService()启动Service

3.2.1、通过 startService() 来启动方式的生命周期如下:

3.2.2、使用Service的步骤如下:

  1. 定义一个类,并继承 Service
  2. 在 AndroidManifest.xml 文件中配置此 Service
  3. 使用 Context 的 startService(Intent) 方法来启动 Service
  4. 不使用该服务时,调用 stopService(Intent) 方法停止此 Service

如果服务已经开启,就不会重复的执行 onCreate() ,而是调用 onStart() 或 onStartCommand()。而服务停止的时候调用 onDestory() 。

3.2.3、特点:

  • 一旦服务开启跟开启者就没有任何关系;
  • 开启者退出之后,服务还是可以在后台长期运行的。前提是没有调用 stopService(Intent);
  • 开启者不能调用服务里面的方法

3.2.4、代码实例

public class Test1Service extends Service {

    public Test1Service() {
    }

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

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
    }

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

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

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

在Activity中三次startService,之后stopService。

public class TestServiceActivity extends AppCompatActivity {

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

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

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


    }
}

打印日志如下:

TestForService: before StartService, Thread ID = 1
TestForService: onCreate - Thread ID = 1
TestForService: onStartCommand - startId = 1, Thread ID = 1
TestForService: onStartCommand - startId = 2, Thread ID = 1
TestForService: onStartCommand - startId = 3, Thread ID = 1

分析:
1.主线程打印出是1,所有回调方法中打印出的执行线程ID都是1,证明回调方法都是在主线程中执行的
2.三次调用startService,只触发一次onCreate回调,触发了三次onStartCommand回调,且startId分别为1,2,3。证明 多次startService不会重复执行onCreate回调,但每次都会执行onStartCommand回调

3.3、通过 bindService() 来启动Service

3.3.1、通过 bindService() 来启动方式的生命周期如下:

3.3.2、使用Service的步骤如下:

  1. 定义一个类,并继承 Service
  2. 在 AndroidManifest.xml 中配置此 Service
  3. 使用 Context 的 bindService(Intent, ServiceConnection, int) 方法来启动此 Service
  4. 不使用该服务时,调用 unbindService(ServiceConnection) 方法停止此 Service

绑定服务不会调用 onStart() 或 onStartCommand() 方法。

3.3.3、特点:

  • bind的方式开启服务,绑定服务,调用者挂了,服务也会跟着挂掉。
  • 绑定者可以调用服务里面的方法。

bindService启动服务特点:

  • 1.bindService启动的服务和调用者之间是典型的client-server模式。调用者是client,service则是server端。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会自行销毁

3.3.4、代码实例

  • 1.创建一个Test2Service继承Service(Server)
  • 2.创建Test2ServiceActivity,可以通过bindService绑定服务(client)
  • 3.创建Test3ServiceActivity,可以通过bindService绑定服务(client)
  • 4.Test2ServiceActivity可以跳转到Test3ServiceActivity

Test2Service创建如下:
要想让Service支持bindService调用方式,需要做以下事情:
1.在Service的onBind()方法中返回IBinder类型的实例。
2.onBInd()方法返回的IBinder的实例需要能够返回Service实例本身。通常,最简单的方法就是在service中创建binder的内部类,加入类似getService()的方法返回Service,这样绑定的client就可以通过getService()方法获得Service实例了。


public class Test2Service extends Service {

    public Test2Service() {
    }

    /**
     * client 通过 Binder 获取Service实例
     */
    public class MyBinder extends Binder {
        public Test2Service getTest2Service() {
            return Test2Service.this;
        }
    }

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

    private final Random mRandom = new Random();

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

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

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

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TEST_FOR_SERVICE, "Test2Service - onUnbind - from = " + intent.getStringExtra("from"));
        return super.onUnbind(intent);
    }

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

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

}

client端要做的事情:
1.创建ServiceConnection类型实例,并重写onServiceConnected()方法和onServiceDisconnected()方法。
2.当执行到onServiceConnected回调时,可通过IBinder实例得到Service实例对象,这样可实现client与Service的连接。
3.onServiceDisconnected回调被执行时,表示client与Service断开连接,在此可以写一些断开连接后需要做的处理。

创建Test2ServiceActivity,代码如下:


public class Test2ServiceActivity extends AppCompatActivity {

    private Test2Service mTest2Service;
    private boolean mIsBind;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            mIsBind = true;
            Test2Service.MyBinder myBinder = (Test2Service.MyBinder) binder;
            mTest2Service = myBinder.getTest2Service();
            Log.i(TEST_FOR_SERVICE, "Test2ServiceActivity - onServiceConnected");
            int num = mTest2Service.getRandomNumber();
            Log.i(TEST_FOR_SERVICE, "Test2ServiceActivity - getRandomNumber = " + num);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mIsBind = false;
            Log.i(TEST_FOR_SERVICE, "Test2ServiceActivity - onServiceDisconnected");
        }
    };

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

        findViewById(R.id.btnJumpToTest3ServiceActivity).setOnClickListener(v -> {
            startActivity(new Intent(Test2ServiceActivity.this, Test3ServiceActivity.class));
            Log.i(TEST_FOR_SERVICE, "----------------------------------------------------------------------");
            Log.i(TEST_FOR_SERVICE, "Test2ServiceActivity 启动 Test3ServiceActivity");
        });

        findViewById(R.id.btnFinish).setOnClickListener(v -> {
            Log.i(TEST_FOR_SERVICE, "----------------------------------------------------------------------");
            Log.i(TEST_FOR_SERVICE, "Test2ServiceActivity 执行 finish");
            finish();
        });

        findViewById(R.id.btnBindService).setOnClickListener(v -> {
            Intent intent = new Intent(Test2ServiceActivity.this, Test2Service.class);
            intent.putExtra("from", "Test2ServiceActivity");
            Log.i(TEST_FOR_SERVICE, "----------------------------------------------------------------------");
            Log.i(TEST_FOR_SERVICE, "Test2ServiceActivity 执行 bindService");
            bindService(intent, mConnection, BIND_AUTO_CREATE);
        });

        findViewById(R.id.btnUnbindService).setOnClickListener(v -> {
            if (mIsBind) {
                Log.i(TEST_FOR_SERVICE, "----------------------------------------------------------------------");
                Log.i(TEST_FOR_SERVICE, "Test2ServiceActivity 执行 unbindService");
                unbindService(mConnection);
            }
        });


    }

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

}

创建Test3ServiceActivity,代码如下:


public class Test3ServiceActivity extends AppCompatActivity {

    private Test2Service mTest2Service;
    private boolean mIsBind;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            mIsBind = true;
            Test2Service.MyBinder myBinder = (Test2Service.MyBinder)binder;
            mTest2Service = myBinder.getTest2Service();
            Log.i(TEST_FOR_SERVICE, "Test3ServiceActivity - onServiceConnected");
            int num = mTest2Service.getRandomNumber();
            Log.i(TEST_FOR_SERVICE, "Test3ServiceActivity - getRandomNumber = " + num);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mIsBind = false;
            Log.i(TEST_FOR_SERVICE, "Test3ServiceActivity - onServiceDisconnected");
        }
    };


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

        findViewById(R.id.btnBindService).setOnClickListener(v -> {
            Intent intent = new Intent(Test3ServiceActivity.this, Test2Service.class);
            intent.putExtra("from", "Test3ServiceActivity");
            Log.i(TEST_FOR_SERVICE, "----------------------------------------------------------------------");
            Log.i(TEST_FOR_SERVICE, "Test3ServiceActivity 执行 bindService");
            bindService(intent, mConnection, BIND_AUTO_CREATE);
        });

        findViewById(R.id.btnUnbindService).setOnClickListener(v -> {
            if (mIsBind) {
                Log.i(TEST_FOR_SERVICE, "----------------------------------------------------------------------");
                Log.i(TEST_FOR_SERVICE, "Test3ServiceActivity 执行 unbindService");
                unbindService(mConnection);
            }
        });

        findViewById(R.id.btnFinish).setOnClickListener(v -> {
            Log.i(TEST_FOR_SERVICE, "----------------------------------------------------------------------");
            Log.i(TEST_FOR_SERVICE, "Test3ServiceActivity 执行 finish");
            finish();
        });

    }

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


}

测试步骤1

step1: 点击Test2ServiceActivity的bindService按钮
step2: 再点击Test2ServiceActivity的unbindService按钮
Log输出:

I/TestForService: ----------------------------------------------------------------------
I/TestForService: Test2ServiceActivity 执行 bindService
I/TestForService: Test2Service - onCreate - Thread = main
I/TestForService: Test2Service - onBind - Thread = main
I/TestForService: Test2ServiceActivity - onServiceConnected
I/TestForService: Test2ServiceActivity - getRandomNumber = 334006729
I/TestForService: ----------------------------------------------------------------------
I/TestForService: Test2ServiceActivity 执行 unbindService
I/TestForService: Test2Service - onUnbind - from = Test2ServiceActivity
I/TestForService: Test2Service - onDestroy - Thread = main

总结调用bindService之后发生的事情:
1.client执行bindService()
2.如果Service不存在,则Service执行onCreate(),onBind()
3.client实例ServiceConnection执行onServiceConnected()方法

总结调用unbindService之后发生的事情:
1.client执行unbindService()
2.client与Service解除绑定连接状态
3.Service检测是否还有其他client与其连接,如果没有Service执行onUnbind()和onDestroy()

测试步骤2

step1: 点击Test2ServiceActivity的bindService按钮
step2: 再点击Test2ServiceActivity的Finish按钮
Log 输出:

I/TestForService: ----------------------------------------------------------------------
I/TestForService: Test2ServiceActivity 执行 bindService
I/TestForService: Test2Service - onCreate - Thread = main
I/TestForService: Test2Service - onBind - Thread = main
I/TestForService: Test2ServiceActivity - onServiceConnected
I/TestForService: Test2ServiceActivity - getRandomNumber = -329606351
I/TestForService: ----------------------------------------------------------------------
I/TestForService: Test2ServiceActivity 执行 finish
I/TestForService: Test2ServiceActivity - onDestroy
I/TestForService: Test2Service - onUnbind - from = Test2ServiceActivity
I/TestForService: Test2Service - onDestroy - Thread = main

总结:如果client销毁,那么client会自动与Service解除绑定。

测试步骤3

step1: 点击Test2ServiceActivity的bindService按钮
step2: 点击Test2ServiceActivity的startActivity Test3ServiceActivity按钮,切换到Test3ServiceActivity
step3: 点击Test3ServiceActivity中的bindService按钮
step4: 点击Test3ServiceActivity中的unbindService按钮
step5: 点击Test3ServiceActivity中的Finish按钮
step6: 点击Test2ServiceActivity中的unbindService按钮
得到Log:

I/TestForService: ----------------------------------------------------------------------
I/TestForService: Test2ServiceActivity 执行 bindService
I/TestForService: Test2Service - onCreate - Thread = main
I/TestForService: Test2Service - onBind - Thread = main
I/TestForService: Test2ServiceActivity - onServiceConnected
I/TestForService: Test2ServiceActivity - getRandomNumber = -551745334
I/TestForService: ----------------------------------------------------------------------
I/TestForService: Test2ServiceActivity 启动 Test3ServiceActivity
I/TestForService: ----------------------------------------------------------------------
I/TestForService: Test3ServiceActivity 执行 bindService
I/TestForService: Test3ServiceActivity - onServiceConnected
I/TestForService: Test3ServiceActivity - getRandomNumber = 883680462
I/TestForService: ----------------------------------------------------------------------
I/TestForService: Test3ServiceActivity 执行 unbindService
I/TestForService: ----------------------------------------------------------------------
I/TestForService: Test3ServiceActivity 执行 finish
I/TestForService: Test3ServiceActivity - onDestroy
I/TestForService: ----------------------------------------------------------------------
I/TestForService: Test2ServiceActivity 执行 unbindService
I/TestForService: Test2Service - onUnbind - from = Test2ServiceActivity
I/TestForService: Test2Service - onDestroy - Thread = main

总结bindService的生命周期:
1.点击Test2ServiceActivity的bindService按钮
第一次调用bindService会实例化Test2Service,然后执行其onBind()方法,得到IBinder类型的实例,将其作为参数传入Test2ServiceActivity的ServiceConnection的onServiceConnected方法中,标志着Test2ServiceActivityTest2Service建立了绑定

2.点击Test3ServiceActivity中的bindService按钮
由于Test2Service已处于运行状态,所以再次调用bindService不会重新创建它的实例,所以也不会执行Test2Service的onCreate()方法和onBind()方法。Test3ServiceActivity与Test2ServiceActivity共享IBinder实例。此时有两个client与Test2Service绑定

3.点击Test3ServiceActivity中的unbindService按钮
Test3ServiceActivity与Test2Service解除了绑定,当没有任何client与Service绑定时,才会执行Service的onUnbind()方法。此时,Test2ServiceActivity还在绑定连接中,所以不会执行Service的解绑方法

4.点击Test2ServiceActivity中的unbindService按钮
Test2ServiceActivity执行unbindService之后,Test2ServiceActivity与Test2Service就解除绑定了,这样就没有client与Test2Service绑定,这时候Android会销毁Test2Service,在销毁前会先执行Test2Service的onUnbind()方法,然后才会执行其onDestroy()方法,这样TestService就销毁了。

 

 

 

 

 

 

 

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值