前几次我们分享分享了四大组件里面的广播,这次还是想继续和大家一起来探讨探讨组件里面的Service服务
那么服务到底是什么呢?简单来说,服务就是在 执行耗时操作(后台),更深层次来说的话Service
是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。
服务基本上分为两种形式:
-
启动
-
当应用组件(如 Activity)通过调用
startService()
启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。例如,它可能通过网络下载或上传文件。 操作完成后,服务会自行停止运行。
绑定
-
当应用组件通过调用
bindService()
绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。 -
-
要创建服务,您必须创建
Service
的子类(或使用它的一个现有子类)。在实现中,您需要重写一些回调方法,以处理服务生命周期的某些关键方面并提供一种机制将组件绑定到服务(如适用)。 应重写的最重要的回调方法包括:-
当另一个组件(如 Activity)通过调用
startService()
请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 如果您实现此方法,则在服务工作完成后,需要由您通过调用stopSelf()
或stopService()
来停止服务。(如果您只想提供绑定,则无需实现此方法。) -
当另一个组件想通过调用
bindService()
与服务绑定(例如执行 RPC)时,系统将调用此方法。在此方法的实现中,您必须通过返回IBinder
提供一个接口,供客户端用来与服务进行通信。请务必实现此方法,但如果您并不希望允许绑定,则应返回 null。 -
首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用
onStartCommand()
或onBind()
之前)。如果服务已在运行,则不会调用此方法。 - 当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最后一个调用。
onStartCommand()
onBind()
onCreate()
onDestroy()
如果组件通过调用
startService()
启动服务(这会导致对onStartCommand()
的调用),则服务将一直运行,直到服务使用stopSelf()
自行停止运行,或由其他组件通过调用stopService()
停止它为止。如果组件是通过调用
bindService()
来创建服务(且未调用onStartCommand()
,则服务只会在该组件与其绑定时运行。一旦该服务与所有客户端之间的绑定全部取消,系统便会销毁它。 -
当另一个组件(如 Activity)通过调用
-
-
大家要是想看看更加详细的内容可以用谷歌浏览器去Android的官网------developer.android.google.cn
-
接下来,我们就对服务来做一个小测试,看看他的生命周期四个方法的运用
-
首先,我在main.xml里面设置了两个按钮控件,一个取名为启动服务,另一个为停止服务,分别给他们设置一个onclick事件
<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" />
-
然后在mainactivity里面通过intent写两个onclick事件方法,开启服务、停止服务
-
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,MyServices2.class); } public void start(View view){ intent.putExtra("data","下载的路径"); startService(intent); } public void stop(View view){ stopService(intent); } }
接着,我们新建一个activity,取名为MyServices,让它继承Service,重写它里面的方法,这里只有OnBind可以自己重写出来,其他三个都需要手打重写 -
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"); } //ANR: application not responsing应用程序未响应 //why: 耗时操作写到了主线程 // how:子线程 @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"); } }
在这里,我设置了一个线程,这样当我点击开启服务按钮的时候,for循环里面的内容就会开始打印,我这里将i<10,那么当服务启动时,后台就会开始打印0到9,前面介绍服务时说到它有两种关闭方式,我上面的代码其实都有体现,public void stop(View view){ stopService(intent); }
这个是直接关闭服务
@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);
}
}
这个是关闭服务自己,如果你一次性开启了多个服务,
stopSelf();当第一个线程执行完毕,则会停止服务,这是
不设置ID的情况,如果设置了ID,那么就是
所有的线程都执行完毕,才停止服务,但这个时候就需要写一个构造方法了
到这里,我们再来讲讲IntentService,它也是属于Service服务的一种,但是他们是有一定区别的
public class MyServices2 extends IntentService {
public MyServices2() {
super("");
}
public MyServices2(String name) {
super(name);
}
//类似Service中的:onStartCommand
@Override
protected void onHandleIntent(Intent intent) {
for (int i = 0; i <10 ; i++) {
Log.i("test","i="+i);
SystemClock.sleep(200);
}
}
}
根据上面的代码,大家可以很清晰的看到,IntentService只有一个生命周期方法onHandleIntent,但是他类似Service中的:onStartCommand
他们两个的区别是:
IntentService是AndroidOS用来处理耗时操作的,IntentService是继承于Service并处理异步请求的一个类,在IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统Service一样,同时,当任务执行完后,IntentService会自动停止,而不需要我们去手动控制。另外,可以启动IntentService多次,而每一个耗时操作会以worker queue的方式在IntentService的onHandleIntent回调方法中执行,并且,每次只会执行一个worker Thread,执行完第一个再执行第二个,以此类推。而且,所有请求都在一个单线程中,不会阻塞应用程序的主线程(UI Thread),同一时间只处理一个请求。
IntentService在处理事务时,还是采用的Handler方式,创建一个名叫ServiceHandler的内部Handler,并把它直接绑定到HandlerThread所对应的子线程。 ServiceHandler把处理一个intent所对应的事务都封装到叫做onHandleIntent的虚函数;因此我们直接实现虚函数onHandleIntent,再在里面根据Intent的不同进行不同的事务处理就可以了。另外,IntentService默认实现了Onbind()方法,返回值为null。
IntentService它的内部其实是有一定的先后顺序的,比如,当我开启多个服务,不关闭时,依照上面的例子,Service是多个服务同时运行,而IntentService则是当第一个服务运行完毕之后再接着去运行第二个服务