目录
3.2.1、通过 startService() 来启动方式的生命周期如下:
3.3、通过 bindService() 来启动Service
3.3.1、通过 bindService() 来启动方式的生命周期如下:
相关文章:面试题三:组件之Service系列
Android Service两种启动方式详解(总结版)(阅读量8w,讲解很详细)
【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的步骤如下:
- 定义一个类,并继承 Service
- 在 AndroidManifest.xml 文件中配置此 Service
- 使用 Context 的 startService(Intent) 方法来启动 Service
- 不使用该服务时,调用 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的步骤如下:
- 定义一个类,并继承 Service
- 在 AndroidManifest.xml 中配置此 Service
- 使用 Context 的 bindService(Intent, ServiceConnection, int) 方法来启动此 Service
- 不使用该服务时,调用 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方法中,标志着Test2ServiceActivity与Test2Service建立了绑定。
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就销毁了。