Android(安卓)四大组件之 Service (一)
Service 是一个应用程序组件,android 四大组件之一,可以长时间的执行一个后台操作,不提供用户界面,其它应用程序组件可以启动一个Service,让一个 Service 运行在后台,一个组件可以绑定到一个Service,进行进程间的通信(IPC)。
IPC(InterProcess communication)进程间的调用
RPC(Remote Process communication)远程进程调用
使用线程还是服务?
即使用户没和应用交互,服务仍然会可以在后台运行。因此,你应该在需要时才使用它。 如果你不想在主线程执行一个任务,而是仅当用户和应用交互时执行该任务,你应该创建新线程而不是服务。例如,你只想 activity运行时才播放音乐,就可以在onCreate()方法里创建一个线程,在onStart()方法里启动该线程,在onStop()方法里关闭它。也可以考虑用AsyncTask或者HandlerThread,代替传统的Thread类.关于进程的更多信息详见-进程和线程文档。记住如果你使用了一个服务,默认它仍然会在主线程里运行,所以,遇到耗时或异步操作,可以创建一个新线程,然后再启动该服务。
Service 的两种类型
-
Started
通过Context.startService() 来启动一个Service,不返回操作结果
-
Bound
应用组件通过 Context.bindSerivce() 来绑定一个 Service,Service 提供一个 client-server 接口,供组件与 Service 进行交互,甚至通过IPC跨进程。一次可以有多个组件绑定到 service, 当所有都解绑后,service 销毁。
虽然两种方式分开介绍,但是一个服务是可以同时使用这两种方式的,简单的说就是同时实现 onStartCommand() 和 onBind()
Caution:默认情况下,一个Service运行在调用该Service组件的同一个线程中,所以在这种情况下,使用Service完成耗时操作时,必须使用线程来完成。
基础
创建 servcie:自定义一个类,继承 Service 或其已有的子类
生命周期方法:onCreate()->onStartCommand()->onDestroy()
-
onStartCommand()
其它组件调用 startService() 时执行,service启动并在后台运行
在服务外部停止一个服务使用:stopService()
在服务内部停止一个服务使用:stopSelf(startId) ,指定要停止的服务startId
-
onBind()
其它组件调用 bindService() 时执行,应当返回 IBinder,此方法必须实现,不想被绑定可以返回 null
-
onCreate()
服务第一次创建,系统都会调用该方法,目的是一次性执行该过程(在调用onStartCommand()或onBind()方法前)。如果服务已经运行,方法不会被调用。
-
onDestroy()
释放各种资源
在 manifest 中声明一个 service
<manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest>
android:enabled="true" 表示当前Service处于激活状态
android:exported="false" 表示该服务只能在本应用内使用
创建一个 Started Service
继承 Servcie
因为默认服务使用应用的主线程,可能会降低程序的性能,因此可能需要创建一个新线程来执行工作,需要自己控制结束
onStartCommand() 的返回值必须是以下之一:
-
START_NOT_STICKY :非粘性的服务
指定该返回值,如果服务被kill掉,该服务将不会重新创建
-
START_STICKY:粘性的服务
指定该返回值,如果服务被kill掉,该服务将会重新被创建,但不会发送Intent
-
START_REDELIVER_INTENT 重新发送最后一个Intent的粘性服务
指定该返回值,如果服务被kill掉,该服务将会重新被创建,并且会发送最后一个Intent
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// 处理程序从线程中收到的消息
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// 通常我们在这里做一些事情,例如下载文件。
// 这里,我们只让程序睡眠5s
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
// 使用startId停止服务,所以我们在处理其他任务的过程中不会停止该服务.
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
// 启动线程开启服务,注意默认服务通常运行在进程的主线程中,所以我们创建了另一个线程。
// 而且我们也不想阻塞程序。另外我们把该线程放在后台运行,这样cpu耗时操作就不会破坏应用界面.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// 获得线程的Looper,用于我们的Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// 对于每个启动请求,发送消息用于启动一个任务,传递start ID,这样当完成此任务时就知道谁发出该请求.
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// 如果服务已被杀死,执行return后,线程重启.
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// 如果我们不提供服务绑定,返回null
return null;
}
@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
继承 IntentServcie
如果我们要执行的业务要在单独的线程中完成,并且完成后自动结束,此时我们可以使用Service为我们提供的另一个子类IntentService
IntentService类可以做这些事情:
- 从应用的主线程当中创建一个默认的线程执行所有的intents发送给onStartCommand()方法,该线程从主线程分离.
- 创建工作队列,每次传递一个intent 给onHandleIntent()方法实现,所以不必担心多线程.
- 所有的请求被处理后服务停止,所以你永远都不必调用stopSelf()函数.
- 默认实现onBind()方法返回null.
- 默认实现onStartCommand()方法是发送一个intent给工作队列,然后发送给onHandleIntent()方法的实现。
IntentServcie 是 Service 的子类,使用了 Worker 来处理请求,不需要调用 stopSelf(),onBind()默认返回 null
你需要做的就是写构造方法并实现onHandleIntent()
栗子:
public class HelloIntentService extends IntentService {
public HelloIntentService() {
super("HelloIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}
如果你打算也重载其他回调方法,例如onCreate(), onStartCommand(),或者onDestroy(),确保调用父类对应的方法,如此,IntentService实例化的对象才能正常工作。
注意:当服务完成任务后停止它,非常重要,因为这样做可以避免浪费系统资源和节约电量.如果有必要,其他组件可以调用 stopService()方法停止服务。即使你绑定服务,如果该服务收到一个onStartCommand()的调用,就必须停止服务。
在前台运行一个服务
前台服务是一种考虑到系统内存不足,但是用户已经意识到而且不想关闭的服务。前台服务必须提供一个 Notifaction,表示该服务在持续进行中,意思是这种提示除非服务停止或者从所有的前台服务中移除才能消失.
将服务运行至前台,需要调用 startForeground()
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text), System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title), getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
Caution: The integer ID you give to startForeground() must not be 0.
Service 的生命周期
启动的服务当另一个组件调用startService(),服务就被创建,然后一直运行下去,直到服务自己调用stopSelf()方法停止该服务.其他组件也可以通过调用stopService()方法停止一个服务.系统会销毁该服务。
绑定的服务当另一个组件(客户端)调用bindService(),服务就被创建,客户端然后通过IBinder接口和服务交互.客户端可以调用unbindService()关闭和服务的连接.多客户端可以绑定到相同的服务,所有的客户端都解除绑定之后,系统销毁该服务.(服务不必像上面那样自己销毁).
这两种方式不是完全不同的,就是说,你绑定一个已经调用startService()启动的服务。例如,一个后台音乐服务可以通过把一个意图传递给startService()启动。稍后,用户可能执行某些操作或获得当前歌曲的信息,一个activity通过调用bindService()绑定某个服务.所有的客户端全都取消绑定之前,调用stopService()或stopSelf()方法不会停止服务.
生命周期接口
public class ExampleService extends Service {
int mStartMode; // indicates how to behave if the service is killed
IBinder mBinder; // interface for clients that bind
boolean mAllowRebind; // indicates whether onRebind should be used
@Override
public void onCreate() {
// The service is being created
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// The service is starting, due to a call to startService()
return mStartMode;
}
@Override
public IBinder onBind(Intent intent) {
// A client is binding to the service with bindService()
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
// All clients have unbound with unbindService()
return mAllowRebind;
}
@Override
public void onRebind(Intent intent) {
// A client is binding to the service with bindService(),
// after onUnbind() has already been called
}
@Override
public void onDestroy() {
// The service is no longer used and is being destroyed
}
}
更多相关文章
- SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
- tcping测试服务器TCP端口
- ContentProvider的工作过程
- Android网络通信库Volley简介(Google IO 2013)
- android 桌面组件 App widget的使用
- XE5 Android(安卓)开发实现手机打电话和发短信
- android Service--服务
- - Android深入浅出Binder机制
- Android(安卓)WebView安全研究