Service的总结(二)

来源:互联网 发布:淘宝直播好做吗 编辑:程序博客网 时间:2024/05/19 02:44

关于Service的概念,作用,生命周期,基本使用,IntentService的相关知识请查看Service的总结(一),这篇文章主要是关于使用Messenger实现进程间通信和aidl。
创建提供绑定的服务时,必须提供 IBinder,用以提供客户端用来与服务进行交互的编程接口。可以通过三种方法定义接口,扩展Binder类,使用Messenger和使用AIDL。

一.使用扩展Binder类

一般情况下,用于服务仅供本地应用使用,不需要跨进程的情况,这时可以实现扩展 Binder类,使客户端通过该类直接访问服务中的公共方法。
具体使用详见Service的总结(一)中的绑定服务。

二.使用Messenger类

一般情况下,用于服务与远程进程通信。Messenger底层是对AIDL进行的封装,用起来比AIDL简单。这是执行进程间通信 (IPC) 的最简单方法。
Messenger 通过Message传递消息实现交互,会在单一线程中创建包含所有请求的队列,几乎可以不用担心多线程可能会带来的问题。
1.步骤:
(1)服务端实现一个Handler,由其接受来自客户端的每个调用的回调。
(2)使用实现的Handler创建Messenger对象。
(3)通过Messenger得到一个IBinder对象,并将其通过onBind()返回给客户端。
(4)客户端使用 IBinder 将 Messenger(引用服务的 Handler)实例化,然后使用后者将 Message 对象发送给服务。
(5)服务端在其 Handler 中(具体地讲,是在 handleMessage() 方法中)接收每个 Message。
2.具体使用:
客户端和服务器之间进行通信:
(1)客户端MainAcitvity.java:
根据onServiceConnected中的IBinder可以获得服务器端的Messenger,通过此Messenger的send方法传递给服务器消息和数据。创建本地Messenger对象接收从服务器传递过来的消息和数据。

public class MainActivity extends Activity implements View.OnClickListener{    private static final String TAG = "client";    private Button bindBtn;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initview();    }    /**     * 客户端的messenger     */    private Messenger messenger = new Messenger(new Handler(){        @Override        public void handleMessage(Message msg) {            switch(msg.what){//接收从服务器发送过来的消息                case 0:                    String str = (String) msg.getData().get("message");                    Log.d(TAG, "客户端收到消息handleMessage: "+str);                    break;            }        }    });    /**     * 服务端传过来的messenger     */    private Messenger servicerMessenger;    private void initview(){        bindBtn = (Button) this.findViewById(R.id.bind_service);        bindBtn.setOnClickListener(this);    }    @Override    public void onClick(View v) {        switch(v.getId()){            case R.id.bind_service://绑定服务                Intent intent = new Intent();                intent.setAction("com.liuwei.ipc.test");                final Intent eintent = new Intent(createExplicitFromImplicitIntent(this,intent));                bindService(eintent, connection, BIND_AUTO_CREATE);                break;        }    }    ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {//连接到服务            Log.d(TAG, "onServiceConnected: ");            servicerMessenger = new Messenger(service);            Message message = Message.obtain();            message.what = 1;            message.replyTo = messenger;//客户端的messenger传递过去给服务器->客户端中的msg.replyTo            Bundle bundle = new Bundle();            bundle.putString("message", "从客户端传递过来的消息");            message.setData(bundle);            try {                servicerMessenger.send(message);//客户端发送消息给服务器            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {            Log.d(TAG, "onServiceDisconnected: ");        }    };    @Override    protected void onDestroy() {        super.onDestroy();        unbindService(connection);    }    //隐式使用Intent    public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {        // Retrieve all services that can match the given intent        PackageManager pm = context.getPackageManager();        List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);        // Make sure only one match was found        if (resolveInfo == null || resolveInfo.size() != 1) {            return null;        }        // Get component info and create ComponentName        ResolveInfo serviceInfo = resolveInfo.get(0);        String packageName = serviceInfo.serviceInfo.packageName;        String className = serviceInfo.serviceInfo.name;        ComponentName component = new ComponentName(packageName, className);        // Create a new intent. Use the old one for extras and such reuse        Intent explicitIntent = new Intent(implicitIntent);        // Set the component to be explicit        explicitIntent.setComponent(component);        return explicitIntent;    }}

(2)服务器MessageBinderService.java:
创建服务器Messenger对象,在onbind中返回messenger.getBinder()传递给客户端,使用服务器Messenger构造出来的Handler接收从客户端发送过来的消息和数据,使用msg.replayTo.send(message)给客户端发送数据。

public class MessageBinderService extends Service {    private Messenger messenger = new Messenger(new Handler(){        @Override        public void handleMessage(Message msg) {            switch(msg.what){//收到从客户端发送过来的消息                case 1:                    String str = (String) msg.getData().get("message");                    Log.d("Service", "服务器收到消息handleMessage: "+str);                    Message message = Message.obtain();                    message.what = 0;                    Bundle bundle = new Bundle();                    bundle.putString("message", "从服务器发送数据");                    message.setData(bundle);                    try {                        msg.replyTo.send(message);//从服务器发送数据,msg.replyTo是客户端Messenger                    } catch (RemoteException e) {                        e.printStackTrace();                    }                    break;            }        }    });    @Nullable    @Override    public IBinder onBind(Intent intent) {        return messenger.getBinder();    }}

注册Service:

<service            android:exported="true"            android:enabled="true"            android:name=".MessageBinderService">            <intent-filter>                <action android:name="com.liuwei.ipc.test"/>                <category android:name="android.intent.category.DEFAULT"/>            </intent-filter>        </service>

从以上可以看出来:服务器和客户端有各自的Messenger对象,通过onbind返回给客户端服务器Messenger对象,通过客户端给服务器发送消息时的message.replyTo可以将客户端的Messenger传递给服务器,以此来实现两者之间的通信。
说明:
此处说明一个问题:在客户端代码中绑定服务使用的是

Intent intent = new Intent();                intent.setAction("com.liuwei.ipc.test");                final Intent eintent = new Intent(createExplicitFromImplicitIntent(this,intent));                bindService(eintent, connection, BIND_AUTO_CREATE);

而不是直接隐式调用

Intent intent = new Intent();intent.setAction("com.liuwei.ipc.test");bindService(intent, connection, BIND_AUTO_CREATE);

是因为在运行demo时出现了 IllegalArgumentException: Service Intent must be explicit错误,需要显式声明。
经查找相关资料使用上述解决办法。
参考:http://blog.csdn.net/shenzhonglaoxu/article/details/42675287

三.使用AIDL(Android Interface Definition Languag/Android 接口定义语言)

AIDL实现进程间的通信,尤其是用于有并发处理问题的需求,或者会有大量的并发请求。
步骤:
(1)在B应用中创建.aidl文件。
(2)在B应用中通过Stub实现aidl文件生成的接口。
(3)在B应用中创建一个Service,在服务的onBind()方法中返回实现了aidl接口的Binder对象。
(4)把B应用中aidl文件所在package连同aidl文件一起拷贝到客户端A应用。
(5)在A中绑定服务,在onServiceConnected方法中通过服务器传递过来的Binder对象得到aidl接口实例,通过调用实例的方法可以实现不同应用之间的通信。
具体使用:
以传递User对象为例进行说明:
1.对于服务器:
第一:创建aidl文件:
步骤:
(1)在main目录下新建aidl文件夹,在aidl文件夹下新建IUser.aidl文件,Studio会生成默认的aidl文件和包路径。
(2)在java目录包路径下新建User类,必须实现Parcelable序列化。
关于序列化请查看
http://www.jianshu.com/p/a60b609ec7e7
(3)在IUser.aidl同级目录下新建User.aidl文件,里面标明User所在的包和实现Parcelable。
(4)修改IUser.aidl文件,导入User类,根据需求写入所需要交互的接口。
效果:
这里写图片描述
User.java:

public class User implements Parcelable{    private String name;    private int age;    private User(Parcel in){        name = in.readString();        age = in.readInt();    }    public User(String name, int age){        this.name = name;        this.age = age;    }    public static final Creator<User> CREATOR = new Creator<User>() {        @Override        public User createFromParcel(Parcel source) {            return new User(source);        }        @Override        public User[] newArray(int size) {            return new User[size];        }    };    @Override    public int describeContents() {        return 0;    }    @Override    public void writeToParcel(Parcel dest, int flags) {        dest.writeString(name);        dest.writeInt(age);    }}

User.aidl:

package com.example.liuwei.aidlserver;parcelable User;

IUser.aidl:

package com.example.liuwei.aidlserver;import com.example.liuwei.aidlserver.User;interface IUser {    List<User> getUser();    void addUser(in User user);}

其中addUser中参数前面有in,表示由客户端传入参数。
参数前面必须写有in,out或是inout。
in:参数由客户端设置,由客户端传入参数值。
out:参数由服务端设置,由服务端返回值。
inout:客户端输入端都可以设置。
注意:User对象类所在文件目录在java下,不能在aidl目录下。否则编译会报错,找不到User类。
此问题参考:http://blog.csdn.net/sacco90725/article/details/42104085
第二:创建Service:
自定义MyService继承Service,创建Binder对象实现AIDL的接口文件中的方法并在onbind方法中返回。

public class MyService extends Service{    private List<User> users = new ArrayList<>();    @Nullable    @Override    public IBinder onBind(Intent intent) {        return stub;    }    IUser.Stub stub = new IUser.Stub() {        @Override        public List<User> getUser() throws RemoteException {            return users;        }        @Override        public void addUser(User user) throws RemoteException {            if(!users.contains(user)){                users.add(user);            }        }    };}

在清单文件中注册服务:

<service            android:name=".MyService">            <intent-filter>                <action android:name="com.liuwei.myService"></action>            </intent-filter>        </service>

2.对于客户端:
第一:创建aidl文件:
把上述服务器中aidl文件所在package连同aidl文件一起拷贝到客户端中,包括IUser.aidl,User.aidl,User.java,所在包和上述服务器时保持一致。如下所示:
这里写图片描述
第二:绑定服务,实现客户端和服务器交互:
使用bindService绑定服务,并在onServiceConnected方法中调用IUser.Stub.asInterface(service)这个方法通过服务端onBind方法返回的binder对象得到IUser的实例,调用它的方法,实现交互。

public class MainActivity extends Activity implements View.OnClickListener{    private static final String TAG = "client";    private IUser stub;    private Button bind_btn;    private Button unbind_btn;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initView();    }    private void initView(){        bind_btn = (Button)this.findViewById(R.id.bind_service);        unbind_btn = (Button) this.findViewById(R.id.unbind_service);        bind_btn.setOnClickListener(this);        unbind_btn.setOnClickListener(this);    }    ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            Log.d(TAG, "onServiceConnected: ");            stub = IUser.Stub.asInterface(service);            User user = new User("test", 12);            try {                stub.addUser(user);            } catch (RemoteException e) {                e.printStackTrace();            }            Log.d(TAG, "onServiceConnected: users"+user.toString());        }        @Override        public void onServiceDisconnected(ComponentName name) {            Log.d(TAG, "onServiceDisconnected: ");        }    };    @Override    public void onClick(View v) {        switch(v.getId()){            case R.id.bind_service:                Intent intent = new Intent();                intent.setAction("com.liuwei.myService");                bindService(createExplicitFromImplicitIntent(this, intent), connection, BIND_AUTO_CREATE);                break;            case R.id.unbind_service:                unbindService(connection);                break;        }    }    public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {        // Retrieve all services that can match the given intent        PackageManager pm = context.getPackageManager();        List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);        // Make sure only one match was found        if (resolveInfo == null || resolveInfo.size() != 1) {            return null;        }        // Get component info and create ComponentName        ResolveInfo serviceInfo = resolveInfo.get(0);        String packageName = serviceInfo.serviceInfo.packageName;        String className = serviceInfo.serviceInfo.name;        ComponentName component = new ComponentName(packageName, className);        // Create a new intent. Use the old one for extras and such reuse        Intent explicitIntent = new Intent(implicitIntent);        // Set the component to be explicit        explicitIntent.setComponent(component);        return explicitIntent;    }}

运行结果:
这里写图片描述
完整代码请点击这里。

原创粉丝点击