Service的总结(二)

本文详细介绍了Android中使用Messenger和AIDL实现进程间通信的方法。包括Messenger的基本原理、使用步骤及示例代码;AIDL的基础概念、实现过程及案例演示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于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.youkuaiyun.com/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.youkuaiyun.com/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;
    }
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值