android开发艺术探索(二补充)

IPC方式

1.使用Bundle

我们知道四大组件中的三大组件(Activity、Service、Receiver)都是支持在Intent中传递Bundle数据的,由于Bundle实现了Parcelable接口,所以他可以很方便的在不同进程中传输。

Bundle传递的数据类型有哪些?
(1)基本类型数据
(2)实现了序列化接口的对象(Parcellable以及Serializable接口)

如下代码:我们在Bundle存储数据,并通过intent发送出去。

Intent intent = new Intent();
        intent.setClass(this, ActivityA.class);
        Bundle bundle = new Bundle();
        bundle.putCharSequence("name", "花花");
        intent.putExtras(bundle);
        startActivity(intent);

获取Binder对象以及其中的数据。

        Bundle b=getIntent().getExtras();
        String name= b.getString("name");

使用文件共享
共享文件是通过两个进程通过读/写同一个文件来交换数据,比如A进程把数据写入文件,B进程通过读取这个文件来获取数据。

新建项目,首先我们再MainActivity中,再文件中存储一串数据。

Context 类中提供了一个 openFileOutput ()方法,可以用于将数据存储到指定的文件中。这个方法接收两个参数,第一个参数是文件名,在文件创建的时候使用的就是这个名称,注意这里指定的文件名不可以包含路径,因为所有的文件都是默认存储到/data/data/packagename/files/ 目 录 下 的 。

 private void writeInFile() {
        String data = "this is my book";
        FileOutputStream out = null;
        BufferedWriter writer = null;

        try {
            out = openFileOutput("data", Context.MODE_PRIVATE);
            writer = new BufferedWriter(new OutputStreamWriter(out));
            writer.write(data);

            handler.sendEmptyMessage(0);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

然后新建ActivityA,在此界面中去读取数据:在弹出的Toast中读取到了我们写入的字符串数据。

  private void readFile() {
        FileInputStream in=null;
        BufferedReader reader=null;
        StringBuilder content=new StringBuilder();

        try {
            in=openFileInput("data");
            reader=new BufferedReader(new InputStreamReader(in));
            String line="";
            while ((line=reader.readLine())!=null){
                content.append(line);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (reader!=null){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        Toast.makeText(this,content.toString(),Toast.LENGTH_LONG).show();
    }

SharedPreference是个特例,它是Android中提供的轻量级存储方案,他通过键值对的方式来存储数据,在底层实现上采用了XML文件来存储键值对。从本质上说SharedPreference也属于文件的一种,但是系统对它的读写有一定的缓存策略,记载内存中会有一份SharedPreference的文件缓存,因此在多进程情况下,系统对它的读/写就变得不可靠。

使用Messenger

Messenger可以翻译为信史,顾名思义,通过它可以再不同进程中传递Message对象,在Message中放入我们需要传递的数据,就可以轻松的实现数据的进程间传递。Messenger是一种轻量级的IPC方案,底层实现为AIDL。Messenger对AIDL做了封装,我们可以更简便的进行进程间通讯。由于它每次只处理一个请求,因此我们再服务端不用考虑线程同步的问题,这是因为在服务端中不存在并发执行的情况。

    Messenger的构造方法,一个传入Handler对象,另外一个传入Ibinder对象。
    public Messenger(Handler target) {
        throw new RuntimeException("Stub!");
    }

    public Messenger(IBinder target) {
        throw new RuntimeException("Stub!");
    }
    /**次方法用于返回Binder对象*/
     public IBinder getBinder() {
        throw new RuntimeException("Stub!");
    }
    /**次方法用于发送一条Message消息*/
    public void send(Message message) throws RemoteException {
        throw new RuntimeException("Stub!");
    }

Messenger的使用方法以及实现步骤
1、服务端进程

首先,我们需要在服务端创建按一个Service用来处理客户端的链接请求,同时创建一个Handler并通过它来创建爱你一个Messenger对象,然后再Service的OnBind中返回这个Messenger对象底层的Binder即可。

2.客户端进程

在客户端进程中,我们首先要绑定到服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,发送消息类型为Message对象。

step1:创建服务端项目,并新建MessengerService.java文件

public class MessengerService extends Service {
    private static final String TAG = "MessengerService";

    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0:
                    Log.e(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
    private final Messenger messenger=new Messenger(new MessengerHandler());

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

step2:在Mainfest文件中注册

 <!--让服务运行在单独的进程中-->
        <service android:name=".service.MessengerService"
            android:process=":romote"
            >
            <intent-filter>
                <action android:name="com.example.ipc.service.MessengerService"></action>
            </intent-filter>

        </service>

step3:创建客户端项目,Client

public class MainActivity extends AppCompatActivity {

    private static final String TAG="MainActivity";
    private Messenger messengerService;

    ServiceConnection conn=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            /**创建Messenger对象*/
            messengerService=new Messenger(iBinder);
            Message msg=Message.obtain(null,Sign.MSG_FROM_CLIENT);
            Bundle data=new Bundle();
            data.putString("msg","hello ,this is client.");
            msg.setData(data);
            try {
                /**通过Messenger对象发送Message消息*/
                messengerService.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
                messengerService=null;
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /**Step1:绑定服务端service*/
        Intent intent=new Intent("com.example.ipc.service.MessengerService");
        bindService(intent,conn, Context.BIND_AUTO_CREATE);
    }

    /**解绑Service*/
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}

然后运行项目,首先运行服务端,然后运行客户端。可以看到收到了客户端发送的消息。
输出结果

通过以上例子,我们可以看到Messenger传递数据,必须将Message消息放入Messenger中。Messenger和Message都实现了Parcelable接口,因此可以进行跨进程传输。然而这个只是客户端发消息给服务端,这时候我们需要服务端能给客户端发送消息。首先修改服务端代码:

public class MessengerService extends Service {
    private static final String TAG = "MessengerService";

    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case Sign.MSG_FROM_CLIENT:
                    Log.e(TAG, "receive msg from Client:" + msg.getData().getString("msg"));

                    /**获取到Messenger对象*/
                    Messenger client=msg.replyTo;
                    /**创建Message*/
                    Message reply=Message.obtain(null, Sign.MSG_FROM_SERVICE);
                    Bundle bundle=new Bundle();
                    bundle.putString("reply","I am receive msg");
                    reply.setData(bundle);
                    try {
                        /**通过Messenger向客户端发送消息*/
                        client.send(reply);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
    private final Messenger messenger=new Messenger(new MessengerHandler());

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

然后是客户端,既然要接收到服务端返回的消息,我们也定义一个接收消息的Messenger和Handler。如下:

public class MainActivity extends AppCompatActivity {

    private static final String TAG="MessengerService";
    private Messenger messengerService;


    private Messenger mGetReplyMessenger=new Messenger(new MessengerHandler());
    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case Sign.MSG_FROM_SERVICE:
                    Log.e(TAG,"receive msg from Service:"+msg.getData().getString("reply"));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }


    ServiceConnection conn=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            /**创建Messenger对象*/
            messengerService=new Messenger(iBinder);
            Message msg=Message.obtain(null,Sign.MSG_FROM_CLIENT);
            Bundle data=new Bundle();
            data.putString("msg","hello ,this is client.");
            msg.setData(data);
            //注意:
            msg.replyTo=mGetReplyMessenger;

            try {
                /**通过Messenger对象发送Message消息*/
                messengerService.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
                messengerService=null;
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /**Step1:绑定服务端service*/
        Intent intent=new Intent("com.example.ipc.service.MessengerService");
        bindService(intent,conn, Context.BIND_AUTO_CREATE);
    }

    /**解绑Service*/
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}

结果:我们可以看到,手下服务端接收到了客户端发送的消息,然后服务端作出响应返回给客户端。
这里写图片描述

AIDL方式
这里就不做具体的例子了,上一篇文章介绍过了。
1、aidl文件支持的数据类型?

(1)基本数据类型(int,long,char,boolean,double)
(2)String 和CharSequence
(3)List,只支持ArrayList,里面每个元素都能被AIDL支持。
(4)Map,只支持HashMap,里面的每个元素都必须被AIDL支持,包括key和value。
(5)Parcelable:所有实现了Parcelable接口的对象。
(6)所有的AIDL接口本身也可以在AIDL文件中使用。

2、服务端可以使用CopyOnWriteArrayList和ConcurrentHashMap来进行自动线程同步,客户端拿到的依然是ArrayList和HashMap;

3、服务端和客户端之间做监听器,服务端需要使用RemoteCallbackList,否则客户端的监听器无法收到通知(因为服务端实质还是一份新的序列化后的监听器实例,并不是客户端那份);

4、客户端通过IBinder.DeathRecipient来监听Binder死亡,也可以在onServiceDisconnected中监听并重连服务端。区别在于前者是在binder线程池中,访问UI需要用Handler,后者则是UI线程。

5、可通过自定义权限在onBind或者onTransact中进行权限验证。

ContentProvider
Android中专门用作不同应用间数据共享的方式,底层实现也是Binder。
Socket
Socket 一般用于网络通信,AIDL用这种方式会过于繁琐,不建议。它在网络通讯中被称为“套接字”,分为流式套接字和用户数据报套接字两种,分别对应网络传输控制层中的TCP和UDP协议。

TCP

面向链接的协议、双向通讯稳定,建立链接需要三次握手,数据传输功能稳定。

UDP

无连接,提供不稳定单项通讯,也可实现双向通讯,效率较高,但传输数据的正确率低。

使用事项

(1)声明权限:

<uses-permission android:name="android.permission.INTERNET"></uses-permission>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>

Binder连接池

Binder连接池,通过BinderPool的方式将Binder的控制与Service本身解耦,同时只需要维护一份Service即可。这里用到了CountDownLatch,大概解释下用意:线程在await后等待,直到CountDownLatch的计数为0,BinderPool里使用它的目的是为了保证Activity获取BinderPool的时候Service已确定bind完成。

选择合适的IPC方式

各种IPC方式对比

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值