`
haizibie453
  • 浏览: 11957 次
  • 性别: Icon_minigender_1
  • 来自: 南京
最近访客 更多访客>>
社区版块
存档分类
最新评论

项目经验---Android平台下进程通信(IPC)需注意的问题

阅读更多
最近忙于一个项目,涉及到Android平台下的进程间通信(IPC)问题,由于项目的架构问题,遇到一系列数据的传送,同步问题,让人蛋疼`~~~~~~~~~~,浅谈一下心得····

整个系统的架构如下:




所要做的项目即图中的中间件,它是手机上的第三方应用和服务器间的桥梁,为第三方应用提供相关服务接口。

整个流程即:第三方应用调用接口中的方法,通知中间件做某些操作,然后中间件把数据发送到服务器或从服务器接受到数据返回给应用(比如,应用需要注册到服务器,首先调用接口中的方法通知中间件,中间件做一些逻辑操作(如:判断是否已注册),中间件最后把应用注册到服务器,并把注册结果返回给应用)

接口的设计:做成一个jar包,放到第三方应用中,供其调用其中的方法。




由于第三方应用和中间件都是安装在手机上的应用程序,在android平台下为两个独立的进程(数据都是私有的),他们之间进行数据的交互就要用到android平台下进程间的通信(IPC)。

在Windows Mobile平台下使用共享内存实现进程间数据的共享很方便,在Android平台下却很麻烦(也没尝试去做)

3种方法实现Android平台下的进程间通信(即上面所说的项目中接口与中间件间的数据交互(接口放在第三方应用中的))

1.说到android平台下的进程间通信,首先会想到AIDL(Android Interface Definition Language)

AIDL是基于Service组件的,实现起来也很简单,大概分为以下几步:

a.在中间件中创建一个aidl文件,把接口中的方法写到xxx.aidl文件中,此时ADT插件会自动生成一个与aidl文件名相同的类,并这个类中有一个内部类Stub

b.在中间件中创建一个Remote Service(即一个service组件),在其onBind方法中返回一个Stub对象,而这个Stub对象中实现了aidl文件中的方法

c.把接口发布给应用,在第三方应用中也创建这样一个相同的aidl文件,在客户端通过调用bindService方法,在调用的时候传入一个ServiceConnect对象,当bind到Remote Service的时候,系统会回调ServiceConnect中的onServiceConnected方法,在此方法中获得RemoteService返回的Stub对象(IBinder对象),把此对象转换成aidl文件所对应的类,即可调用中间件中的方法,AIDL实现的步骤和流程基本上是这样,但还要注意许多细节,请看Android SDK官方文档,写的很详细。Dev Guid-->Developing-->Tools-->aidl

但是,此时有个同步的问题,即第三方应用调用接口中的注册方法appRegist(int appId)(假如是在onClick方法中调用的),接口中的操作就是bindService,然后系统会回调onServiceConnected方法,在此方法被回调完成后才能获得中间件返回的操作结果(注册是否成功),但是在UI线程中调用appRegist(int appId)方法和系统回调onServiceConnected方法不是同步执行的,即onServiceConnected方法还未回调完成,appRegist方法已经返回,所以应用获得的数据时错误的,但是由于是UI线程又不能阻塞去等待onServiceConnected方法的完成,所以这是个很麻烦的问题。。。




2.第二种方法使用Messenge+Message,即接口和中间件各自维护一个消息队列,并互发消息来实现的,也是基于Service和bindService方法,具体实现看Android SDK官方文档,写的很详细,如下:




Remote Messenger Service SampleIf you need to be able to write a Service that can perform complicated communication with clients in remote processes (beyond simply the use of Context.startServiceto send commands to it), then you can use the Messenger class instead of writing full AIDL files.An example of a Service that uses Messenger as its client interface is shown here. First is the Service itself, publishing a Messenger to an internal Handler when bound:public class MessengerService extends Service {    /** For showing and hiding our notification. */    NotificationManager mNM;    /** Keeps track of all current registered clients. */    ArrayList<Messenger> mClients = new ArrayList<Messenger>();    /** Holds last value set by a client. */    int mValue = 0;    /**     * Command to the service to register a client, receiving callbacks     * from the service.  The Message's replyTo field must be a Messenger of     * the client where callbacks should be sent.     */    static final int MSG_REGISTER_CLIENT = 1;    /**     * Command to the service to unregister a client, ot stop receiving callbacks     * from the service.  The Message's replyTo field must be a Messenger of     * the client as previously given with MSG_REGISTER_CLIENT.     */    static final int MSG_UNREGISTER_CLIENT = 2;    /**     * Command to service to set a new value.  This can be sent to the     * service to supply a new value, and will be sent by the service to     * any registered clients with the new value.     */    static final int MSG_SET_VALUE = 3;    /**     * Handler of incoming messages from clients.     */    class IncomingHandler extends Handler {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case MSG_REGISTER_CLIENT:                    mClients.add(msg.replyTo);                    break;                case MSG_UNREGISTER_CLIENT:                    mClients.remove(msg.replyTo);                    break;                case MSG_SET_VALUE:                    mValue = msg.arg1;                    for (int i=mClients.size()-1; i>=0; i--) {                        try {                            mClients.get(i).send(Message.obtain(null,                                    MSG_SET_VALUE, mValue, 0));                        } catch (RemoteException e) {                            // The client is dead.  Remove it from the list;                            // we are going through the list from back to front                            // so this is safe to do inside the loop.                            mClients.remove(i);                        }                    }                    break;                default:                    super.handleMessage(msg);            }        }    }    /**     * Target we publish for clients to send messages to IncomingHandler.     */    final Messenger mMessenger = new Messenger(new IncomingHandler());    @Override    public void onCreate() {        mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);        // Display a notification about us starting.        showNotification();    }    @Override    public void onDestroy() {        // Cancel the persistent notification.        mNM.cancel(R.string.remote_service_started);        // Tell the user we stopped.        Toast.makeText(this, R.string.remote_service_stopped, Toast.LENGTH_SHORT).show();    }    /**     * When binding to the service, we return an interface to our messenger     * for sending messages to the service.     */    @Override    public IBinder onBind(Intent intent) {        return mMessenger.getBinder();    }    /**     * Show a notification while this service is running.     */    private void showNotification() {        // In this sample, we'll use the same text for the ticker and the expanded notification        CharSequence text = getText(R.string.remote_service_started);        // Set the icon, scrolling text and timestamp        Notification notification = new Notification(R.drawable.stat_sample, text,                System.currentTimeMillis());        // The PendingIntent to launch our activity if the user selects this notification        PendingIntent contentIntent = PendingIntent.getActivity(this, 0,                new Intent(this, Controller.class), 0);        // Set the info for the views that show in the notification panel.        notification.setLatestEventInfo(this, getText(R.string.remote_service_label),                       text, contentIntent);        // Send the notification.        // We use a string id because it is a unique number.  We use it later to cancel.        mNM.notify(R.string.remote_service_started, notification);    }}If we want to make this service run in a remote process (instead of the standard one for its .apk), we can use android:process in its manifest tag to specify one:<service android:name=".app.MessengerService"        android:process=":remote" />Note that the name "remote" chosen here is arbitrary, and you can use other names if you want additional processes. The ':' prefix appends the name to your package's standard process name.With that done, clients can now bind to the service and send messages to it. Note that this allows clients to register with it to receive messages back as well:/** Messenger for communicating with service. */Messenger mService = null;/** Flag indicating whether we have called bind on the service. */boolean mIsBound;/** Some text view we are using to show state information. */TextView mCallbackText;/** * Handler of incoming messages from service. */class IncomingHandler extends Handler {    @Override    public void handleMessage(Message msg) {        switch (msg.what) {            case MessengerService.MSG_SET_VALUE:                mCallbackText.setText("Received from service: " + msg.arg1);                break;            default:                super.handleMessage(msg);        }    }}/** * Target we publish for clients to send messages to IncomingHandler. */final Messenger mMessenger = new Messenger(new IncomingHandler());/** * Class for interacting with the main interface of the service. */private ServiceConnection mConnection = new ServiceConnection() {    public void onServiceConnected(ComponentName className,            IBinder service) {        // This is called when the connection with the service has been        // established, giving us the service object we can use to        // interact with the service.  We are communicating with our        // service through an IDL interface, so get a client-side        // representation of that from the raw service object.        mService = new Messenger(service);        mCallbackText.setText("Attached.");        // We want to monitor the service for as long as we are        // connected to it.        try {            Message msg = Message.obtain(null,                    MessengerService.MSG_REGISTER_CLIENT);            msg.replyTo = mMessenger;            mService.send(msg);            // Give it some value as an example.            msg = Message.obtain(null,                    MessengerService.MSG_SET_VALUE, this.hashCode(), 0);            mService.send(msg);        } catch (RemoteException e) {            // In this case the service has crashed before we could even            // do anything with it; we can count on soon being            // disconnected (and then reconnected if it can be restarted)            // so there is no need to do anything here.        }        // As part of the sample, tell the user what happened.        Toast.makeText(Binding.this, R.string.remote_service_connected,                Toast.LENGTH_SHORT).show();    }    public void onServiceDisconnected(ComponentName className) {        // This is called when the connection with the service has been        // unexpectedly disconnected -- that is, its process crashed.        mService = null;        mCallbackText.setText("Disconnected.");        // As part of the sample, tell the user what happened.        Toast.makeText(Binding.this, R.string.remote_service_disconnected,                Toast.LENGTH_SHORT).show();    }};void doBindService() {    // Establish a connection with the service.  We use an explicit    // class name because there is no reason to be able to let other    // applications replace our component.    bindService(new Intent(Binding.this,             MessengerService.class), mConnection, Context.BIND_AUTO_CREATE);    mIsBound = true;    mCallbackText.setText("Binding.");}void doUnbindService() {    if (mIsBound) {        // If we have received the service, and hence registered with        // it, then now is the time to unregister.        if (mService != null) {            try {                Message msg = Message.obtain(null,                        MessengerService.MSG_UNREGISTER_CLIENT);                msg.replyTo = mMessenger;                mService.send(msg);            } catch (RemoteException e) {                // There is nothing special we need to do if the service                // has crashed.            }        }        // Detach our existing connection.        unbindService(mConnection);        mIsBound = false;        mCallbackText.setText("Unbinding.");    }}这种实现方式同样存在一个同步的问题,和第一种实现方法中的问题是一样的3.第三种实现方式有点笨重,通过ContentProvider+sqlite数据库实现我们知道在Android平台下可以使用ContentProvider暴露一个应用程序中的数据给其他应用,其他应用通过ContentResolver和URI可以操作该数据。在上面项目中,先在中间件中创建一个数据库,接口可以通过ContentResolver+URI向该数据库中写入数据,然后通过startService或发送广播通知中间件,去做相应的操作,中间件收到命令后再从数据库中取出数据做相应操作。但问题是接口中的方法不会等待中间件操作完成就首先返回了。所以中间件做完操作后还要通知接口(可以使用广播或Message实现),在接口中还要注册一个广播接收器或消息接收器,问题又来了,接口还要返回数据给应用,此时应用的方法调用早已返回。。。总结以上几种实现方式,除非在应用中作一个消息接收器或广播接收器,让接口把最终的操作结果放到消息或广播中发送给应用,但这样就增加了接口跟第三方应用的耦合度,也许是架构师当初没考虑到Android平台下进程间的通信吧,在应用和中间件间增加了一个接口,由于操作的异步性,最终还是要把操作结果通知到应用,而应用无法主动或不知道什么时候去从接口获得数据,通过多线程和阻塞线程也许会有更好的解决办法,研究中。。。
  • 大小: 19.8 KB
分享到:
评论
1 楼 bluky999 2011-08-17  
请问你这个架构图没有现实出来,还会补充上来么?  我想了解下你们的模仿kik应用开发结果如何??

相关推荐

    AndroidIPC和binder框架

    总结来说,《Android IPC与Binder框架》是一本全面解析Android进程间通信机制和Binder核心技术的书籍,适合有一定Android开发经验的工程师深入学习和研究。通过阅读这本书,开发者不仅能了解Android IPC的各种实现...

    Android AIDL进程间通信

    6. **线程管理**:由于跨进程通信涉及到多个线程,因此开发者需要注意线程安全问题,通常需要在服务端方法中添加同步锁或使用Handler、AsyncTask等进行异步处理。 **使用AIDL步骤** 1. 创建AIDL文件:在项目的`src/...

    50个优秀的Android项目源码

    这些源码覆盖了Android开发的多个方面,包括UI设计、网络编程、多媒体处理、数据存储、进程通信、语音交互等。通过深入研究这些项目,开发者不仅可以提升编程技巧,还能了解到最新的开发趋势和技术。无论是初学者...

    一个Android通用的IPC(进程通信)框架。该项目主要是模仿饿了么开源项目Hermes的设计进行的自我理解改写。.zip

    项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松copy复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全栈开发),有任何使用问题欢迎随时与我联系,我会及时为您解惑,...

    android在wifi下手机与电脑的socket通信.zip

    标题“android在wifi下手机与电脑的socket通信.zip”揭示了这个压缩包文件的主要内容,即关于Android设备通过Wi-Fi网络使用Socket与电脑进行通信的示例或教程。Socket编程是计算机网络通信的基础,它允许两个设备...

    初中级Android开发社招面试之数据存储、IPC及View.zip

    在Android应用开发中,数据存储、进程间通信(IPC)以及View系统是三大核心知识点,对于初中级开发者来说,这些都是面试中的常见问题。下面将详细阐述这三个方面的内容。 首先,我们来探讨数据存储。Android提供了...

    50个Android项目源码.rar

    在给定的“50个Android项目源码.rar”压缩包中,包含了多个与Android应用开发相关的实践项目,这些项目涵盖了各种功能和组件的...同时,这些源码也是实际项目经验的积累,对于提升开发者解决实际问题的能力大有裨益。

    Android高级编程--源代码

    而富有经验的移动开发人员现在也可以方便地扩展到Android平台上来,利用它独特的功能来改进现有产品或者开发其他新奇产品。  本书将指导你使用Androidl.0软件开发包来构建移动应用程序。在每章的讲解中,它会让你...

    联想Android开发工程师面试题.zip

    - 熟悉Android的多进程策略和进程间的通信(IPC)。 8. **测试与调试** - 理解单元测试、集成测试和UI测试,掌握JUnit和Espresso测试框架。 - 使用Android Studio的调试工具进行问题定位。 9. **热修复与插件化...

    Android+Java面试题

    - AIDL:理解进程间通信(IPC)原理,使用AIDL实现跨进程通信。 9. **Android组件** - Fragment:在Activity中使用Fragment,理解Fragment的生命周期。 - ContentProvider:作为数据共享的桥梁,提供统一的数据...

    安卓通信接口大全

    5. **Binder**: Binder是Android特有的进程间通信(IPC,Inter-Process Communication)机制。它是Android系统服务和应用程序组件之间通信的基础,允许跨进程调用方法。 6. **AIDL(Android Interface Definition ...

    安卓疯狂讲义里安卓基于socket的通信

    Socket,又称为套接字,是网络通信中的一个端点,它提供了进程间通信(IPC)或网络间进程通信(IPC)的接口。在安卓系统中,Socket通常用于实现服务器与客户端之间的通信,例如,移动应用可能需要与远程服务器交换...

    android 完全详解

    四、Android进程间通信(IPC) 第六章“Android进程间通信”是Android系统的一大特色,它允许不同进程之间共享数据和协调操作。Android提供了多种IPC机制,如Binder、Intent、Content Provider、Broadcast Receiver...

    联想Android 面试题.pdf

    - **AIDL (Android Interface Definition Language)**: 用于定义跨进程通信接口的语言。 - **Socket**: 基于网络协议的进程间通信方式。 - **Shared Memory** (共享内存): 通过共享内存区域来交换数据。 #### 7. ...

    MultiProcessSample

    "MultiProcessSample"是一个专为Android开发者设计的示例项目,它详细展示了如何在Android应用程序中实现多进程通信。在第二章的"IPC(上)"中,我们将深入理解这一概念,学习如何在不同的进程中有效地进行数据交换...

    高焕堂 android好书

    《AndroidBook_004_IPC_Binder_V2.0.rar》可能围绕“Android进程间通信(IPC)和Binder机制”展开。Binder是Android系统内核级的组件,用于不同进程之间的数据交换和通信。书中可能详细解释了如何使用Binder进行服务...

    Android365MobileSecretary v1.0.6(365手机助手AIDL)-IT计算机-毕业设计.zip

    AIDL是Android系统中用于实现进程间通信(IPC,Inter-Process Communication)的关键技术。在Android365MobileSecretary中,AIDL可能被用来允许不同的应用程序组件之间交换数据或触发操作。通过AIDL,开发者可以定义...

    整理的两个应用之间的通信

    然而,当需要进行更复杂的数据交换或跨进程通信时,Socket通信就显得尤为重要。Socket是网络编程的基础,它允许两个应用程序通过网络连接进行数据传输。本资料包主要探讨的是Android应用之间如何利用Socket实现通信...

Global site tag (gtag.js) - Google Analytics