Android知识点总结(四)进程间通信

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chaoyangsun/article/details/88950409

进程间通讯方式

Android进程通讯方式有很多,如Socket、ContentProvider、共享文件(这种方式的缺点是不支持并发写,同时需要手动操作IO)、AIDL、Messenger(底层实现也是AIDL)等。

Messenger实现

Messenger是一种轻量级的IPC方案,可以在不同进程中传递Message对象。不适用与并发服务场景。下面是其常用方法源码

    /**
     * Create a new Messenger pointing to the given Handler.  Any Message
     * objects sent through this Messenger will appear in the Handler as if
     * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
     * been called directly.
     */
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
    
     /**
     * Create a Messenger from a raw IBinder, which had previously been
     * retrieved with {@link #getBinder}.
     */
    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    } 
    
    /**
     * Send a Message to this Messenger's Handler.
     */
    public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }
    
    /**
     * Retrieve the IBinder that this Messenger is using to communicate with
     * its associated Handler.
     */
    public IBinder getBinder() {
        return mTarget.asBinder();
    }

它的实现要比直接使用AIDL简单的多:

1. 服务端进程
首先,在服务端创建一个Service,同时创建一个Handler并通过它来创建一个Messenger对象,然后在Service的onBind中返回Messenger底层的Binder即可。
2. 客户端进程
客户端进程,首先绑定服务端的Service,然后利用服务端返回的IBinder的代理对象创建一个Messenger,通过这个Messenger对象就可以向服务端发送消息了。消息类型为Message对象,跨进程发送要使用Bundle传送,否则可能会崩溃:Can't marshal non-Parcelable objects across processes.
服务端代码如下:

public class MyService extends Service {
    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (msg != null) {
                switch (msg.what) {
                    case MSG:
                        Log.d("from customer: ", msg.getData().getString("msg"));
                        break;
                    default:
                        break;
                }
            }
            return false;
        }
    });
    private Messenger mMessenger = new Messenger(handler);

    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}

客户端代码如下

public class MainActivity extends AppCompatActivity {
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Messenger  mMessenger = new Messenger(service);
            Message obtain = Message.obtain(null, MyUtils.MSG);
            Bundle bundle = new Bundle();
            bundle.putString("msg", "hello, i am customer");
            obtain.setData(bundle);
            try {
                mMessenger.send(obtain);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.tv1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               Intent intent = new Intent(this, MyService.class);
        	   bindService(intent, serviceConnection,Context.BIND_AUTO_CREATE);
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(serviceConnection);
    }
}

另外,注意给远程Service设置进程:

<service
       android:name=".service.MyService"
       android:enabled="true"
       android:exported="true"
       android:process=":scy"
       />

最后查看服务端日志的时候,注意在AS的Logcat上切换到服务进程查看:

04-01 16:08:08.244 16677-16677/com.scy.component.arithmetictest:scy D/
from customer:: hello, i am customer

到这里实现了单向的Client -> Service的跨进程通信,服务端要想实现回复,只需稍作修改就行:
首先,在客户端要再创建一个Messenger和Handler对象,用于接收消息,在发送消息前将这个Messenger对象赋值给Message的replyTo属性即可。
在这里插入图片描述
然后,在服务端的Handler里取出消息后,紧接着用Message的replyTo属性获取到客户端的Messenger对象并用其发送消息即可。
在这里插入图片描述
如果要查看日志,记得切换进程。
在这里插入图片描述

Message有多种创建方式,建议使用Message.obtain系列进行创建,相对于直接new Message()的创建方式可以有效避免对象的重复创建
Message的obj字段在Android2.2版本以后支持实现Parcelable接口的对象进行跨进程传递,但是不支持非系统内置即自定义的Parcelable对象。所以跨进程通信尽量使用Bundle方式传递
Messenger是以串行方式处理消息的,即便大量的消息同时发送到服务端,服务端仍然只能一个个处理,所以Messenger不适用于并发请求,对于高并发场景可以使用AIDL来实现跨进程通信
Messenger的作用主要是为了传递消息,如果需要跨进程调用服务端的方法,Messenger就无法做到了,AIDL可以做到

AIDL实现

事实上AIDL才是应用最广泛的IPC方式,使用AIDL可以在客户端调用服务端进程的方法,适用高并发场景。AIDL通信流程分为服务端和客户端两方面:
1. 服务端
服务端首先创建一个Service来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Service中实现这个AIDL接口即可。
2. 客户端
客户端首先需要绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了。

使用步骤如下:
1. 创建AIDL接口
创建一个后缀为aidl的文件,在里面声明所需接口。

// IBookManager.aidl
package com.scy.component.arithmetictest.aidl;
import com.scy.component.arithmetictest.aidl.Book;
// Declare any non-default types here with import statements

interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
}

对于自定义Parcelable对象Book,需要显示的import进来,同时对于自定义的Parcelable对象,需要创建一个同名的aidl文件,对于Book类就需要创建一个Book.aidl文件,声明它为Parcelable对象。

package com.scy.component.arithmetictest.aidl;
parcelable Book;

对应的Book类实体如下:

package com.scy.component.arithmetictest.aidl;

import android.os.Parcel;
import android.os.Parcelable;

public class Book implements Parcelable {
    private double price;
    private String name;

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Book{" +
                "price=" + price +
                ", name='" + name + '\'' +
                '}';
    }

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

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

    public Book() {
    }

    public Book(double price, String name) {
        this.price = price;
        this.name = name;
    }

    protected Book(Parcel in) {
        this.price = in.readDouble();
        this.name = in.readString();
    }

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

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

在这里插入图片描述

注意包名一样,aidl文件里导入正确的package
否侧可能报Process ‘command ‘C:\SDK\build-tools\28.0.3\aidl.exe’’ finished with non-zero exit value 1

2. 远程服务端Service的实现

public class BookManagerService extends Service {
    //支持并发读写
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
    //创建Binder对象,在onBind方法中返回
    private Binder binder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
        	//模拟耗时操作
            SystemClock.sleep(5000);
            mBookList.add(book);
        }
    };
    public BookManagerService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(19, "艺术探索"));
        mBookList.add(new Book(20, "Java虚拟机"));
    }
}

为BookManagerService设置进程

        <service
            android:name=".service.BookManagerService"
            android:enabled="true"
            android:exported="true"
            android:process=":remote" />

2.客户端的实现


public class SecondActivity extends AppCompatActivity {
    private IBookManager iBookManager;
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iBookManager = IBookManager.Stub.asInterface(service);
            try {
                List<Book> bookList = iBookManager.getBookList();
                Log.w("aidl", bookList.toString());

            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
        findViewById(R.id.add).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            iBookManager.addBook(new Book(18, "编译原理"));
                            Log.w("aidl", iBookManager.getBookList().size()+"本");
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();

            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(connection);
    }
}

服务端的方法可能需要很久才能执行,所以调用的时候最好开启线程。

展开阅读全文

没有更多推荐了,返回首页