Service是一种在Android应用后台的一种组件,没有自己的界面,不需要与用户交互。
最基本的两种用途:执行长时间时间运行的耗时操作,如网络下载,音乐播放,文件系统检测。
一种是组件间的交互(通过将某些功能以Service组件的形式进行封装,然后提供给其他应用组件调用,而不管这些组件是否与Service组件在同一进程中)。
Service组件有两种运行模式,一种是启动模式,一种是绑定模式。
如果Service组件是长时间运行的操作,则一般采用启动模式,采用启动模式的Service一般持续执行一个单一的操作,而且并不会向调用者返回任何信息。Service被启动后,将一直处于运行状态,即使调用startService的进程结束了,Service仍然还存在,直到有进程调用stopService,或者Service调用stopSelf自杀。
如果Service组件提供一种封装的功能供其他组件使用,则一般采用绑定模式。步骤: 通过调用组件对象的bindService方法启动Service对象实例,如果没有指定的Service实例被创建,则该方法调用Service的onCreate()方法来创建一个实例,实例启动后,将调用onBind()方法,onBind方法返回给客户端一个IBinder接口实例,IBinder允许客户端回调Service方法,不如得到Service运行的状态或其他操作。只要连接建立,Service就会一直运行,(不管客户是否保留Service的IBinder的引用)。通常IBinder是一个使用AIDL写成的复杂接口。
绑定模式下Service的生命周期:onCreate()--->onBind(只一次,不能多次绑定)---->onUnbind()--->onDestory()
两种Service运行模式不是完全隔离的,通过调用startService方法启动的Service对象实例也可以被其他进程通过bindService方法来绑定,此时,只有对Service实例既调用了stopService,也调用unbindService饿,这个Service才会结束。
1、启动模式下的Service
实现步骤:1、创建Service类,继承android.app.Service类
2、实现onStartCommand等生命周期方法。
3.在Andridmanifest.xml文件中配置Service组件。
由于Service是在主线程运行的,为避免产生应用无响应异常,必须在Service类的内部创建一个单独的线程,用于耗时的业务逻辑。
Android SDK提供了更好的方法,既IntentService(同时解决了多请求下线程同步的问题)。
IntentService:
1、在应用的主线程外创建一个单独的工作线程来执行传递到onStartCommand方法的Intent组件。
2、创建一个工作队列,它每次将一个Intent传递到onHandleIntent(),不需要考虑多线程的同步问题。
3、当所有请求被处理完成后,将自动停止服务而不需要显示调用stopSelf方法。
4、提供一个返回null值的onBind方法的默认实现。
5、提供了onStartCommand方法的默认时间,它将所有的Intent发送到一个工作队列,并进一步发送到onHandleInteng方法。
例子:
CountIntentService.java
package com.demo.startmodeservice;
import java.text.SimpleDateFormat;
import android.app.IntentService;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
public class CountIntentService extends IntentService {
int span;
public CountIntentService() {
super("CountIntentService");
// TODO Auto-generated constructor stub
}
@Override
protected void onHandleIntent(Intent arg0) {
// TODO Auto-generated method stub
Log.v("onHandleIntent", "服务启动时间 " +getCurrentTime());
Bundle b=arg0.getBundleExtra("attachment");
span=b.getInt("waitingtime");
long endTime = System.currentTimeMillis() + span*1000;
Log.v("onHandleIntent", "服务持续时间 " +span);
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
Log.v("onDestroy", "服务销毁时间 " +getCurrentTime());
super.onDestroy();
}
public String getCurrentTime(){
SimpleDateFormat tempDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String datetime = tempDate.format(new java.util.Date());
return datetime;
}
}
main.java
package com.demo.startmodeservice;
import android.app.Activity;
import android.view.View.OnClickListener;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class main2 extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main2);
Button Button1 = (Button) findViewById(R.id.button1);
Button1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent i=new Intent(main2.this, CountIntentService.class);
Bundle bd=new Bundle();
double a = Math.random()*10;
a = Math.ceil(a);
int randomNum = new Double(a).intValue();
bd.putInt("waitingtime", randomNum);
i.putExtra("attachment", bd);
startService(i);
}
});
Button Button2 = (Button) findViewById(R.id.button2);
Button2.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
stopService(new Intent(main2.this, CountIntentService.class));
}
});
// this.startService(new Intent(this, CountService.class));
}
@Override
protected void onDestroy() {
super.onDestroy();
// this.stopService(new Intent(this, CountService.class));
}
}
xml文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<Button android:text="启动" android:id="@+id/button1"
android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<Button android:text="ֹͣ停止" android:id="@+id/button2"
android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
</LinearLayout>
运行模式:
在启动模式下,Service中的业务逻辑主要在onStartCommand方法中实现,每次通过调用方法startService来启动Service,都会调用onStartCommand方法,其中方法的返回值决定了Service的运行模式。
1、START_NOT_STICKY:如果Sevice在启动后,被kill掉,并且没有新启动的Intent传给它,那么将Service移出启动状态并且不重新生成,知道再次显示调用Context.startService。适用场景:网上下载数据。
2、START_REDELIVER_INTENT:如果Service进程在启动后kill掉,那么它将会被重启,并且最后传给他的Intent通过onStartCommand(Intent ,int,int)会被重新传给他,这种模式保证了传给它Intent一定会被处理完毕,适用场景:关键业务处理。
3、START_STICKY:如果Service在它启动后被kill掉,那么Android将让Service继续保持started状态,但是不保留启动它的Intent,Android将重新创建Service实例,并执行onStartCommand方法,如果此时没有新的Intent请求,此时Intent的参数是null,这一点要特别注意。适用场景:后台播放音乐。这种运行模式的特点是需要显示启动并停止Service。
2、绑定模式:
Service组件往往是用来实现一些底层的操作和业务功能,供其他组件来调用。
外部组件与Service交互,都是通过Context.bind()方法来绑定服务,此时需要Service组件返回一个实现了IBinder接口的对象,外部组件正是通过此对象来调用Service组件的功能。
实现对Service组件功能的调用Service组件要做以下改造:
1、将Service组件的功能封装到一个接口中。
2、实现一个内部类,它继承Bind类(既实现IBinder接口),并实现Service组件的功能接口类。
3、在Service组件的onBind方法中,返回步骤2的内部类对象,供其他组件使用。
例子:
ICountService.java
package com.demo.bindservice;
public interface ICountService {
public abstract int getCount();
}
接口功能:一个返回计数信息的抽象方法。
LocalCountService.java
package com.demo.bindservice;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class LocalCountService extends Service {
private boolean threadDisable;
private int count;
private ServiceBinder serviceBinder = new ServiceBinder();
public class ServiceBinder extends Binder implements ICountService {
@Override
public int getCount() {
return count;
}
}
@Override
public IBinder onBind(Intent intent) {
Log.v("CountService", "onBind");
return serviceBinder;
}
@Override
public void onCreate() {
super.onCreate();
new Thread(new Runnable() {
@Override
public void run() {
while (!threadDisable) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
count++;
Log.v("CountService", "Count is " + count);
}
}
}).start();
/**/
Log.v("CountService", "oncreate");
}
@Override
public int onStartCommand(Intent intent, int flag, int startid) {
Log.v("CountService", "onStartCommand");
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
this.threadDisable = true;
Log.v("CountService", "on destroy");
}
public int getCount() {
return count;
}
}
新增一个内部类ServiceBinder,它扩展了Binder并实现了countService的功能接口IciuntService,onBind()方法,它返回一个内部类对象。
在绑定模式下,onStartCommand方法将不会被执行,要将业务逻辑放置到onCreate方法中,这一点需要注意。
只有仅仅希望服务在后台运行,而不需要向其他组件提供功能服务的情况下才能将业务逻辑实现放在onStartCommand方法中。
创建一个Activity来调用conutService的功能:
LocalBind.java:
package com.demo.bindservice;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
public class LocalBind extends Activity {
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LocalcountService = (ICountService) service;
Log.v("CountService", "on serivce connected, count is "
+ LocalcountService.getCount());
}
@Override
public void onServiceDisconnected(ComponentName name) {
LocalcountService = null;
}
};
private ICountService LocalcountService;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//第一个参数为Intent对象,定义要绑定哪个Service组件
//第二个参数为ServerConnection对象,它代表了到Service组件的链接
//第三个参数为常量,它代表绑定时如果没有Service实例存在则允许创建一个新的Service实例。
this.bindService(new Intent("com.demo.service.LocalCountService"),
this.serviceConnection, BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
this.unbindService(serviceConnection);
super.onDestroy();
}
}
在onDestory方法中,必须先调用onbindService方法来解除对Service组件的绑定,然后才能调用super.onDestory销毁对象。否则会造成内存泄露。
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.demo.bindservice" android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="7" />
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".LocalBind" 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=".LocalCountService">
<intent-filter>
<action android:name="com.demo.service.LocalCountService" />
</intent-filter>
</service>
</application>
</manifest>