Android 进程通信方法

Service是Android四大组件之一,一般用来处理后台任务。Service运行于宿主进程的主线程中,所以如果处理耗时的后台任务,需要启动子线程来执行,也可以考虑使用IntentService。IntentService会启动一个工作线程,依次处理到达的工作请求。

Service运行于后台,就需要与前台的Client(一般是Activity)通信,本文整理了几种常见的通信方式供大家参考,如有遗漏,请补充,错误请指正,谢谢。

一、通过PendingIntent通信

按照官方文档的说法,PendingIntent是“A description of an Intent and target action to perform with it”。我们能够通过静态方法,getActivity(Context, int, Intent, int), getActivities(Context, int, Intent[], int), getBroadcast(Context, int, Intent, int), 和 getService(Context, int, Intent, int)创建它。

联系到本文的内容,我们选择使用,getBroadcast创建PendingIntent,如下:

mPendingIntent = PendingIntent.getBroadcast(this, requestCode, new Intent(ACTION), flags);

然后在启动Service时,把PendingIntent通过Intent传给Service。

 

Intent intent = new Intent(this, CustomService.class);
intent.putExtra("PendingIntent", mPendingIntent);
startService(intent);

 

因为PendingIntent继承了Parcelable接口,可以当作参数直接传递。

Service中获取传递的PendingIntent对象。

pendingIntent = intent.getParcelableExtra("PendingIntent”);

就可以使用它和Client通信了。

Client端注册一个BroadcastReceiver, 处理PendingIntent的ACTION和requestCode。

Service端使用PendingIntent的send()方法就可以和Client通信了。

此处执行send()和执行sendBroadcast()效果相同。

下面也正是我想说的第二种方法。

二、通过BroadcastReceiver通信

PendingIntent方法中主要也是借助于BroadcastReceiver进行通信的,此处我们要说单纯使用BroadcastReceiver方式进行通信的方法。这里我们使用LocalBroadcastManager注册,发送,解除注册广播。使用LocalBroadcastManager发送广播比发送全局广播更加安全和高效。

首先,在Activity中通过

mBroadcastManager = LocalBroadcastManager.getInstance(this);

获取LocalBroadcastManager的实例。

然后使用

 

mBroadcastManager.registerReceiver(mLocalReceiver, new IntentFilter(ACTION));
mBroadcastManager.unregisterReceiver(mLocalReceiver);

 

注册和解除注册广播。

最后,在Service中同样使用

mLocalManager = LocalBroadcastManager.getInstance(getApplicationContext());

获取其实例,并在需要发送广播的地方,使用

mLocalManager.sendBroadcast(intent);

进行广播的发送,这样就可以进行简单的通信了。

三、通过IBinder通信

如果Service和Activity运行在同一个进程,可以考虑使用IBinder通信。

具体的步骤如下:

首先,在Service中创建Binder的实例,并提供公有的方法供Client调用。

 

public class MyBinder extends Binder {
        public MyService getService() {
            return MyService.this;
        }
 }

 

getService方法返回当前Service实例,Client获得该实例之后可以调用Service的公有方法。

在onBind回调方法中把Binder对象的实例返回给Client。

 

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

 

Activity在bindService的时候,传入ServiceConnection对象。

bindService(intent, connection, BIND_AUTO_CREATE);

ServiceConnection用来监听Service的状态变化,运行于主线程。

它提供了两个回调的方法,onServiceConnected和onServiceDisconnected,分别在连接建立和断开的时候回调。

 

private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = ((MyService.MyBinder)service).getService();
            isBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
            mService = null;
        }
 };

 

在onServiceConnected的时候我们获得Service的时候,这样就可以调用公有方法了。记得在通信完成之后unbindService,这样在所有连接断开之后,Service也就停止运行了。

四、通过Messager通信

Messenger是一个指向Handler的引用,通过它可以给Handler发送消息。我们可以基于它实现跨进程的通信(IPC)。

如果你要求跨进程的多任务处理,那你可以选择AIDL实现,但是如果你不要求多线程处理,那Messenger完全可以满足你的需求,接下来我们具体看一下使用Messenger的通信的步骤。

首先,在Service中实现一个Handler,用于接收来自客户端的回调。

 

mHandlerThread = new HandlerThread("Messenger-Service");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_REGISTER_CLIENT:
                        clients.add(msg.replyTo);
                        break;
                    case MSG_UNREGISTER_CLIENT:
                        clients.remove(msg.replyTo);
                        break;
                    case MSG_SET_VALUE:
                        Message message = Message.obtain(null, MSG_SET_VALUE);
                        message.arg1 = msg.arg1;
                        for (Messenger messenger : clients) {
                            try {
                                messenger.send(message);
                            } catch (RemoteException e) {
                                e.printStackTrace();
                            }
                        }
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
  };

 

这里我们使用了HandlerThread的方式,这里所有回调都运行在一个子线程中。如果你的Service不处理耗时操作,可以单纯的实现Handler即可。

其次,把我们的Messenger指向Handler。

mMessenger = new Messenger(mHandler);

在onBind中返回Messenger的IBinder。

 

public IBinder onBind(Intent intent) {
     return mMessenger.getBinder();
}

然后,客户端从返回的IBinder中实例化Messenger,并通过该Messenger向Service的Handler发送消息。

 

private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = new Messenger(service);

            try {
                Message message = Message.obtain(null, MessengerService.MSG_REGISTER_CLIENT);
                message.replyTo = mMessenger;
                mService.send(message);

                message = Message.obtain(null, MessengerService.MSG_SET_VALUE);
                message.arg1 = this.hashCode();
                mService.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

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

 

Service收到Messenger发送的消息后,在Handler的handleMessage中进行处理。

从上面的代码中我们可以看到,我们把客服端的Messenger作为replyTo发送给了Service,这样Service就可以使用该Messenger向对应客户端发送消息了。

下面附上Client端的全部代码,仅供参考。

 

public class MainActivity extends AppCompatActivity {

    private Messenger mService;
    private Messenger mMessenger;
    private boolean isBound;

    private TextView textView;

    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MessengerService.MSG_SET_VALUE:
                    textView.setText(Integer.toString(msg.arg1));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = new Messenger(service);

            try {
                Message message = Message.obtain(null, MessengerService.MSG_REGISTER_CLIENT);
                message.replyTo = mMessenger;
                mService.send(message);

                message = Message.obtain(null, MessengerService.MSG_SET_VALUE);
                message.arg1 = this.hashCode();
                mService.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

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

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

        mMessenger = new Messenger(new IncomingHandler());
        textView = (TextView) findViewById(android.R.id.text1);
    }

    @Override
    protected void onStart() {
        super.onStart();
        doBindService();
    }

    @Override
    protected void onStop() {
        super.onStop();
        doUnBindService();
    }

    private void doBindService() {
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, connection, Service.BIND_AUTO_CREATE);
        isBound = true;
    }

    private void doUnBindService() {
        if (isBound) {
            try {
                if (mService != null) {
                    Message message = Message.obtain(null, MessengerService.MSG_UNREGISTER_CLIENT);
                    message.replyTo = mMessenger;
                    mService.send(message);
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            unbindService(connection);
            isBound = false;
        }
    }
}

 

五、通过AIDL通信

最后,我们说一下通过AIDL进行通信的方式。如果你不需要进行IPC通信,那么可以使用我们三中讲到的实现Binder的方式,如果你需要IPC,但不需要处理多线程的请求,那我们四中讲的Messenger方式应该也可以满足要求。如果你即需要IPC,又需要多线程处理,那AIDL可以满足你的要求。

下面我们说一下实现AIDL的步骤,以Android Studio为例。

首先,创建.aidl文件。在AS中,右键单击项目目录,选择新建AIDL文件。新建完成的AIDL文件在与Java同级目录的aidl目录中。这里需要注意:

新建的AIDL文件需要和你的Service的包名相同,否则不能自动生成接口的文件。

我们这里的文件内容如下:

 

// MyRemoteService.aidl
package com.lkk.demo.aidl;

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

interface MyRemoteService {
    /**
     * 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);

    int getPid();
}

 

然后,实现通信所需的Service,在Service中实现自动生成的接口。

 

private final MyRemoteService.Stub stub = new MyRemoteService.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public int getPid() throws RemoteException {
            return Process.myPid();
        }
};

 

在getPid方法中返回本进程的进程ID。

因为MyRemoteService.Stub继承了Binder,所以可以在onBind方法中直接返回stub.

 

public IBinder onBind(Intent intent) {
        return stub;
}

现在可以在remote进程中连接本Service,并调动Stub暴露的方法了。

我们在上边的service的Manifest中声明可以启动它的action

 

<service android:name="com.lkk.demo.aidl.AIDLService">
       <intent-filter>
           <action android:name="com.lkk.demo.AIDL_SERVICE" />
       </intent-filter>
</service>

然后新建一个项目,按照上边的方法新建一个.aidl文件,AIDL的文件名和包名必须和上边的完全一致,否则会出错。把上边AIDL文件的内容原封不动的拷贝到此文件中。

下面就可以Bind Service了。

 

Intent intent = new Intent("com.lkk.demo.AIDL_SERVICE");
intent.setPackage("lkk.download.com.aidldemo");
bindService(intent, connection, BIND_AUTO_CREATE);

 

这里通过隐式的方式启动Service,必须设置包名,这是5.0之后的新要求。

此处的connection为:

 

private MyRemoteService service;
private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            service = MyRemoteService.Stub.asInterface(iBinder);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            service = null;
        }
};

 

这里我们必须使用MyRemoteService.Stub.asInterface把返回值转换成我们想要的接口,这样就可以调用它暴露的方法了。

textView.setText(String.valueOf(service.getPid()));

以上内容是我现在所能了解到的关于Service和Client通信的方法了,这里一一记录下来,以备后续查看,如果能帮到大家那是最好不过了。

内容上可能存在着错误和不足,希望大家不吝赐教,如果有其他没有涉及到的更好的方法可以comment一下,谢谢。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值