Android 进程间通信Service 两种实现方式 AIDL 、Messenger

这篇我们讲下如何使用service实现进程间通信 。通常讲到这里大家都会想到aidl,其实不然,aidl只是其中的一种方式,并且使用起来,我个人感觉太不灵活了,相对Messenger更加灵活。

整个过程我们还是借助于 Activity 跨进程通信  中使用的项目

1.AIDL 跨进程通讯 

   aidl 是个简写,全拼android interface difine language(android自定义接口语言)

  服务端:

    1.1先从新建开始,选中main 右键选择aidl file 新建,会在main 下生成aidl文件夹,我的新建默认名字叫

IMyAidlInterface,也就是IMyAidlInterface.aidl文件。这个就是我们要定义的接口类,默认会有
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
        double aDouble, String aString);接口,也就是默认支持六种基本数据类型

先写两个基本数据类型,再自定义一个自定义接口

package com.example.intentmode;
import com.example.intentmode.Person;

// Declare any non-default types here with import statements

interface IRemoteService {
            void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
            int getPid();
            String getName(int id);
            Person getPerson(int id);
}

先别着急,

1.2 Person getPerson(int id);是会报错的。现在定义Person类,在和IRemoteService类平级目录下新建Person类,并且必须实现Parcelable接口,为啥Serializable 接口不行呢,在android 中都使用Parcelable接口因为Serializable的序列化效率相对较低。

Person 如下:

public class Person implements Parcelable {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.name);
        dest.writeInt(this.age);
    }

    public Person() {
    }

    protected Person(Parcel in) {
        this.name = in.readString();
        this.age = in.readInt();
    }

    public static final Creator<Person> CREATOR = new Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel source) {
            return new Person(source);
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };
}

1.3 新建Person.aidl类 :

// Person.aidl
package com.example.intentmode;

// Declare any non-default types here with import statements

parcelable Person;

1.4 新建服务类 ServiceAIDL ,继承自Service

 

public class ServiceAIDL extends Service {
    private String [] names={"吕布","关羽","赵子龙","张飞"};
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    private final IMyAidlInterface.Stub mBinder=new IMyAidlInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            System.out.println("Thread: " + Thread.currentThread().getName());
            System.out.println("basicTypes aDouble: " + aDouble +" anInt: " + anInt+" aBoolean " + aBoolean+" aString " + aString);
        }

        @Override
        public int getPid() throws RemoteException {
            System.out.println("Thread: " + Thread.currentThread().getName());
            System.out.println("RemoteService getPid ");
            return android.os.Process.myPid();
        }

        @Override
        public String getName(int id) throws RemoteException {
            return names[id];
        }

        @Override
        public Person getPerson(int id) throws RemoteException {
            Person person=new Person();
            try {
                person.setAge(20);
                person.setName(names[id]);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return person;
        }
    };
}

 

在这里我们定义了接口的返回数据,用于客户端调用时返回想要的数据,其实叫他服务端另一端客户端是有问题的,他们是可以相互调用的,我们暂且这么叫吧。这里有的朋友可能会报attempting to use incompatible return type ,这是因为使用自定义类型要在接口类中导入包名,

package com.example.csdnactivity;

// Declare any non-default types here with import statements
//这个要手动导入下
import com.example.csdnactivity.Person;
interface IMyAidlInterface {
    /**
     * 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();
               String getName(int id);
               Person getPerson(int id);
}

1.5 注册service 并且设置标签 action

  <service android:name=".ServiceAIDL">
            <intent-filter>
                <action android:name="com.example.intentmode"/>
            </intent-filter>
        </service>

需要注意的是,aidl写完后需要重新构建下,会在build --> generated--> source --> aidl --> debug--> 接口类

如果没有,有可能是代码有错误了。

服务端结束,下面看客户端

客户端:

1.1 将服务端的aidl文件夹粘贴到客户端的main文件下。

1.2 绑定客户端service

        
//服务端设置的action
        intent.setAction("com.example.intentmode");
        //服务端的包名
        intent.setPackage("com.example.csdnactivity");
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
package com.example.csdnactivity1;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.example.csdnactivity.IMyAidlInterface;
import com.example.csdnactivity.Person;

/**
 * Created by LCT
 * Time:2018/12/24 13:49.
 * Annotation:
 */
public class AIDLActivity extends Activity implements View.OnClickListener {
    private static final String TAG = "AIDLActivity";
    /**
     * 获取远aidl程数据
     */
    private Button mGetData;
    IMyAidlInterface iMyAidlInterface;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.aidl_activity);
        initView();
        bindService();
    }

    private void bindService() {
        Intent intent = new Intent();
        //服务端设置的action
        intent.setAction("com.example.intentmode");
        //服务端的包名
        intent.setPackage("com.example.csdnactivity");
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            /**
             * IBinder 转为IRemoteService接口
             */
            iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    private void initView() {
        mGetData = (Button) findViewById(R.id.get_Data);
        mGetData.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            default:
                break;
            case R.id.get_Data:
                /**
                 * 通过iMyAidlInterface 获取数据
                 */
                try {
                    if (iMyAidlInterface != null) {
                        String name = iMyAidlInterface.getName(0);
                        iMyAidlInterface.basicTypes(12, 1223, true, 12.2f, 12.3, "有梦就要去追,加油!");
                        Toast.makeText(this, name, Toast.LENGTH_SHORT).show();
                        Log.d(TAG, "onClick: name:"+ name);
                        Person person = iMyAidlInterface.getPerson(0);
                        String str=person.getName() + "_" +person.getAge();
                        Log.d(TAG, "onClick: person:"+ str);
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
        }
    }
}

客户端代码较少,现在可以运行服务端,如果你使用的是studio 使用了默人新建的build.gradle 文件,那么现在项目可能运行不起来,会提示你:错误:找不到符号符号:类 Person 我用的3.2的编辑器,即使这样还是不太智能需要手动写点东西

在build.gradle  添加如下代码

 sourceSets {
        main {
            manifest.srcFile 'src/main/AndroidManifest.xml'
            java.srcDirs = ['src/main/java', 'src/main/aidl']
            resources.srcDirs = ['src/main/java', 'src/main/aidl']
            aidl.srcDirs = ['src/main/aidl']
            res.srcDirs = ['src/main/res']
            assets.srcDirs = ['src/main/assets']
        }
    }

这样问题就解决了。

现在可以运行服务端,然后启动客户端,获取数据了。

1.Messenger跨进程通讯

  原理与实现

      这个相对aidl相对比较简单,并不是代码实现少,而是比较好理解,首先在服务端会有一个Messenger 它绑定了一个Hander ,客户端同样有一个Messenger 同样绑定了一个Hander ,然后客户端绑定服务通过service onBind返回服务端的Messenger对象,然后将客户端的Messenger    Message.replyTo=cMessenger到服务端的messenger中,然后发送消息,服务端在Hander收到消息,得到客户端的Messenger ,这样就各自持有对方messenger对象,互相发送消息了。接着我们看看具体实现。

服务端

1.1 新建Messenger对象

  Messenger有两个构造函数分别是

public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

handler用于绑定Messenger,接收消息。

IBinder用于将服务端的IBinder构造为Messenger对象

 @Override
    public void onCreate() {
        super.onCreate();
        sMessenger=new Messenger(handler);
    }

1.2获取Messenger 的IBinder通过onBind返回

 public IBinder onBind(Intent intent) {
        return sMessenger.getBinder();
    }

1.3Handler 接收数据处理,将收到的客户端Messenger保存,并处理相关逻辑

 Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg!=null) {
                Log.d(TAG, "handleMessage:  收到客户端发来的消息");
                /**
                 * 保存客户端Messenger,用于向客户端发送消息
                 */
                if (msg.replyTo!=null) {
                    cMessenger=msg.replyTo;
                }
                Bundle bundle=msg.getData();
                String ms;
                if (bundle !=null) {
                    ms=bundle.getString("serviceData");
                }else {
                    Log.d(TAG, "handleMessage: null bundle");
                    return;
                }
                if (ms==null) {
                    Toast.makeText(ServiceMessenger.this,"客户端收到空消息" ,Toast.LENGTH_LONG).show();
                    return;
                }
                /**
                 * 如果收到消息1 就往客户端发送一条消息
                 */
                if (ms.equals("1")) {
                    Message message=Message.obtain();
                    Bundle bundle1=new Bundle();
                    bundle1.putString("clientData","2");
                    message.setData(bundle1);
                    try {
                        cMessenger.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }else {
                    Toast.makeText(ServiceMessenger.this,"收到客户端发来的消息"+ms,Toast.LENGTH_LONG).show();
                }
            }

        }
    };

服务端的完整代码

public class ServiceMessenger extends Service {
    private static final String TAG = "ServiceMessenger";
    /**
     * 服务端Messenger
     */
    Messenger sMessenger;
    /**
     * 客户端 Messenger
     */
    Messenger cMessenger=null;
    @Override
    public void onCreate() {
        super.onCreate();
        sMessenger=new Messenger(handler);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return sMessenger.getBinder();
    }
    Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg!=null) {
                Log.d(TAG, "handleMessage:  收到客户端发来的消息");
                /**
                 * 保存客户端Messenger,用于向客户端发送消息
                 */
                if (msg.replyTo!=null) {
                    cMessenger=msg.replyTo;
                }
                Bundle bundle=msg.getData();
                String ms;
                if (bundle !=null) {
                    ms=bundle.getString("serviceData");
                }else {
                    Log.d(TAG, "handleMessage: null bundle");
                    return;
                }
                if (ms==null) {
                    Toast.makeText(ServiceMessenger.this,"客户端收到空消息" ,Toast.LENGTH_LONG).show();
                    return;
                }
                /**
                 * 如果收到消息1 就往客户端发送一条消息
                 */
                if (ms.equals("1")) {
                    Message message=Message.obtain();
                    Bundle bundle1=new Bundle();
                    bundle1.putString("clientData","2");
                    message.setData(bundle1);
                    try {
                        cMessenger.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }else {
                    Toast.makeText(ServiceMessenger.this,"收到客户端发来的消息"+ms,Toast.LENGTH_LONG).show();
                }
            }

        }
    };
}

1.4.注册service 并设置action,action用来客户端绑定服务时使用

 <service android:name=".ServiceMessenger">
            <intent-filter>
                <action android:name="com.android.serviceMessenger.action" />
            </intent-filter>
        </service>

服务端结束。

 

客户端

1.1 创建客户端Messenger ,绑定handler,用于接收消息

cMessenger = new Messenger(handler);

1.2 绑定服务并监听绑定状态,获取服务端的Messenger对象。

 private void bindMessengerService() {
        Intent intent = new Intent();
        //服务端设置的action
        intent.setAction("com.android.serviceMessenger.action");
        //服务端的包名
        intent.setPackage("com.example.csdnactivity");
        bindService(intent, connMessenger, Context.BIND_AUTO_CREATE);
    }

获取服务端Messenger,并将客户端的Messenger发送到服务端

 private ServiceConnection connMessenger = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            /**
             * 得到服务端的messenger
             */
            sMessenger = new Messenger(service);
            if (sMessenger == null) {
                Log.d(TAG, "onServiceConnected:  messenger is null");
                return;
            }
            /**
             * 发送客户端的messenger到服务端
             */
            Message message = Message.obtain();
            message.replyTo = cMessenger;
            try {
                sMessenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

 

1.3 发送消息到服务端

 String str = mSendDataToServiceEdit.getText().toString();
                if (str != null) {
                    Message message = Message.obtain();
                    Bundle bundle = new Bundle();
                    bundle.putString("serviceData",str);
                    message.setData(bundle);
                    try {
                        sMessenger.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }

完整代码类

 

public class MessengerActivity extends Activity implements View.OnClickListener {
    private static final String TAG = "MessengerActivity";
    /**
     * 输入发送的信息 通过Messenger 发送
     */
    private EditText mSendDataToService;
    /**
     * SENd
     */
    private Button mSendData;
    /**
     * 服务端 Messenger
     */
    Messenger sMessenger;
    /**
     * 客户端 Messenger
     */
    Messenger cMessenger;
    /**
     * messenger
     */
    private Button mMessengerButton;
    /**
     * 输入发送的信息 通过Messenger 发送
     */
    private EditText mSendDataToServiceEdit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.messenger_activity);
        initView();
        initMessenger();
    }

    private void initMessenger() {
        cMessenger = new Messenger(handler);
        bindMessengerService();

    }

    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Bundle bundle = msg.getData();
            if (bundle != null) {
                String ms = bundle.getString("clientData");
                if (ms.equals("2")) {
                    Toast.makeText(MessengerActivity.this, "收到服务端发来的数据了" + ms + "", Toast.LENGTH_LONG).show();
                }
            }
        }

        ;
    };

    private void bindMessengerService() {
        Intent intent = new Intent();
        //服务端设置的action
        intent.setAction("com.android.serviceMessenger.action");
        //服务端的包名
        intent.setPackage("com.example.csdnactivity");
        bindService(intent, connMessenger, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection connMessenger = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            /**
             * 得到服务端的messenger
             */
            sMessenger = new Messenger(service);
            if (sMessenger == null) {
                Log.d(TAG, "onServiceConnected:  messenger is null");
                return;
            }
            /**
             * 发送客户端的messenger到服务端
             */
            Message message = Message.obtain();
            message.replyTo = cMessenger;
            try {
                sMessenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    private void initView() {
        mSendDataToServiceEdit = (EditText) findViewById(R.id.send_data_to_service_edit);
        mSendDataToServiceEdit.setOnClickListener(this);
        mSendData = (Button) findViewById(R.id.send_data);
        mSendData.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            default:
                break;
            case R.id.send_data:
                String str = mSendDataToServiceEdit.getText().toString();
                if (str != null) {
                    Message message = Message.obtain();
                    Bundle bundle = new Bundle();
                    bundle.putString("serviceData",str);
                    message.setData(bundle);
                    try {
                        sMessenger.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
                break;
        }
    }
}

xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/send_data_to_service_edit"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_toLeftOf="@+id/send_data"
            android:hint="输入发送的信息 通过Messenger 发送"
            android:textSize="14sp" />

        <Button
            android:id="@+id/send_data"
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:layout_alignParentRight="true"
            android:text="SENd" />
    </RelativeLayout>

</LinearLayout>

 

客户端完毕。这里注意一点,在调试时记得将两端都启动起来,客户端可以向服务端通过输入框发送消息,服务端收到后会toast出来,如果发送的数据是1,那么服务端会给客户端发送一条消息内容为 2,客户端会toast出来,整个service 跨进程通信就这么多了。

 

 

service 跨进程通信源码

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值