Service与Client的通信方法
来源:互联网 发布:aspx网站源码修改教程 编辑:程序博客网 时间:2024/05/19 04:53
Service是Android四大组件之一,一般用来处理后台任务。Service运行于宿主进程的主线程中,所以如果处理耗时的后台任务,需要启动子线程来执行,也可以考虑使用IntentService。IntentService会启动一个工作线程,依次处理到达的工作请求。
Service运行于后台,就需要与前台的Client(一般是Activity)通信,本文整理了几种常见的通信方式供大家参考,如有遗漏,请补充,错误请指正,谢谢。
一、通过PendingIntent通信
按照官方文档的说法,PendingIntent是“A description of an Intent and target action to perform with it”。我们能够通过静态方法,getActivity(Context, int, Intent, int), getActivities(Context, int, Intent[], int), getBroadcast(Context, int, Intent, int), 和 getService(Context, int, Intent, int)创建它。
联系到本文的内容,我们选择使用,getBroadcast创建PendingIntent,如下:
mPendingIntent = PendingIntent.getBroadcast(this, requestCode, new Intent(ACTION), flags);
然后在启动Service时,把PendingIntent通过Intent传给Service。
Intent intent = new Intent(this, CustomService.class);intent.putExtra("PendingIntent", mPendingIntent);startService(intent);
因为PendingIntent继承了Parcelable接口,可以当作参数直接传递。
Service中获取传递的PendingIntent对象。
pendingIntent = intent.getParcelableExtra("PendingIntent”);
就可以使用它和Client通信了。
Client端注册一个BroadcastReceiver, 处理PendingIntent的ACTION和requestCode。
Service端使用PendingIntent的send()方法就可以和Client通信了。
此处执行send()和执行sendBroadcast()效果相同。
下面也正是我想说的第二种方法。
二、通过BroadcastReceiver通信
PendingIntent方法中主要也是借助于BroadcastReceiver进行通信的,此处我们要说单纯使用BroadcastReceiver方式进行通信的方法。这里我们使用LocalBroadcastManager注册,发送,解除注册广播。使用LocalBroadcastManager发送广播比发送全局广播更加安全和高效。
首先,在Activity中通过
mBroadcastManager = LocalBroadcastManager.getInstance(this);
获取LocalBroadcastManager的实例。
然后使用
mBroadcastManager.registerReceiver(mLocalReceiver, new IntentFilter(ACTION));mBroadcastManager.unregisterReceiver(mLocalReceiver);
注册和解除注册广播。
最后,在Service中同样使用
mLocalManager = LocalBroadcastManager.getInstance(getApplicationContext());
获取其实例,并在需要发送广播的地方,使用
mLocalManager.sendBroadcast(intent);
进行广播的发送,这样就可以进行简单的通信了。
三、通过IBinder通信
如果Service和Activity运行在同一个进程,可以考虑使用IBinder通信。
具体的步骤如下:
首先,在Service中创建Binder的实例,并提供公有的方法供Client调用。
public class MyBinder extends Binder { public MyService getService() { return MyService.this; } }
getService方法返回当前Service实例,Client获得该实例之后可以调用Service的公有方法。
在onBind回调方法中把Binder对象的实例返回给Client。
@Override public IBinder onBind(Intent intent) { return binder; }
Activity在bindService的时候,传入ServiceConnection对象。
bindService(intent, connection, BIND_AUTO_CREATE);
ServiceConnection用来监听Service的状态变化,运行于主线程。
它提供了两个回调的方法,onServiceConnected和onServiceDisconnected,分别在连接建立和断开的时候回调。
private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = ((MyService.MyBinder)service).getService(); isBound = true; } @Override public void onServiceDisconnected(ComponentName name) { isBound = false; mService = null; } };
在onServiceConnected的时候我们获得Service的时候,这样就可以调用公有方法了。记得在通信完成之后unbindService,这样在所有连接断开之后,Service也就停止运行了。
四、通过Messager通信
Messenger是一个指向Handler的引用,通过它可以给Handler发送消息。我们可以基于它实现跨进程的通信(IPC)。
如果你要求跨进程的多任务处理,那你可以选择AIDL实现,但是如果你不要求多线程处理,那Messenger完全可以满足你的需求,接下来我们具体看一下使用Messenger的通信的步骤。
首先,在Service中实现一个Handler,用于接收来自客户端的回调。
mHandlerThread = new HandlerThread("Messenger-Service");mHandlerThread.start();mHandler = new Handler(mHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_REGISTER_CLIENT: clients.add(msg.replyTo); break; case MSG_UNREGISTER_CLIENT: clients.remove(msg.replyTo); break; case MSG_SET_VALUE: Message message = Message.obtain(null, MSG_SET_VALUE); message.arg1 = msg.arg1; for (Messenger messenger : clients) { try { messenger.send(message); } catch (RemoteException e) { e.printStackTrace(); } } break; default: super.handleMessage(msg); } } };
这里我们使用了HandlerThread的方式,这里所有回调都运行在一个子线程中。如果你的Service不处理耗时操作,可以单纯的实现Handler即可。
其次,把我们的Messenger指向Handler。
mMessenger = new Messenger(mHandler);
在onBind中返回Messenger的IBinder。
public IBinder onBind(Intent intent) { return mMessenger.getBinder();}
然后,客户端从返回的IBinder中实例化Messenger,并通过该Messenger向Service的Handler发送消息。
private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = new Messenger(service); try { Message message = Message.obtain(null, MessengerService.MSG_REGISTER_CLIENT); message.replyTo = mMessenger; mService.send(message); message = Message.obtain(null, MessengerService.MSG_SET_VALUE); message.arg1 = this.hashCode(); mService.send(message); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { mService = null; } };
Service收到Messenger发送的消息后,在Handler的handleMessage中进行处理。
从上面的代码中我们可以看到,我们把客服端的Messenger作为replyTo发送给了Service,这样Service就可以使用该Messenger向对应客户端发送消息了。
下面附上Client端的全部代码,仅供参考。
public class MainActivity extends AppCompatActivity { private Messenger mService; private Messenger mMessenger; private boolean isBound; private TextView textView; class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MessengerService.MSG_SET_VALUE: textView.setText(Integer.toString(msg.arg1)); break; default: super.handleMessage(msg); } } } private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = new Messenger(service); try { Message message = Message.obtain(null, MessengerService.MSG_REGISTER_CLIENT); message.replyTo = mMessenger; mService.send(message); message = Message.obtain(null, MessengerService.MSG_SET_VALUE); message.arg1 = this.hashCode(); mService.send(message); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { mService = null; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mMessenger = new Messenger(new IncomingHandler()); textView = (TextView) findViewById(android.R.id.text1); } @Override protected void onStart() { super.onStart(); doBindService(); } @Override protected void onStop() { super.onStop(); doUnBindService(); } private void doBindService() { Intent intent = new Intent(this, MessengerService.class); bindService(intent, connection, Service.BIND_AUTO_CREATE); isBound = true; } private void doUnBindService() { if (isBound) { try { if (mService != null) { Message message = Message.obtain(null, MessengerService.MSG_UNREGISTER_CLIENT); message.replyTo = mMessenger; mService.send(message); } } catch (RemoteException e) { e.printStackTrace(); } unbindService(connection); isBound = false; } }}
五、通过AIDL通信
最后,我们说一下通过AIDL进行通信的方式。如果你不需要进行IPC通信,那么可以使用我们三中讲到的实现Binder的方式,如果你需要IPC,但不需要处理多线程的请求,那我们四中讲的Messenger方式应该也可以满足要求。如果你即需要IPC,又需要多线程处理,那AIDL可以满足你的要求。
下面我们说一下实现AIDL的步骤,以Android Studio为例。
首先,创建.aidl文件。在AS中,右键单击项目目录,选择新建AIDL文件。新建完成的AIDL文件在与Java同级目录的aidl目录中。这里需要注意:
新建的AIDL文件需要和你的Service的包名相同,否则不能自动生成接口的文件。
我们这里的文件内容如下:
// MyRemoteService.aidlpackage com.lkk.demo.aidl;// Declare any non-default types here with import statementsinterface MyRemoteService { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); int getPid();}
然后,实现通信所需的Service,在Service中实现自动生成的接口。
private final MyRemoteService.Stub stub = new MyRemoteService.Stub() { @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { } @Override public int getPid() throws RemoteException { return Process.myPid(); }};
在getPid方法中返回本进程的进程ID。
因为MyRemoteService.Stub继承了Binder,所以可以在onBind方法中直接返回stub.
public IBinder onBind(Intent intent) { return stub;}
现在可以在remote进程中连接本Service,并调动Stub暴露的方法了。
我们在上边的service的Manifest中声明可以启动它的action
<service android:name="com.lkk.demo.aidl.AIDLService"> <intent-filter> <action android:name="com.lkk.demo.AIDL_SERVICE" /> </intent-filter></service>
然后新建一个项目,按照上边的方法新建一个.aidl文件,AIDL的文件名和包名必须和上边的完全一致,否则会出错。把上边AIDL文件的内容原封不动的拷贝到此文件中。
下面就可以Bind Service了。
Intent intent = new Intent("com.lkk.demo.AIDL_SERVICE");intent.setPackage("lkk.download.com.aidldemo");bindService(intent, connection, BIND_AUTO_CREATE);
这里通过隐式的方式启动Service,必须设置包名,这是5.0之后的新要求。
此处的connection为:
private MyRemoteService service;private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { service = MyRemoteService.Stub.asInterface(iBinder); } @Override public void onServiceDisconnected(ComponentName componentName) { service = null; }};
这里我们必须使用MyRemoteService.Stub.asInterface把返回值转换成我们想要的接口,这样就可以调用它暴露的方法了。
textView.setText(String.valueOf(service.getPid()));
以上内容是我现在所能了解到的关于Service和Client通信的方法了,这里一一记录下来,以备后续查看,如果能帮到大家那是最好不过了。
内容上可能存在着错误和不足,希望大家不吝赐教,如果有其他没有涉及到的更好的方法可以comment一下,谢谢。
- Service与Client的通信方法
- Activity与Service之间的相互通信方法小结
- Activity 与 Service 的通信
- TCPServer与Client的通信代码
- 与服务service实现双向通迅方法 或叫 Service端和Client端的双向通信
- Android Service 与 Activity 通信方法
- Java____HTTP协议详解(web client通过http与service通信)
- Service的运用与activity的通信
- Android的Service与Activity通信
- Activity与Service通信的方式有三种:
- activity 与 service 数据的通信
- android activity与service之间的通信
- Activity与Service通信的方式
- Activity与Service通信的方式有三种:
- Activity与Service通信的方式有三种
- Activity与service 之间的通信
- Activity 与 Service 之间的通信
- 与Service通信的简单总结
- 在linux下搭载git服务器
- androidstudio自动快速更新
- css之框模型(盒子模型)、默认样式初始化、选择器和伪类
- HDU 5723 (最小生成树 树DP)
- iOS开发从入门到精通-- 步进器UIStepper和分栏控制器UISegmentedControl
- Service与Client的通信方法
- Python-print函数用法
- 矩形覆盖
- linux 设备树及节点引用
- PHP實時輸出
- zookeeper 集群安装(单点与分布式成功安装)摘录
- Oracle数据库创建实例
- android view继承关系
- Spring事务管理(2)-AOP注册解析器