Android跨进程通信

  • Bundle
  • 文件共享
  • ContentProvider
  • Socket
  • Messenger
  • AIDL

Bundle

先看下拨打电话的代码

Uri uri = Uri.parse("smsto:10086");
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
Bundle bundle = new Bundle();
bundle.putString("sms_body", "SMS Text");
intent.putExtras(bundle);
//  intent.putExtra("sms_body", "SMS Text");
startActivity(intent);
复制代码

可见,通过将数据存储在Intent的Bundle中,可以在不同进程/APP间传递数据。

文件共享

通过将数据写入到一个文件中,不同进程可以对这个文件进行读取访问,来达到跨进程通信目的。 不过,多进程同时访问一个文件,存在并发和IO性能低的问题。

ContentProvider

Android四大组件之一,提供访问数据的统一格式。数据来源可以是文件、数据库。 可以对外提供访问的接口,实现跨进程/APP访问。

private void readContacts() {
    //用于查询电话号码的URI
    Uri phoneUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
    // 查询的字段
    String[] projection = {
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,//通讯录姓名
            ContactsContract.CommonDataKinds.Phone.DATA1, "sort_key",//通讯录手机号
            };
    Cursor cursor = getContentResolver().query(phoneUri, projection, null, null, null);
    while ((cursor.moveToNext())) {
        String name = cursor.getString(0);
        String phone = cursor.getString(1);
    }
}
复制代码

Socket

Socket主要分为两种

  • StreamSocket:基于TCP协议的封装,以流的方式提供数据交互服务,提供了稳定的双向通信,通过“三次握手”建立连接,传输数据具有较高的稳定性。 Java中客户端使用Socket类,服务器端使用ServerSocket类。
  • DatagramSocket:基于UDP协议的封装,以数据报文的方式提供数据交互服务,提供了不稳定的单向通信,具有更好的执行效率,由于基于无连接的方式,传输数据不稳定,不保证数据的完整性。 Java中使用DatagramPacket类,表示数据报包;DatagramSocket类,进行端到端通信。

Messager

底层也是通过封装AIDL来实现的,所以使用的方式和AIDL基本类似。

  1. 在服务端进程Service中创建Messenger对象,用来接收客户端发来的Message数据,和获取客户端Messenger对象,并给客户端发Message数据。
  2. 创建客户端Messenger对象,用来接收服务端数据。
  3. 客户端绑定服务端服务,并获取服务端Messenger对象,用来给服务端发Message数据。
  4. 通过服务端Messenger发消息,将客户端Messenger对象,添加到Message.replyTo。
public class MsgerService extends Service {
    private Messenger mServerMessenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            // 接收客户端发过来的消息
            switch (msg.what) {
                case 1000:

                    Toast.makeText(getBaseContext(), "" + msg.arg1, Toast.LENGTH_SHORT).show();

                    Message cMsg = Message.obtain();
                    cMsg.what = msg.what;
                    Bundle bundle = new Bundle();
                    bundle.putString("name", "Jim");
                    cMsg.obj = bundle;

                    // 获取客户端的Messenger对象,需要客户端在发送消息时设置
                    Messenger cMsger = msg.replyTo;
                    try {
                        // 给客户端发送消息
                        cMsger.send(cMsg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }
    });
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mServerMessenger.getBinder();
    }
}
复制代码
public class ClientActivity extends Activity {
    private TextView mNameTxt;

    /**
     * 客户端Messenger对象,用来接收服务端数据
     */
    private Messenger mClientMessenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 1000:
                    // 接收服务端数据
                    Bundle bundle = (Bundle) msg.obj;
                    mNameTxt.setText(bundle.getString("name"));
                    break;
            }
        }
    });

    /**
     * 服务端Messenger对象,建立连接时获取,用来给服务端发消息
     */
    private Messenger mServerMessenger;
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 获取服务端Messenger对象
            mServerMessenger = new Messenger(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mServerMessenger = null;
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.fragment_common);

        mNameTxt = (TextView) findViewById(R.id.name);

        // 绑定远端服务
        Intent intent = new Intent(this, MsgerService.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);

        findViewById(R.id.bind).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                int number = (int) (Math.random() * 100);
                Message msg = Message.obtain();
                msg.what = 1000;
                msg.arg1 = number;
                msg.replyTo = mClientMessenger;

                // 给服务端发送消息
                if (mServerMessenger != null) {
                    try {
                        mServerMessenger.send(msg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }
}
复制代码

AIDL

AIDL(Android Interface Definition Language)指的就是接口定义语言,通过它可以让客户端与服务端在进程间使用共同认可的编程接口来进行通信 AIDL使用的步骤相对较多,主要总结为三个基本步骤:

  • 创建AIDL接口

  • 根据AIDL创建远程Service服务

  • 绑定远程Service服务

(1)创建AIDL接口

定义aidl接口文件

在Android Studio中已经集成好了这个文件的创建方式,直接右击工程,点击New -> AIDL -> AIDL File,然后输入接口的名称就好,将会在src/main目录下创建一个与java目录平级,且里面的包名与java目录里的包名一致,后缀为.aidl的文件

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

interface IMyAidlTest {
    /**
     * 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);
}
复制代码

上面这个文件是Android Studio自动创建的模版文件,里面的basicTypes方法不需要使用到可以删掉。 AIDL对数据类型的支持包括Java中的所有基本数据类型,还有String、CharSequence、List、Map。

自定义AIDL的数据类型

在AIDL提供的默认数据类型无法满足需求的情况下,就需要自定义数据类型了 比如我们有个Product类,需要用来传递数据,那么这个类必须要实现Parcelable接口,并在AIDL中新建一个相同类名的aidl文件进行声明,并且这个aidl文件所在的路径必须要和java文件里的实体类路径保持一致,如以下文件Product.aidl

package demo.csdn.zhuwentao.bean;

parcelable Product;
复制代码

然后在IMyAidlTest.aidl中使用import导入进来,除了AIDL默认支持的数据类型外,其它自定义的类型都需要通过此方法导入进来,包名路径需要精确到类名。

interface IMyAidlTest {
    void addProduct(in Product person);
    List<Product> getProductList();
}
复制代码

这里的方法只作为接口声明的作用,以上定义的接口最终会在Service服务里实现具体的操作逻辑。

根据aidl文件生成java接口文件

这个步骤Android Studio已经帮我们集成好了,只需要点击 Build -> Make Project,或者点击AS上的那个小锤子图标就可以,构建完后将会自动根据我们定义的IMyAidlTest.aidl文件生成IMyAidlTest.java接口类,可以在build/generated/source/aidl/debug/路径下找到这个类。

(2)根据AIDL创建远程Service服务

上一步中创建好的IMyAidlTest.java接口文件,需要使用Service来进行绑定,这里就需要我们新建一个Service服务。

/**
 * 根据AIDL创建远程Service服务端
 */
public class MyAidlService extends Service {

    private List<Product> mProducts;
    public MyAidlService() {
    }

    private IBinder mIBinder = new IMyAidlTest.Stub() {

        @Override
        public void addProduct(Product product) throws RemoteException {
            mProducts.add(product);
        }

        @Override
        public List<Product> getProductList() throws RemoteException {
            return mProducts;
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        mProducts = new ArrayList<>();
        return mIBinder;
    }
}
复制代码

mIBinder对象实例化了IMyAidlTest.Stub,并在回调接口中实现了最终的处理逻辑 当与客户端绑定时,会触发onBind()方法,并返回一个Binder对象给客户端使用,客户端就可以通过这个类调用服务里实现好的接口方法 记得要在配置文件中加入声明,并使用android:process属性指定其运行在新的进程中。

<service
    android:name=".MyAidlService"
    android:process=":process"/>
复制代码

配置好以上步骤后,跨进程通信的服务端就配置好了

(3)绑定远程Service服务

跨进程通信服务端实现好了后,就可以在客户端中开始调用它了,首先在Activity中先创建好服务连接对象

private IMyAidlTest mAidlTest;

private ServiceConnection mServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mAidlTest = IMyAidlTest.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        mAidlTest = null;
    }
};
复制代码

再通过Intent的bindService来绑定Service服务,建立起连接

Intent intent = new Intent(getApplicationContext(), MyAidlService.class);
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
复制代码

启动成功后,onServiceConnected方法将会在建立连接时被回调,回调时将生成一个接口实现mAidlTest对象,这个对象就是我们进行跨进程操作调用对象 接下来就是通过这个mAidlTest对象来操作AIDL方法就好了。

private void addProduct(String name, int price) {
    Product pro = new Product();
    pro.mName = name;
    pro.mPrice = price;

    try {
        // 通过mAidlTest调用AIDL方法
        mAidlTest.addProduct(pro);
        List<Product> proLists = mAidlTest.getProductList();

        mAIDLTv.setText(proLists.toString());
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}
复制代码

以上就是AIDL使用的基本步骤了。

参考:www.jianshu.com/p/b17f1276e…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
千里马8年Android系统及应用开发经验,曾担任过美国unokiwi公司移动端技术总监兼架构师,对系统开发,性能优化,应用高级开发有深入的研究,Android开源定制ROM Lineage的贡献者之一,国内首家线下开辟培训Android Framework课程,拥有2年的Android系统培训经验。成为腾讯课堂专业负责android framework课程分享第一人,致力于提高国内android Framework水平Android Framework领域内是国内各大手机终端科技公司需要的人才,应用开发者都对Android系统充满着好奇,其中的binder是重中之重,都说无binder无Android,binde是Android系统的任督二脉。课程水平循序渐进,由中级再到高级,满足各个层次水平的android开发者。1、灵活使用binder进程通信,在app端对它的任何api方法等使用自如2、可以单独分析android系统源码中任何binder部分,分析再也没有难度3、掌握binder驱动本质原理,及对应binder驱动怎么进行进程通信,及内存等拷贝方式数据等4、对binder从上层的java app端一直到最底层的内核binder驱动,都可以顺利理通5、针对系统开发过程中遇到的binder报错等分析方法,及binder bug案例学习6、针对面试官任何的binder问题都可以对答自如7、socket这种进程通信实战使用8、针对android源码中使用的socket源码轻松掌握9、android系统源码中最常见的socketpair中双向进程通信10、使用socket实现一个可以让app执行shell命令的程序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值