1、 概述
每个服务都继承Service基类。
可以连接到(或者bind to)一个正在运行的服务(如果没有在运行则启动它)。当连接成功后,你可以通过服务提供的接口来与它通信。服务通常产生另外的线程来进行占用时间长的任务。
Service是没有用户可见的界面,不与用户交互,而是在后台运行一段不确定的时间的应用程序组件。每个Service class 都必须在AndroidManifest.xml文件中有相应的<service>声明。Service可以通过Context.startService()和Context.bindService()来启动。
注意,与其他程序中的对象一样,Service运行他们的主进程中。这意味着,如果服务要进行消耗CPU或者阻塞的操作,它应该产生新的线程,在新线程里进行这些工作。
Service,后台运行,可交互这样的一个东西。它跟Activity的级别差不多,但是他不能自己运行,需要通过某一个Activity或者其他Context对象来调用, Context.startService() 和 Context.bindService()。
两种启动Service的方式有所不同。这里要说明一下的是如果你在Service的onCreate或者onStart做一些很耗时间的事情,最好在 Service里启动一个线程来完成,因为Service是跑在主线程中,会影响到你的UI操作或者阻塞主线程中的其他事情。
什么时候需要Service呢?比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务嘛,总是藏在后头的。
Service在android当中是一个比较重要的构件,她只在后台工作,没有Context。而且他也不是一个独立的线程,而是和main用一个线程。在service中可以直接使用Toast进行一些信息的提示。
但要注意,如果直接在service中直接启动另一个Activity则会出错。原因是没有设置flag,即在声明Intent的时候要设置flag:intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)。下面给出一个例子:
public class backgroundServer extends Service{
...
public void startMyActivity(){
Intent i = new Intent();
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.setClass(backgroundServer.this, myActivity.class);
startActivity(i);
}
...
}
这样就可以启动MyActivity了。(注意不要在AndroidManifest.xml中注册该Activity)
2、Service 的生命周期
系统有两种启动Service的方法。如果调用Context.startService(),那么系统将会retrieve这个Service(如果必要则创建它并调用它的onCreate()方法),然后使用客户端传递的参数调用它的onStart(Intent,int)方法。这是Service开始运行直到Context.stopService()或者stopSelf()方法被调用。注意,多次调用Context.startservice()不会嵌套(即使会有相应的onStart()方法被调用),所以无论同一个服务被启动了多少次,一旦调用Context.stopService()或者stopSelf(),他都会被停止。
客户端也可以使用context.bindservice()来得到一个Service的永久链接。这个方法也会在服务没有运行的条件下创建服务(调用onCreate()),但是不调用onStart()。客户端会得到服务的onBind(Intent)方法返回的IBinder对象,用来允许客户端回调服务的方法。只要连接已经建立服务会一直运行下去(无论客户端是否保留服务的IBinder对象的引用)。通常返回的IBinder对象是aidl中定义的复杂接口。
A service can be both started and have connections bound to it.这种情况下,系统会为之Service运行只要它被启动或者有一个或者多个对它的使用Context.BIND_AUTO_CREATE flag的链接。一旦没有着这一个的情况发生,Service的哦呢Destroy()会被调用,Service会被有效的结束。所有的cleanup(停止线程,反注册receiver等)在onDestroy()返回的时候完成。
Service的生命周期方法比Activity少一些,只有onCreate, onStart, onDestroy
我们有两种方式启动一个Service,他们对Service生命周期的影响是不一样的。
1 通过startService
Service会经历 onCreate -> onStart
stopService的时候直接onDestroy
如果是调用者(TestServiceHolder)自己直接退出而没有调用stopService的
话,Service会一直在后台运行。
下次TestServiceHolder再起来可以stopService。
2 通过bindService
Service只会运行onCreate, 这个时候 TestServiceHolder 和TestService绑定在一起
TestServiceHolder 退出了,Srevice就会调用onUnbind->onDestroyed
所谓绑定在一起就共存亡了。
要是这几个方法交织在一起的话,会出现什么情况呢?
一个原则是Service的onCreate的方法只会被调用一次,就是你无论多少次的startService又 bindService,Service只被创建一次。如果先是bind了,那么start的时候就直接运行Service的onStart方法,如果先是start,那么bind的时候就直接运行onBind方法。如果你先bind上了,就stop不掉了,对啊,就是stopService不好使了,只能先UnbindService, 再StopService,所以是先start还是先bind行为是有区别的。
使用Service的两种方法:
1、It can be started and allowed to run until someone stops it or it stops itself. In this mode, it's started by calling Context.startService() and stopped by calling Context.stopService(). It can stop itself by calling Service.stopSelf() or Service.stopSelfResult(). Only one stopService() call is needed to stop the service, no matter how many times startService() was called.
2、It can be operated programmatically using an interface that it defines and exports. Clients establish a connection to the Service object and use that connection to call into the service. The connection is established by calling Context.bindService(), and is closed by calling Context.unbindService(). Multiple clients can bind to the same service. If the service has not already been launched, bindService() can optionally launch it.
也可以在startService()后bindService()。
你应该实现Service的方法( they are public):
void onCreate()
void onStart(Intent intent)
void onDestroy()
通过实现这些方法,你可以监视Service生命周期中的两个嵌套循环:
1、Service的完全生命时间(entire lifetime)是指从调用onCreate()开始到onDestroy()返回的这段时间。Service在onCreate()中进行初始化,在onDestroy()中释放资源。
2、Service的活动生命时间(active lifetime)从onStart()开始,onStart()会处理从startService()方法传递过来的Intent对象。
Service不存在onStop()方法。
注意:只有通过startService()启动Service才会调用它的onStart()方法,通过onBind()启动的Service不会调用。
The onCreate() and onDestroy() methods are called for all services, whether they're started by Context.startService() or Context.bindService(). However, onStart() is called only for services started by startService().
如果Service运气其他程序bind到它,你需要实现其他的回调方法。
IBinder onBind(Intent intent)
boolean onUnbind(Intent intent)
void onRebind(Intent intent)
传递给bindService()的Intent对象会传递给onBind(),传递给unbindService()的Intent对象会传递给onUnbind()方法。如果这个Service允许连接,onBind()返回客户端和Service交互的通信频道(the communications channel that clients use to interact with the service)。如果有新的客户端链接到Service,onUnbind()方法可以请求调用onRebind()。
3、权限(Permissions)
在manifest 文件中声明的Service可以被全局访问(所有的应用程序都可以访问这个Service)。为了可以访问这个Service,其他的程序需要在他们的manifest文件中声明相应的<uses-permission> 来使用启动、停止或者绑定到这个Service。
另外,Service可以通过权限(通过在执行那个调用的实现之前调用checkCallingPermission(String))保护自己的IPC调用。
4、进程生命周期(Process LIfecycle)
Android系统会尽量保持使用Service的进程尽可能长(Service被启动或者有客户端绑定到Service)。当系统内存变低,系统需要kill现存的进程时,Service的hosting进程的优先级将会在下列的可能中保持更高。
If the service is currently executing code in its onCreate(), onStart(), or onDestroy() methods, then the hosting process will be a foreground process to ensure this code can execute without being killed.
If the service has been started, then its hosting process is considered to be less important than any processes that are currently visible to the user on-screen, but more important than any process not visible. Because only a few processes are generally visible to the user, this means that the service should not be killed except in extreme low memory conditions.
If there are clients bound to the service, then the service's hosting process is never less important than the most important client. That is, if one of its clients is visible to the user, then the service itself is considered to be visible.
注意:大多数时间你的Service是运行的,但在严重的内存压力下它也可能被系统kill。如果是这样,系统会在稍后尝试重新启动这个Service。An important consequence of this is that if you implement onStart() to schedule work to be done asynchronously or in another thread, then you may want to write information about that work into persistent storage during the onStart() call so that it does not get lost if the service later gets killed.
Other application components running in the same process as the service (such as an Activity) can, of course, increase the importance of the overall process beyond just the importance of the service itself.
package test.service;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class MyTestService extends Service {
private static final String TAG = "MyTestService";
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
Log.d(TAG, "onBind(Intent intent) called");
return null;
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
Log.d(TAG, "onCreate() called");
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.d(TAG, "onDestroy() called");
}
@Override
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
super.onStart(intent, startId);
Log.d(TAG, "onStart(Intent intent, int startId) called");
}
@Override
public boolean onUnbind(Intent intent) {
// TODO Auto-generated method stub
Log.d(TAG, "onUnbind(Intent intent) called");
return super.onUnbind(intent);
}
public class LocalBinder extends Binder {
public MyTestService getService() {
return MyTestService.this;
}
}
}
package test.service;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class ServiceDemo extends Activity {
private static final String TAG = "ServiceDemo";
private Button mBtnStart, mBtnStop, mBtnBind, mBtnUnbind;
private MyTestService mService;
private boolean isBinded;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mBtnStart = (Button) findViewById(R.id.btnStartService);
mBtnStop = (Button) findViewById(R.id.btnStopService);
mBtnBind = (Button) findViewById(R.id.btnBindService);
mBtnUnbind = (Button) findViewById(R.id.btnUbindService);
mBtnStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startService();
}
});
mBtnStop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stopService();
}
});
mBtnBind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bindService();
}
});
mBtnUnbind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService();
}
});
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = ((MyTestService.LocalBinder)(service)).getService();
Log.i(TAG, "in onServiceConnected");
}
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
Log.i(TAG, "in onServiceDisconnected");
}
};
private void startService() {
Intent i = new Intent(this, MyTestService.class);
startService(i);
}
private void stopService() {
Intent i = new Intent(this, MyTestService.class);
stopService(i);
}
private void bindService() {
Intent i = new Intent(this, MyTestService.class);
bindService(i, mServiceConnection, Context.BIND_AUTO_CREATE);
isBinded = true;
}
private void unbindService() {
if(isBinded) {
unbindService(mServiceConnection);
isBinded = false;
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TableRow android:layout_width="fill_parent" android:gravity="center"
android:layout_height="wrap_content">
<TextView android:id="@+id/text" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:minLines="2" />
</TableRow>
<TableRow android:layout_width="fill_parent" android:gravity="center"
android:layout_height="wrap_content" android:paddingTop="24px">
<Button android:id="@+id/btnStartService" android:text="StartService"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<Button android:id="@+id/btnStopService" android:text="StopService"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
</TableRow>
<TableRow android:layout_width="fill_parent" android:gravity="center"
android:layout_height="wrap_content" android:paddingTop="24px">
<Button android:id="@+id/btnBindService" android:text="BindService"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<Button android:id="@+id/btnUbindService" android:text="UbindService"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
</TableRow>
</TableLayout>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="test.service" android:versionCode="1" android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".ServiceDemo" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MyTestService" android:enabled="true"
android:process=":local" >
</service>
</application>
<uses-sdk android:minSdkVersion="4" />
</manifest>