【问题标题】:Rapid IPC with Messengers or AIDL带 Messenger 或 AIDL 的快速 IPC
【发布时间】:2016-02-12 21:52:40
【问题描述】:

我正在尝试在 Android 中创建一个与远程服务快速通信的程序(约 40,000 次/秒),但是所有 Android IPC 似乎都无法完成这项任务。我的第一次尝试涉及一个标准的 Messenger 系统,该系统无法超过每秒约 2,000 次,同样糟糕的是,它似乎被间歇性延迟打断。

MainActivity(使用 Messenger 测试)

public class MainActivity extends Activity implements ServiceConnection{

    Messenger mServiceMessenger;
    Messenger mClientMessenger = new Messenger(new ClientHandler());

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this,TestService.class);
        bindService(intent,this, Context.BIND_AUTO_CREATE);
    }


    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mServiceMessenger = new Messenger(service);
        Message m = Message.obtain();
        m.replyTo = mClientMessenger;
        try {
            mServiceMessenger.send(m);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {}

    public class ClientHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            Log.d("Spam","Message Received");
        }
    }
}

RemoteService(使用 Messenger 测试)

public class TestService extends Service {

    private Messenger mServiceMessenger = new Messenger(new ServiceHandler());
    private Messenger mClientMessenger;
    private Random r = new Random();

    public TestService() {
        super();
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }


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

    public void initSpam(){
        for(int i=0;i<10;i++) {
            TimerTask task = new TimerTask() {
                @Override
                public void run() {
                    Bundle b = new Bundle();
                    b.putInt("INT",r.nextInt());
                    b.putLong("LONG",r.nextLong());
                    b.putBoolean("BOOL",r.nextBoolean());
                    b.putFloat("FLOAT",r.nextFloat());
                    b.putDouble("DOUBLE",r.nextDouble());
                    b.putString("STRING",String.valueOf(r.nextInt()));
                    Message msg = Message.obtain();
                    msg.setData(b);

                    try {
                        mClientMessenger.send(msg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            };
            Timer timer = new Timer();
            timer.scheduleAtFixedRate(task,1,1);
        }
    }

    public class ServiceHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            mClientMessenger = msg.replyTo;
            initBarrage();

        }
    }
}

第二次尝试是用 AIDL 完成的。尽管这也为 IPC 实现了 Binder,但我认为开销要少得多。然而,事实证明,AIDL 的效率并不比 Messenger 高得多,而且它也没有解决口吃的问题。

MainActivity(使用 AIDL 测试)

public class MainActivity extends Activity implements ServiceConnection{

    IRemoteService mService;
    TextView countTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this,TestService.class);
        bindService(intent,this, Context.BIND_AUTO_CREATE);
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {

        mService = IRemoteService.Stub.asInterface(service);
        try {
            mService.registerCallback(mClientBinder);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {}


    public final IServiceAidlCallback.Stub mClientBinder = new IServiceAidlCallback.Stub(){
        public void basicTypes(int anInt, long aLong, boolean aBoolean,
                               float aFloat, double aDouble, String aString){
            Log.d("Spam","Callback Received");
        }
    };
}

RemoteService(使用 AIDL 测试)

public class TestService extends Service {

    private Random r = new Random();

    private IServiceAidlCallback mClientCallback;

    public TestService() {
        super();
    }

    @Override
    public void onRebind(Intent intent) {
        super.onRebind(intent);
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

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

    public final IRemoteService.Stub mBinder = new IRemoteService.Stub(){
        public void registerCallback(IBinder callback){

            mClientCallback = IServiceAidlCallback.Stub.asInterface(callback);
            initSpam();

        }
    };

    public void initSpam(){
        for(int i=0;i<10;i++) {
            TimerTask task = new TimerTask() {
                @Override
                public void run() {
                    try {
                        mClientCallback.basicTypes(
                                r.nextInt(),
                                r.nextLong(),
                                r.nextBoolean(),
                                r.nextFloat(),
                                r.nextDouble(),
                                String.valueOf(r.nextInt()));
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            };
            Timer timer = new Timer();
            timer.scheduleAtFixedRate(task,1,1);
        }
    }
}

在这两种情况下,我是否做错了什么会阻止我达到约 5,000/秒?还是有其他我不知道的 Android IPC 系统?

【问题讨论】:

  • 在具有制造商提供的 ROM 的现成硬件上,您将无法以每秒约 40,000 次的速度持续执行任何操作而不会卡顿,更不用说跨进程了。安卓 SDK。 SDK 应用无法控制线程和进程调度。
  • 你可以考虑原始的Binder 协议(transact / onTransact),它应该比 AIDL 快,但也不快,也可能是套接字或 MemoryFile(我实际上从未使用过它,所以我'不确定它有多快)?
  • @CommonsWare 我可以通过 telnet 通过本地网络获得大约 40,000 个远程服务,该远程服务本质上是 IPC,Android 上没有什么可以匹配的吗?
  • @Beryllium: telnet 不是 Android SDK 应用程序。 telnet 会遇到同样的基本问题,因为操作系统线程和进程调度程序不能保证给您足够的时间来完成您的处理目标。正如 pskink 建议的那样,您当然欢迎使用套接字,这将直接类似于使用 telnet。也欢迎您确认Timer 将让您达到约 40,000 个事件/秒,无论 IPC 是什么。我只是警告你,你最终可能会感到失望。
  • 使用原始Binders(和FLAG_ONEWAY)我能够在120-200毫秒内发送100.000个整数,当然不是使用100000个事务,而是每个事务连续打包100个整数,从而进行 1000 次交易,在鸸鹋上运行

标签: android performance android-service ipc android-binder


【解决方案1】:

做这样的事情:

MainActivity

// use it for writing: stream.write(byte[]) 
// (make sure to write as biggest data chunks as possible)
// or wrap it around some other streams like DataOutputStream
private OutputStream stream;

// ServiceConnection implementation
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
    try {
        ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
        stream = new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]);

        Parcel data = Parcel.obtain();
        FileDescriptor readFileDescriptor = pipe[0].getFileDescriptor();
        data.writeFileDescriptor(readFileDescriptor);
        service.transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0);
    } catch (Exception e) {
        e.printStackTrace();
    }
    Log.d(TAG, "onServiceConnected " + stream);
}

远程服务

@Override
public IBinder onBind(Intent intent) {
    Log.d(TAG, "onBind ");
    return binder;
}

IBinder binder = new Binder() {
    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        ParcelFileDescriptor pfd = data.readFileDescriptor();
        final InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
        // do something with a 'stream', start a new Thread for example and read data in a loop
        ...
        ...
        return true;
    }
};

【讨论】:

  • 我再次通过您的链接与您交谈。就您在 onServiceConnected 和 onTransact 覆盖中的事务调用而言,我做了几乎相同的事情。除非您使用 null 表示回复,使用 0 表示标志。其中我使用了一个包裹进行回复。我会试试那些参数看看。在 createpipe 方面,谢谢,我从来不知道。这有助于进行进程间通信。如果我通过了,会通知你的。
  • @JaveneCPPMcGowan reply 是可选的(只需要data - 即使你根本不使用它),你使用了什么flags
  • 我使用 0 作为标志。而且我仍然没有运气。我在这里发布了我的示例代码作为答案。请阅读,稍后我将删除它。
  • @JaveneCPPMcGowan 你正在从另一个活动开始,在这种情况下你需要startActivityForResult,而不是Binder
  • @JaveneCPPMcGowan 你有上面的工作代码 - 只需使用从onBind 方法返回Binder 的“绑定”服务
猜你喜欢
  • 2010-11-12
  • 2016-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多