四大组件之Service
四大组件之Service
Service是Android开发的基础之一,但是在实际项目开发中用到较少,有必要梳理一下,就先篇文章记录下。
Service的启动方式
与Activity一样,service需要在manifest.xml 中进行注册
<service android:name=".service.TestService" />
Service的启动方式有两种,接下来分别进行介绍
方式一
方法一使用上也与Activity跳转相似,通过Intent的方式进行启动。
startService(new Intent(ServiceActivity.this, TestService.class));
我们在相关生命周期中打印日志观察启动流程。
@Override
public void onCreate() {
Log.e(TAG, "onCreate:");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand:");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.e(TAG, "onDestroy:");
super.onDestroy();
}
当第一次点击按钮触发startService时,
service.TestService: onCreate:
service.TestService: onStartCommand:
第二次点击触发
service.TestService: onStartCommand:
发现后面start多少次都只会走onStartCommand函数,可以与Activity的onNewIntent进行类比,已经初始化的Service不需要被重复创建。
接着触发stopService后再启动Service,则会从OnCreate开始。
方式二
若要Activity与Service之间有交互,就需要通过方法二bindService的方式。创建ServiceConnection对象进行关联。
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
bindService(new Intent(ServiceActivity.this, TestService.class),connection, 1);
通过bindService的方式只会绑定一次
service.TestService: onCreate:
service.TestService: onBind:
同样的通过unbindService之后重复创建绑定流程。
Service的使用
相对于Activity来说,Service并没有页面,也就没有控件上的交互,单纯用于处理逻辑。
现在我们通过方式一执行下1s的耗时任务,使用Sleep进行模拟,然后进行Toast。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
Thread.sleep(1000);
Toast.makeText(getBaseContext(), "任务完成", Toast.LENGTH_LONG).show();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, "onStartCommand:");
return super.onStartCommand(intent, flags, startId);
}
但是如果任务时间过长,由于处于主线程中,就会造成ANR的现象。这个时候就可以创建一个线程执行,或者使用IntentService的方式。
IntentService封装了HandlerThread,创建启动线程并进行Loop。Handler接收消息处理任务后直接关闭Service。
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
另一种通过Bind的方式更加的灵活,可以根据需要执行任务。在TestService中定义LocalBinder类,在创建Service时进行初始化,执行bindService后返回该对象。
private LocalBinder mbinder = new LocalBinder();
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind:");
return mbinder;
}
public class LocalBinder extends Binder {
public TestService getservices() {
return TestService.this;
}
public void method1() {
Toast.makeText(getBaseContext(), "method1", Toast.LENGTH_LONG).show();
Log.e(TAG, "start:");
}
public void method2() {
Toast.makeText(getBaseContext(), "method2", Toast.LENGTH_LONG).show();
Log.e(TAG, "start:");
}
}
在Activity中的onServiceConnected时保存对象。
private TestService.LocalBinder localBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
localBinder = (TestService.LocalBinder) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
即可根据需要调用localBinder.method1()。
Service的跨进程通信
IBinder这个东西是用来实现跨进程通信的,像上面那样使用简直是杀鸡用牛刀。
使用Messenger来发送跨进程消息
首先在Manifest中将TestService设置其他进程
<service android:name=".service.TestService"
android:process=":remote"/>
在TestService中创建Messenger对象,在onBind中返回
private Messenger messenger = new Messenger(new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.e(TAG, "remoteService接收到了Activity发送的消息");
break;
default:
super.handleMessage(msg);
break;
}
}
});
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind:");
return messenger.getBinder();
}
在Activity中使用通过onBind返回的IBinder对象创建Messenger发送远程消息。
private Messenger mMessenger;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMessenger = new Messenger(service);
Message message = Message.obtain(null, 1);
try {
mMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
运行观察结果
service.TestService: remoteService接收到了Activity发送的消息
也可以设置message.replyTo 为本地的Messenger ,接收远程的消息。
Activity中
private Messenger remoteMessenger;
private Messenger localMessenger;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
remoteMessenger = new Messenger(service);
localMessenger = new Messenger(new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.e(TAG, "Activity接收到了remoteService返回的消息");
break;
default:
super.handleMessage(msg);
break;
}
}
});
Message message = Message.obtain(null, 1);
message.replyTo = localMessenger;
try {
remoteMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
Service中
private Messenger messenger = new Messenger(new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.e(TAG, "remoteService接收到了Activity发送的消息");
Message message = Message.obtain(null, 1);
try {
msg.replyTo.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
break;
}
}
});
就实现了远程与本地的跨进程双端通信。
使用AIDL来发送跨进程消息
首先创建一个RemoteServer的aidl文件,点击锤子编译一下。在debug中会生成对应RemoteServer.java类,生成的文件中有对应的Stub、Proxy和在aidl中写的request方法。
// RemoteServer.aidl
interface RemoteServer {
void request();
}
在Service中实现Binder方法
IBinder RemoteServer = new RemoteServer.Stub() {
@Override
public void request() throws RemoteException {
Log.e(TAG, "RemoteServer接收到了Activity发送的消息");
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind:");
return RemoteServer;
}
在Activity中使用与Messenger类似,通过IBinder创建代理对象进行调用。
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
try {
RemoteServer.Stub.asInterface(service).request();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
本文以Service的创建使用为主,AIDL以及binder通信的底层原理还在学习中。。。