IPC

Android IPC机制详解(二)

2017-01-11  本文已影响241人  星星_点灯

本篇主要介绍内容
1、Binder连接池

先来回顾一下AIDL的大致流程
1、首先创建一个Service和AIDL接口;
2、创建一个类继承AIDL接口中的Stub类,并实现其中抽象方法;
3、在Service的onBind方法中返回这个对象;
4、客户端绑定服务端获取到这个对象就可以访问服务器的方法了;

以上就是典型的AIDL的执行流程,现在有这样一个问题:一个项目越来越庞大,现在有10个不同的业务模块都需要使用AIDL来实现,那么要怎么办呢?按照AIDL的方式一个个来实现?如果按照这种方式,那么我们就要创建10个Service。如果是100个呢?到这里应该明白,随着我们的业务不断增加,AIDL也要不断增加,这就导致了我们要无限制的增加Service;Service本身是一种资源,如果一个应用有十几个Service运行,极大的消耗内存资源,影响应用的性能;那么应该怎么办呢?有没有一种方式创建一个Service就能解决这个问题呢?答案是肯定的,就是我们今天要介绍的主角:Binder连接池;

什么是Binder连接池
按照上面我们提的需求,服务端有10个不同的业务模块,每个模块我们都创建相应的AIDL接口和实现,然后这些不同业务模块的Binder对象统一由Binder连接池来管理,客户端可以根据不同编码或唯一标识来获取相应模块的Binder对象;Binder连接池主要用来管理和查询相应的Binder对象,具体流程如下图所示:

Paste_Image.png

接下来我们用一个实例来实现Binder连接池,场景是这样的,我们在服务端定义了两个aidl接口,分别是做加法和乘法的计算(当然可以增加更多接口),客户端通过Binder连接池来调用服务器的方法;

首先是服务端:
定义两个aidl的接口

// IMyAdd.aidl
package test.jiao.com.aidl;

interface IMyAdd {

    int add(int first,int second);
}
// IMyRide.aidl
package test.jiao.com.aidl;

interface IMyRide {

    int ride(int first, int second);
}

然后实现这两个aidl接口:

package test.jiao.com.aidl;

import android.os.RemoteException;

/**
 * date :2016/12/30
 * author :SuperJiao
 * Description
 */
public class MyAddImpl extends IMyAdd.Stub {

    @Override
    public int add(int first, int second) throws RemoteException {
        return first + second;
    }
}
package test.jiao.com.aidl;

import android.os.RemoteException;

/**
 * date :2016/12/30
 * author :SuperJiao
 * Description
 */
public class MyRideImpl extends IMyRide.Stub {

    @Override
    public int ride(int first, int second) throws RemoteException {
        return first * second;
    }
}

接下来实现我们的Binder连接池,首先定义Binder连接池的aidl接口

// IBinderPool.aidl
package test.jiao.com.aidl;

interface IBinderPool {
  
    IBinder getIBinder(int binderCode);
}

我是根据code来判断用户查询哪个接口,并返回相应的服务的,你也可以用其他方式来查询,这个可以根据需求灵活变换;
接下来我们来实现这个aidl接口

package test.jiao.com.aidl;

import android.os.IBinder;
import android.os.RemoteException;

/**
 * date :2016/12/30
 * author :SuperJiao
 * Description
 */
public class BinderPoolImpl extends IBinderPool.Stub {
    @Override
    public IBinder getIBinder(int binderCode) throws RemoteException {
        IBinder mBinder = null;
        switch (binderCode) {
            case ContentPool.POOL_ADD:

                mBinder = new MyAddImpl();
                break;
            case ContentPool.POOL_RIDE:

                mBinder = new MyRideImpl();
                break;
        }
        return mBinder;
    }
}

可以看出来,服务端所有aidl的接口的实现,都是在连接池中创建出来的,客户端只需要根据code来获取相应的方法即可;
接下来我们只需要创建一个服务在onbind中将该连接池的Binder对象返回即可;

package test.jiao.com.aidl;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

public class BinderPoolService extends Service {
    private Binder mBinderPool = new BinderPoolImpl();

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

然后是客户端,客户端的核心代码主要是连接池的具体实现;
首先客户端要和服务端一致创建一样的aidl接口,可以看上方服务端的代码;
客户端的aidl接口如下

Paste_Image.png

接下来就是客户端的核心连接池的实现:

package test.jiao.com.aidl;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;

import java.util.concurrent.CountDownLatch;

/**
 * date :2016/12/30
 * author :SuperJiao
 * Description
 */
public class BinderPool {

    private Context mContext;
    private static volatile BinderPool sInstance;
    private IBinderPool mBinderPool;

    private BinderPool(Context context) {
        mContext = context;

    }

    public static BinderPool getInstance(Context context) {
        if (sInstance == null) {
            synchronized (BinderPool.class) {
                if (sInstance == null) {
                    sInstance = new BinderPool(context);
                }
            }
        }
        return sInstance;
    }


    //绑定服务
    public synchronized void connectBinderPoolService() {
        Intent intent = new Intent();
        intent.setAction("com.jiao.myaidl.action.BINDERPOOL_SERVICE");
        mContext.bindService(intent, mBinderPoolConnection, Context.BIND_AUTO_CREATE);

    }

    //解绑服务
    public void disConnectBinderPoolService() {
        mContext.unbindService(mBinderPoolConnection);
    }

    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinderPool = IBinderPool.Stub.asInterface(service);
            try {
                mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    //服务死亡监听
    private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {

        @Override
        public void binderDied() {
            mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
            mBinderPool = null;
            connectBinderPoolService();
        }
    };


    //根据code获取相应的Binder对象
    public IBinder getBinder(int code) {
        IBinder binder = null;
        switch (code) {
            case ContentPool.POOL_ADD://加法
                try {
                    binder = mBinderPool.getIBinder(ContentPool.POOL_ADD);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            case ContentPool.POOL_RIDE://乘法
                try {
                    binder = mBinderPool.getIBinder(ContentPool.POOL_RIDE);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
        }

        return binder;
    }

}

可以看出来,客户端的连接池类,主要用来绑定服务端的服务,然后获取到服务端返回的连接池Binder对象,通过这个对象就可以连接服务器获取服务端的其他aidl接口的binder对象,通过这个Binder对象就可以调取服务端相应的方法;

接下来我们来验证一下,我们创建一个Activity,有两个按钮,一个是乘法计算一个是加法计算;点击相应的按钮得到相应的计算结果,代码如下:

package test.jiao.com.aidl;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity implements View.OnClickListener {

    private Button bt_aidl_add;
    private Button bt_aidl_ride;
    private BinderPool binderPool;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        start();
    }


    private void start() {
        bt_aidl_ride = (Button) findViewById(R.id.bt_aidl_ride);
        bt_aidl_add = (Button) findViewById(R.id.bt_aidl_add);
        bt_aidl_add.setOnClickListener(this);
        bt_aidl_ride.setOnClickListener(this);
        binderPool();
    }

    private void binderPool() {
        binderPool = BinderPool.getInstance(this);
        binderPool.connectBinderPoolService();
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bt_aidl_ride:

                IMyRide iMyRide = IMyRide.Stub.asInterface(binderPool.getBinder(ContentPool.POOL_RIDE));
                try {
                    Toast.makeText(this, iMyRide.ride(5, 5) + "", Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.bt_aidl_add:

                IMyAdd iMyAdd = IMyAdd.Stub.asInterface(binderPool.getBinder(ContentPool.POOL_ADD));
                try {
                    Toast.makeText(this, iMyAdd.add(5, 5) + "", Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

                break;
        }
    }


    @Override
    protected void onDestroy() {
        binderPool.disConnectBinderPoolService();
        super.onDestroy();
    }
}

运行结果如下:

Paste_Image.png

这样,无论服务端有多少个aidl接口,我们只需要扩展连接池就可以了,避免了创建更多的Service,大大的提升了程序的效率;

最后我们再来总结一下:在Android中的IPC机制最主要的方式有AIDL、Messenger、ContentProvider、Socket等;无论哪种方式,底层都是用Binder来实现的;本篇取其中最基本、通用的一种方式AIDL来讲解主要是要大家弄明白Android中的进程间通信的原理和具体实现,到这里,相信大家对Android的IPC机制和AIDL的实现已经有了进一步的了解;关于其他的方式,有兴趣的小伙伴可以自己探究一下;

上一篇 下一篇

猜你喜欢

热点阅读