通过这种方式启动的服务属于比较标准的一类。一般来说,从定义一个服务到使用一个服务,要遵循一下几个步骤:
- 自己实现一个类,该类继承service类
- 在清单文件中注册自己的服务(实际上,四大组件的使用,都需要在清单文件中进行注册)
- 调用startservice来初始化并且启动自己的服务
- 调用stopservice来停止我们已经运行的服务。
通过上面的图,我们也可以观察到,这种服务的生命周期是怎样的:
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
}
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/start_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start Service" />
<Button
android:id="@+id/stop_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Stop Service" />
private Button startService;
private Button stopService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService = (Button) findViewById(R.id.start_service);
stopService.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.start_service:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent); // 启动服务
break;
case R.id.stop_service:
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent); // 停止服务
break;
default:
break;
}
}
在之前实现service类的时候,我们可以看到有一个抽象方法被重新实现—onBind()。service和activity的通信大致是通过这样的一种过程来实现的:
- 首先在service的类中,实现一个类,这个类继承于Binder,这个类中你可以定义一些你自己的方法,这些方法就是以后要在服务中执行的方 法,这个类是服务类的子类。然后,创建一个这个binder类的实例,在Onbind函数中返回这个binder对象。这个对象就像是一个开着的接口,向 外提供了子类内部将要执行的操作。
- 服务对外通信的接口做好之后,想让活动和服务建立连接的话,就要在两者之间创建一个连接点。这个连接点的工作由 ServiceConnection类的对象来实现,我们要在activity中创建一个ServiceConnection的匿名类,类中要重写两个方 法: onServiceDisconnected,onServiceConnected。之后在创建一个这个类的对象,到此为止,连接点也做好了,那么如何 让服务和活动通过这个连接点连接起来进行通讯工作呢?
- 因为在实现服务的时候,给我们返回了一个Binder的对象作为接口,可以看见在重写onServiceConnected方法的时候,里面有一 个参数就是Binder类型的参数,通过把这个Binder对象强制类型转换成我们在服务中自己实现binder类的那个对象的类类型之后,就可以通过它 来调用我们服务内部的一些操作。这就为活动来指挥服务内部的运行,提供了良好的接口和条件。
- 而对于acitivity来说的话,我们可以通过bindservice这个启动服务的函数,在参数中把要启动服务的intent和创建的连接点 ServiceConnection对象传递过去,如此一来,在绑定成功之后就可以创建服务并且连接在一起,然后自动调用连接点中 onServiceconnected方法,在这个方法的内部就可以通过参数中的binder对象来指挥服务。在服务和活动解除绑定的时候,自然也会调用 连接点处的onServiceDisconnected方法。
- 我们在活动中,现在通过Binder对象所能执行的是service类的子类所提供的方法,如果想执行service类内部的方法,可以在这个接口中再定义一个函数,返回服务类的一个对象,便可以执行服务类中的成员方法了。
那么BinderService的生命周期就由以下几个部分构成:
public static final int UPDATE_TEXT = 1;
private Button start_Service;
private Button stopService;
private Button bindService;
private Button unbindService;
private Button changeUI;
private TextView tvContent;
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
downloadBinder = (MyService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
public void onClick(View v) {
switch (v.getId()) {
case R.id. start_service:
Intent startIntent = new Intent( this, MyService. class);
startService(startIntent);
break;
case R.id. stop_service:
Intent stopIntent = new Intent( this, MyService. class);
stopService(stopIntent);
break;
case R.id. bind_service:
Intent bindIntent = new Intent( this, MyService. class);
bindService(bindIntent, connection, BIND_AUTO_CREATE); //绑定服务
break;
case R.id. unbind_service:
unbindService( connection); //解绑服务
break;
}
}
}
private DownloadBinder mBinder = new DownloadBinder();
class DownloadBinder extends Binder {
public void startDownload() {
Log. d( "MyService", "startDownload executed");
}
public void stopDownload() {
Log. d( "MyService", "stopDownload executed");
}
public int getProgress() {
Log. d( "MyService", "getProgress executed");
return 0;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@TargetApi(Build.VERSION_CODES. JELLY_BEAN)
@Override
public void onCreate() {
super.onCreate();
Log. d( "MyService", "onCreate executed");
Notification notification = new Notification.Builder( this)
.setContentTitle( "This is Title")
.setContentText( "This is content")
.setSmallIcon(R.mipmap. ic_launcher)
.build();
Intent notificationIntent = new Intent( this, MainActivity. class);
PendingIntent pendingIntent = PendingIntent. getActivity( this, 0, notificationIntent, 0);
startForeground( 1, notification);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log. d( "MyService", "onStartCommand executed");
new Thread( new Runnable() {
@Override
public void run() {
//线程逻辑
Log. d( "MyService Thread", "Thread executed");
stopSelf(); //终止自己的线程
}
}).start(); //开启线程
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log. d( "MyService", "onDestory executed");
}
}
< LinearLayout xmlns: android = "http://schemas.android.com/apk/res/android"
xmlns: tools = "http://schemas.android.com/tools"
android :layout_width= "match_parent"
android :layout_height= "match_parent"
android :orientation= "vertical"
android :paddingBottom= "@dimen/activity_vertical_margin"
android :paddingLeft= "@dimen/activity_horizontal_margin"
android :paddingRight= "@dimen/activity_horizontal_margin"
android :paddingTop= "@dimen/activity_vertical_margin"
tools :context= ".MainActivity">
< Button
android :id= "@+id/start_service"
android :layout_width= "wrap_content"
android :layout_height= "wrap_content"
android :text= "Staet Service" />
< Button
android :id= "@+id/stop_service"
android :layout_width= "wrap_content"
android :layout_height= "wrap_content"
android :text= "Stop Service" />
< Button
android :id= "@+id/bind_service"
android :layout_width= "wrap_content"
android :layout_height= "wrap_content"
android :text= "Bind Service" />
< Button
android :id= "@+id/unbind_service"
android :layout_width= "wrap_content"
android :layout_height= "wrap_content"
android :text= "UnBind Service" />
< Button
android :id= "@+id/changeUI"
android :layout_width= "wrap_content"
android :layout_height= "wrap_content"
android :text= "Change UI"/>
< TextView
android :id= "@+id/tv_content"
android :layout_width= "match_parent"
android :layout_height= "wrap_content"
android :textSize= "30sp"
android :text= "Hello World"/>
</ LinearLayout>
服务的运行一般来说关心两个问题:
- 如果处理服务中的耗时操作
- 如果在服务中的逻辑运行完毕之后,自动关闭该服务。
那么能否有一种service可以不用我们自己手动单独开辟线程,自动的来完成我们的耗时操作呢,并且在服务的逻辑处理完成之后,可以自动的帮我们结束服务,不用我们自己再调用stopself。这种服务就是intentservice。
intentservice的使用方法和普通的service是一样的,但是在intentservice的实现中,我们需要继承自intentservice的类,并且要为这个类重载一个没有参数的构造方法。具体的实现步骤有以下几个比较重要的:
- 继承Intentservice并提供一个无参数的构造函数,在方法内部调用父类的构造函数。
- 重写父类当中onhandleintent这个方法,把我们要在服务当中处理的逻辑过程都放在这个方法内完成
- 启动这个service。 这个服务的特殊之处就在于onhandleintent这个方法,这个方法能够代替我们手动进行的两个工作:这个方法内部的代码都是在子线程中运行的,并 且在这个方法执行到末尾的时候,会自动的帮我们关闭service。这样一来,就不用我们再手动的开子线程和关闭服务了。其实在 intentservice中也是通过开启子线程来完成服务内的逻辑过程的执行的。
public MyIntentService() {
super( "MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
Log. d( "MyIntentService", "onHandleIntent executed ,Thread id is "+Thread. currentThread());
}
@Override
public void onDestroy() {
super.onDestroy();
Log. d( "MyIntentService", "onDestroy executed");
}
Log. d( "MainActivity", "MainActivity Thread id is "+Thread. currentThread());
Intent intentService = new Intent( this,MyIntentService. class);
startService(intentService);
- 如果是使用startservice的方式来启动服务的话,即使启动这个服务的activity可能处于destory状态,但是服务本身还是在继续运行的,除非这个服务所在进程被销毁,那么这个服务也会停止。
- 如果是使用bindservice来启动服务的话,一旦启动这个服务的activity变为destory的状态的时候,那么这个服务也随之会销毁,因为这个服务是和特定的activity进行绑定的。
- startservice一旦启动之后,activity就不能再插手服务运行过程中的事情,比如我想了解服务运行的状态等等,都不能够实现,也就是说,activity只能是给服务发一个信号来启动他,但是并不能和它进行交互。
- bindservice就不一样了,它是依靠binder进程间通讯的机制,在服务本身内部向外提供接口,以便让activity能够和服务进行交互操作,这样一来,activity就可以在服务运行的过程当中来查看服务运行的情况。
- bindservice虽然向外部提供了一个binder的对象,通过这个对象可以调用service内部的一些方法,但是在activity 中,通过binder调用的函数并不是直接一步到位调用了服务中的相关成员函数,而只是得到一个类似指针和映射的东西,通过这些再去找在服务内部真正存在 的方法。总的来说把,bindservice向外部提供的,只是内部信息的一个映射,并不是真正的把内部的函数地址等等信息提供出去。
- 如果有一个service和多个activity同时绑定的话,那么即使一个activity解除了service的绑定,但是并不会回调 unbind和ondestory的方法,只不过在绑定多个actiity的时候要注意绑定的顺序。> 其实bind和start两种服务最本质的区别就是,Bind是一种服务器/客户端模型,能够让服务的启动者和服务的执行者交互,但是start不行。
Service到底是什么?进程 ?线程?
这个问题也是自己刚刚遇到的,一开始有点模糊不清,包括不明白service为什么和调用他的activity是在同一个主线程中的。网上的资料, 大家还是最好有一点甄别能力,尤其是论坛上面的,有些明显的错误来误导提问者。当然,我写的东西可能也是有错的,如果有不同意见,大家可以一起讨论。 我对这个问题的理解是这样的:<br> 首先,service不是一线程,也不是一个进程,它是一个组件。它的生存运行,和线程的生命周期没有一点关系。也就是说,线程的死活和它是没有关系的。 但是,service的运行,毕竟需要一些条件,比如内存,CPU这些资源。然而,这些资源,操作系统只会分配给进程,这也就是为什么,不管我们用什么种 类的service,只要我们把这个进程干掉,服务神马的也就都是浮云了。所以,服务虽然是一个组件,但是它的运行还是要依赖于进程。
服务分为远程服务和本地服务,本地服务就是在我们这个应用进程之内启动并运行的。一般来说,服务的运行并不会新开一个线程,无论是哪一种service他们都是运行在进程中的主线程中。这也就是为什么不能在服务中直接执行大量的耗时操作,要单独新开一个子线程完成的原因。
还有一个问题:startservice在启动之后,其实activity销毁了,但是它还是继续在运行,直到我们显式的调用 stopservice或者stopself,他才会停止。而且,即使我们在服务运行的过程中stopservice,但是服务还是会完成这一次的逻辑, 也就是说,我们在一个服务执行的过程中,不能中断本次执行的这个过程。这一点对于Bindservice和startservice都是一样。