- 服务简介
- 服务的生命周期
- 服务的停止
- 服务实现(本地)
- 服务与线程的区别
1.服务简介
Service 是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。
服务基本上分为两种形式:
1)启动
当应用组件(如 Activity)通过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。例如,它可能通过网络下载或上传文件。 操作完成后,服务会自行停止运行。
2)绑定
当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。
2.服务的生命周期
onStartCommand()
当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 如果您实现此方法,则在服务工作完成后,需要由您通过调用 stopSelf() 或 stopService() 来停止服务。(如果您只想提供绑定,则无需实现此方法。)
onBind()
当另一个组件想通过调用 bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法。在此方法的实现中,您必须通过返回 IBinder 提供一个接口,供客户端用来与服务进行通信。请务必实现此方法,但如果您并不希望允许绑定,则应返回 null。
onCreate()
首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或 onBind() 之前)。如果服务已在运行,则不会调用此方法。
onDestroy()
当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最后一个调用。
3. 服务的停止
启动服务必须管理自己的生命周期。也就是说,除非系统必须回收内存资源,否则系统不会停止或销毁服务,而且服务在 onStartCommand() 返回后会继续运行。因此,服务必须通过调用 stopSelf() 自行停止运行,或者由另一个组件通过调用 stopService() 来停止它。
一旦请求使用 stopSelf() 或 stopService() 停止服务,系统就会尽快销毁服务。
但是,如果服务同时处理多个 onStartCommand() 请求,则您不应在处理完一个启动请求之后停止服务,因为您可能已经收到了新的启动请求(在第一个请求结束时停止服务会终止第二个请求)。为了避免这一问题,您可以使用 stopSelf(int) 确保服务停止请求始终基于最近的启动请求。也就说,在调用 stopSelf(int) 时,传递与停止请求的 ID 对应的启动请求的 ID(传递给 onStartCommand() 的 startId)。然后,如果在您能够调用 stopSelf(int) 之前服务收到了新的启动请求,ID 就不匹配,服务也就不会停止。
4.服务实现(本地)
下面看一个小DEMO
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="启动服务"
android:onClick="start"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="停止服务"
android:onClick="stop"
/>
activity_main.xml
public class MainActivity extends AppCompatActivity {
private Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent = new Intent(this,MyServices.class);
}
public void start(View view){
intent.putExtra("data","下载的路径");
startService(intent);
}
public void stop(View view){
stopService(intent);
}
}
MainActivity.java
这里用的startService()来启动服务还有一种方式是bindService()
<service android:name=".MyServices"
android:exported="true"
></service>
AndroidManifest.xml
android:exported=”true”表示可以被其他组件启动
public class MyServices extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i("test","onBind");
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.i("test","onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String data=intent.getStringExtra("data");
Log.i("test","onStartCommand,"+data);
new MyThread(startId).start();
return super.onStartCommand(intent, flags, startId);
}
class MyThread extends Thread{
private int startId;
public MyThread(int startId) {
this.startId = startId;
}
@Override
public void run() {
super.run();
//耗时操作
for (int i = 0; i <10 ; i++) {
Log.i("test","i="+i);
SystemClock.sleep(1000);
}
//停止服务
//stopSelf();//当第一个线程执行完毕,则会停止服务
//所有的线程都执行完毕,才停止服务
stopSelf(startId);
}
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("test","onDestroy");
}
}
MyServices.java
一个简单的服务就完成了,注意:
1)当第一次点击启动服务时会调用onCreate()和onStartCommand(),当再次点击时则不会调用onCreate()
2)这里继承自Service,也可以继承自IntentService(如果是,则不需要new一个线程,耗时操作可以直接写在onHandleIntent方法中,但是要注意实现2个构造函数并且在无参构造中调用有参的)
3)如果在onStartCommand()方法中写了耗时操作会出现ANR问题(application not responsing应用程序未响应)
这里服务里面用到了线程,我们来看看两者有什么区别
5.服务与线程的区别
1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。
2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。
既然这样,那么我们为什么要用 Service 呢?其实这跟 android 的系统机制有关,我们先拿Thread 来说。Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。
举个例子:如果你的 Thread 需要不停地隔一段时间就要连接服务器做某种同步的话,该 Thread 需要在 Activity 没有start的时候也在运行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制之前创建的 Thread。因此你便需要创建并启动一个 Service ,在 Service 里面创建、运行并控制该 Thread,这样便解决了该问题(因为任何 Activity 都可以控制同一 Service,而系统也只会创建一个对应 Service 的实例)。
因此你可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。