清单文件中的Service
service 是一个可以在后台执行长时间操作不提供用户界面的组件。组件可以绑定service,与之交互,甚至进行进程间通信(IPC),例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。
一般有两种方式启动服务:
startService
当组件通过startService方式启动服务的时候,Service即处于启动状态,一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。已启动的服务通常执行单一操作,不会将结果返回给调用方。例如,它可能通过网络下载或上传文件。 操作完成后,服务会自行停止运行。
bindService
当组件通过bindService绑定服务的时候,Service即处于绑定状态。绑定服务提供了服务端-客户端接口,允许组件与服务进行交互,发送请求,获取结果,甚至是利用进程间通信(IPC)执行这些操作。多个组件可以同事绑定该服务,全部取消后,该服务即会被销毁。
1. Service在清单文件中声明
< service android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:isolatedProcess=["true" | "false"]
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string" >
. . .
< /service >
复制代码
元素属性说明:
属性名
作用
android:enabled
服务是否能被系统实例化。默认true
android:exported
其它应用程序的组件是否可以启动服务或与服务交互。
默认值取决于service组件是否包含intent-filter元素,
不包含,表示该服务不能被其它应用程序调用,即false。反之true。
android:isolatedProcess
如果设为 true ,则本服务将会运行于一个特殊的进程中。
该进程与系统其他部分隔离,且没有自己的权限。
与其通讯的唯一手段就是通过 Service API (绑定和启动)。
android:process
通常,应用程序的所有组件都运行在创建时的默认进程中。该进程的名称与程序包名相同。
如果本属性设置的名称以冒号(’:’)开头,则必要时会新建一个属于该程序私有的进程,服务将在该新进程中运行。
如果进程名称以小写字母开头,则服务将运行于一个以此名字命名的全局进程中,并赋予应有的访问权限。 这就允许分属于不同应用程序的多个组件共享同一个进程,以减少资源的占用。
2. 创建Service
2.1 Service 生命周期回调
创建Service需要继承Service,并重写一些回调方法,以处理service生命周期。
onStartCommand()
当一个组件(Activity)通过startService()请求启动服务时,系统将调用此方法。服务在工作完成后,可以通过调用stopSelf()或者stopService()来停止服务。
在该方法中,返回的int flags参数,有三种类型,代表了不同意义:
1. START_STICKY
当Service因内存不足而被系统kill后,一段时间后内存再次空闲时,系统将会尝试重新创建此Service,一旦创建成功后将回调onStartCommand方法,但其中的Intent将是null,除非有挂起的Intent,如pendingintent,这个状态下比较适用于不执行命令、但无限期运行并等待作业的媒体播放器或类似服务。
2. START_NOT_STICKY
当Service因内存不足而被系统kill后,即使系统内存再次空闲时,系统也不会尝试重新创建此Service。除非程序中再次调用startService启动此Service,这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。
3. START_REDELIVER_INTENT
当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand(),任何挂起 Intent均依次传递。与START_STICKY不同的是,其中的传递的Intent将是非空,是最后一次调用startService中的intent。这个值适用于主动执行应该立即恢复的作业(例如下载文件)的服务。
onBind()
当一个组件(Activity)通过bindService()绑定服务的时候,系统将回调此方法。此方法必须返回一个IBinder提供的接口,供客户端和服务端通信。如果不绑定,可以返回null
onCreate()
首次创建服务时,系统会回调此方法。如果服务已经在运行,则不会回调此方法。
onDestroy()
当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最后一个调用。
2.2 通过继承Service IntentService 创建Service
Service
这是适用于所有服务的基类。默认情况下,服务将使用应用的主线程,这会降低应用正在运行的所有 Activity 的性能。
IntentService
这是 Service 的子类,它使用工作线程逐一处理所有启动请求。如果不需要Service同时处理多个请求,这是最好的选择。IntentService 提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现。 您只需实现 onHandleIntent() 方法即可,该方法会接收每个启动请求的 Intent,使您能够执行后台工作。处理完所有启动请求后停止服务,不必调用 stopSelf()。
IntentService源码,是通过在onCreate方法中,通过HandlerThread创建了一个名字叫做ServiceHandler的Handler,工作在子线程。在onStart方法中,通过向ServiceHandler发送一个消息。在ServiceHandler中接收到onStartCommand方法中发送的消息,回调onHandleIntent(),然后通过 stopSelf(msg.arg1)关闭服务。
通过源码,我们发现使用bindService启动IntentService是不会回调onHandleIntent(),因为,bindService启动Service的时候,不会走onStartCommand方法,也就不会发送消息了。
以下具体源码:
1.onCreate方法中创建ServiceHandler
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
复制代码onStart方法中发送消息
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
复制代码ServiceHandler处理消息,并停止服务。
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
复制代码
3. Service 示例
3.1 继承自IntentService
1.在AndroidManifest文件中注册Service组件
复制代码
2.创建Service继承自IntentService
public class HelloIntentService extends IntentService {
private static final String TAG = "HelloIntentService";
public HelloIntentService() {
super("HelloIntentService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
try {
Log.e(TAG, "onHandleIntent: lll");
Thread.sleep(5000);
} catch (InterruptedException e) {
// Restore interrupt status.
Thread.currentThread().interrupt();
}
}
}
复制代码
3.在组件中启动Service
startService(new Intent(this, HelloIntentService.class));
复制代码
3.2 继承自Service
1. startService
public class HelloService extends Service {
private static final String TAG = "HelloService";
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate: lll");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand: lll ");
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind: lll ");
return null;
}
@Override
public boolean onUnbind(Intent intent) {
Log.e(TAG, "onUnbind: lll");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.e(TAG, "onDestroy: lll");
super.onDestroy();
}
}
复制代码
2. bindService
创建Service
public class LocalService extends Service {
private LocalBinder binder = new LocalBinder();
private final Random mGenerator = new Random();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
public class LocalBinder extends Binder {
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
复制代码
绑定Service
bindService(new Intent(this, LocalService.class), new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LocalService.LocalBinder binder = (LocalService.LocalBinder) service;
LocalService mService = binder.getService();
Toast.makeText(ServiceActivity.this, "LocalService getRandomNumber = " + mService.getRandomNumber(), Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, Service.BIND_AUTO_CREATE);
复制代码
3.3 使用 Messenger 简化客户端和服务端交互
当执行 IPC 时,为接口使用 Messenger 要比使用 AIDL 实现它更加简单,因为 Messenger 会将所有服务调用排入队列,而纯粹的 AIDL 接口会同时向服务发送多个请求,服务随后必须应对多线程处理。
对于大多数应用,服务不需要执行多线程处理,因此使用 Messenger 可让服务一次处理一个调用。如果服务必须执行多线程处理,则应使用 AIDL 来定义接口。
Messenger 实现通信步骤:
Service实现一个 Handler,由其接收来自客户端的每个调用的回调
Handler 用于创建 Messenger 对象(对 Handler 的引用)
Messenger 创建一个 IBinder,服务通过 onBind() 使其返回客户端
客户端使用 IBinder 将 Messenger(引用服务的 Handler)实例化,然后使用后者将 Message 对象发送给服务
服务在其 Handler 中(具体地讲,是在 handleMessage() 方法中)接收每个 Message。
利用 Messenger 通信实例
服务端MessengerService代码, 继承Service,重写onBind()方法。
创建一个ClientIncomingHandler用于接收客户端消息
利用IncomingHandler创建Messager对象
在onBind方法中将Messager生成的IBinder返回给客户端
public class MessengerService extends Service {
/**
* Command to the service to display a message
*/
public static final int MSG_SAY_HELLO = 1;
public static final int MSG_SAY_HELLO_REPLY = 2;
private class ClientIncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Log.e(TAG, "handleMessage: lll msg = " + msg.what);
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
//得到客户端的信使对象,并向它发送消息
Messenger client = msg.replyTo;
try {
Message message = Message.obtain();
message.arg1 = new Random().nextInt(100);
message.what = MSG_SAY_HELLO_REPLY;
client.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind: lll ");
return mMessenger.getBinder();
}
}
复制代码
2.客户端MessengerActivity代码:
创建一个 ServiceIncomingHandler,用于接收服务消息
绑定服务端,在绑定服务端的时候,获取服务端Messager对象,同时初始化客户端Messager对象,用于响应服务端向客户端发送的消息。
//创建一个 ServiceIncomingHandler,用于接收服务消息
private class ServiceIncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Log.e(TAG, "handleMessage");
switch (msg.what) {
case MessengerService.MSG_SAY_HELLO_REPLY:
Toast.makeText(MessengerActivity.this, "client received message from service : " + msg.arg1, Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
//绑定服务端,绑定成功后,获取服务端Messager对象,同时初始化客户端Messager对象
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
client = new Messenger(new ServiceIncomingHandler());
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName className) {
mService = null;
mBound = false;
}
};
//向服务端发送消息
public void sayHello() {
if (!mBound) {
return;
}
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
msg.replyTo = client;
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
复制代码
MessengerActivity.java 完整代码:
package com.littlezan.interviewpractice.service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.littlezan.interviewpractice.R;
/**
* ClassName: MessengerActivity
* Description:
*
* @author 彭赞
* @version 1.0
* @since 2018-07-24 19:05
*/
public class MessengerActivity extends AppCompatActivity {
/**
* Messenger for communicating with the service.
*/
Messenger mService = null;
Messenger client;
/**
* Flag indicating whether we have called bind on the service.
*/
boolean mBound;
public static void start(Context context) {
context.startActivity(new Intent(context, MessengerActivity.class));
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
client = new Messenger(new ServiceIncomingHandler());
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName className) {
mService = null;
mBound = false;
}
};
private static final String TAG = "MessengerActivity";
private class ServiceIncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Log.e(TAG, "handleMessage");
switch (msg.what) {
case MessengerService.MSG_SAY_HELLO_REPLY:
Toast.makeText(MessengerActivity.this, "client received message from service : " + msg.arg1, Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messager);
initView();
}
private void initView() {
findViewById(R.id.btnStartService).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MessengerActivity.this, MessengerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
});
findViewById(R.id.btnSayHello).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sayHello();
}
});
}
public void sayHello() {
if (!mBound) {
return;
}
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
msg.replyTo = client;
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
}
复制代码