• Android开发之---AIDL


      在Android开发中,有时会用到多进程通信,这时,可选的方案为:

      1. Bundle    :四大组件之间的进程间通信

      2. 文件共享   :适合无并发情景

      3. Messager : 低并发的一对多即使通信,无RPC需求或无须要返回结果的RPC需求

      4. AIDL        :一对多通信且有RPC需求 

      5. Content Provider : 一对多的进程间数据共享

      6. Socket     : 网络数据交换

      这里使用AIDL来举例,其实,Message底层也是AIDL来处理的,是对AIDL的一个简单封装,AIDL用到一个Android系统的核心--Binder,Binder在Android系统中用来处理进程间通信,它基于OpenBinder来实现的,是一个类似于COM和CORBA的分布式组件架构,其本身也挺复杂的,源码还没看懂,一开始看时感觉云里雾里的,但是,先用着吧,实践帮助理论,二者不断交错,肯定有一天弄懂的,哈哈~

      在一个APP应用里,通过process属性可以方便测试多进程通信,这里采用另一种方式,使用两个APP应用,一个是服务端,一个是客户端,其实,两种方式都一样道理,因为对于同一个应用的不同组件,如果它们运行在不同的进程中,它们与分别属于两个应用没有什么本质区别。

      开始测试:

      1. 新建服务端项目:TestAIDLServer:

      

      2. 新建一个用户管理类User.java,AIDL需要使用Paracelable来序列化和反序列化对象:

    package cn.linjk.testaidlserver;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    
    /**
     * Created by LinJK on 06/12/2016.
     */
    
    public class User implements Parcelable{
    
        public int     userId;
        public String  userName;
    
        public User(int userId, String userName) {
            this.userId = userId;
            this.userName = userName;
        }
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel pParcel, int pI) {
            pParcel.writeInt(userId);
            pParcel.writeString(userName);
        }
    
        public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {
            @Override
            public User createFromParcel(Parcel pParcel) {
                return new User(pParcel);
            }
    
            @Override
            public User[] newArray(int pI) {
                return new User[pI];
            }
        };
    
        private User(Parcel in) {
            userId = in.readInt();
            userName = in.readString();
        }
    }
    

       3. 新建AIDL文件夹并新建aidl接口文件:

        3.1 新建aidl文件夹:

          

          新建完成后目录结构如下:

          

        3.2 新建aidl接口文件,在接口文件用到了User类,也需要新建对应的类的aidl文件来声明类,并且在接口文件需要显示import这个User类

          

          这里声明了两个方式:新增用户和获取当前所有用户的方法。

        (小插曲:

          我们在这里make一次工程后,会生成一个java文件:

          

          使用aidl来处理,我们就不用自己写Binder通信,当然,也可以手写,这样有利于更加理解binder的原理,但是初学者就麻烦了,所有,两者都可以实现,AIDL文件的本质就是android系统为我们提供了一种快速实现binder的工具而已。

          )

      4. 为了方便AIDL开发,所有和AIDL相关的类和文件放入同一个包内,如果类的完整路径不一样,在反序列化时会失败,开发用户端时直接把这个包复制到客户端应用即可,这里把User类放进与aidl同级目录下。

      5. 服务新建一个Service,实现AIDL接口方法(这里为了方便,先在服务端准备两条数据):

    package cn.linjk.testaidlserver;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.Binder;
    import android.os.IBinder;
    import android.os.RemoteException;
    import android.support.annotation.Nullable;
    
    import java.util.List;
    import java.util.concurrent.CopyOnWriteArrayList;
    
    import cn.linjk.testaidlserver.aidl.IUserManager;
    import cn.linjk.testaidlserver.aidl.User;
    
    /**
     * Created by LinJK on 06/12/2016.
     */
    
    public class ServiceUserManager extends Service{
    
        private static final String TAG = ServiceUserManager.class.getSimpleName();
    
        private CopyOnWriteArrayList<User> mUserCopyOnWriteArrayList = new CopyOnWriteArrayList<>();
    
        private Binder mBinder = new IUserManager.Stub() {
            @Override
            public void addUser(User user) throws RemoteException {
                mUserCopyOnWriteArrayList.add(user);
            }
    
            @Override
            public List<User> getAllUsers() throws RemoteException {
                return mUserCopyOnWriteArrayList;
            }
        };
    
        @Override
        public void onCreate() {
            super.onCreate();
            mUserCopyOnWriteArrayList.add(new User(1, "Jim"));
            mUserCopyOnWriteArrayList.add(new User(2, "Jim_LinJK"));
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent pIntent) {
            return mBinder;
        }
    }
    

         6. 启动服务端,发现出现如下错误,找不到数据类?

        

        修改app/build.gradle文件配置:

        

        7. 可以了,服务端开启完成。

        8. 新建客户端项目:

        

        9. 把服务端的src/main/aidl文件下的文件拷贝到客户端同样目录路径下(如果同一个APP的话就不用拷贝了,共用的),完成后如下:

        

        10. 编写连接服务端代码:

        因为这里需要连接服务端的servicemanager,所以需要把ServiceUserManager类也移到aidl文件夹下面,并保证服务端项目和客户端项目文件和其内容一致,最后aidl文件夹下文件为:

        

    package cn.linjk.testaidlclient;
    
    import android.content.ComponentName;
    import android.content.Context;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.IBinder;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    
    import java.util.List;
    
    import cn.linjk.testaidlserver.aidl.IUserManager;
    import cn.linjk.testaidlserver.aidl.ServiceUserManager;
    import cn.linjk.testaidlserver.aidl.User;
    
    public class MainActivity extends AppCompatActivity {
        private final static String TAG = MainActivity.class.getSimpleName();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            bindService(new Intent(this, ServiceUserManager.class), mServiceConnection, Context.BIND_AUTO_CREATE);
        }
    
        @Override
        protected void onDestroy() {
            unbindService(mServiceConnection);
            super.onDestroy();
        }
    
        private ServiceConnection mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName pComponentName, IBinder pIBinder) {
                IUserManager lvIUserManager = IUserManager.Stub.asInterface(pIBinder);
    
                try {
                    List<User> lvUserList = lvIUserManager.getAllUsers();
                    for (User lvUser : lvUserList) {
                        Log.i(TAG, "[User name] : " + lvUser.userName + " and [User id] : " + lvUser.userId);
                    }
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public void onServiceDisconnected(ComponentName pComponentName) {
                //
            }
        };
    }
    

         11. 先启动服务端APP

         12. 再启动客户端APP,可以看到,客户端APP能获取服务端APP的数据:

        13. 在客户端APP增加一个按钮,用于向服务端新增一条用户数据,界面代码比较简单,就不写了,看看按钮的监听部分代码:

    btnAddUser.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View pView) {
                    try {
                        User lvUser = new User(3, "Apple");
                        lvIUserManager.addUser(lvUser);
                        //
                        List<User> lvUserList = lvIUserManager.getAllUsers();
                        for (User lvUserTmp : lvUserList) {
                            Log.i(TAG, "[User name] : " + lvUserTmp.userName + " and [User id] : " + lvUserTmp.userId);
                        }
                    }
                    catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
    

         14. 再次运行客户端APP,看看日志:

        一开始没有id为3的用户,点击按钮后,再次看看用户list,新增了一个用户成功:    

        到这里,aidl的测试已经可以了,当然,整个逻辑还有很多异常没考虑,这里就不全部完善了,例如不用每次从服务器查询数据,而是服务器数据更新了自动提醒客户端进行界面刷新功能等。

        代码已提交GitHub: https://github.com/linjk/TestAIDL.git

  • 相关阅读:
    2011级csdnjava张侃—Spring加载配置web
    基于thinkphp实现根据用户ip判断地理位置并提供对应天气信息的应用
    ip地址库 与浏览器的关系
    根据IP定位用户所在城市信息
    Linux利用OneinStack搭建环境
    Laravel根据Ip获取国家,城市信息
    艾伟:一次挂死(hang)的处理过程及经验 狼人:
    艾伟:正则表达式30分钟入门教程 狼人:
    艾伟:C# Design Patterns (1) Factory Method 狼人:
    艾伟:打通.NET 3.5与ExtJS数据交互的任督二脉 狼人:
  • 原文地址:https://www.cnblogs.com/linjk/p/6138405.html
Copyright © 2020-2023  润新知