14.2.1. 绑定到远程Service
14.2.1.绑定到远程Service
客户端可以是一个Activity,这样我们可以在图形界面中看出它的工作状况。在这个Activity中,我们将绑定到远程Service,随后就可以像一个本地的Service那样使用它了。Android的Binder将自动处理其间的序列化/反序列化操作。
绑定操作是异步的,我们先发出请求,至于具体的操作可能会在稍后进行。为此,我们需要一个回调机制来响应远程服务的连接和断开。
连接上Service之后,我们就可以像本地对象那样使用它了。不过,要传递复杂的数据类型(比如自定义的Java对象),就必须把它做成Parcel。对我们的Message类型而言,它已经实现了Parcelable接口,拿来直接使用即可。
例 14.5. LogActivity.java
package com.marakana.logclient;
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.Parcel;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import com.marakana.logservice.ILogService;
import com.marakana.logservice.Message;
public class LogActivity extends Activity implements OnClickListener {
private static final String TAG = "LogActivity";
ILogService logService;
LogConnection conn;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Request bind to the service
conn = new LogConnection(); //
Intent intent = new Intent("com.marakana.logservice.ILogService"); //
intent.putExtra("version", "1.0"); //
bindService(intent, conn, Context.BIND_AUTO_CREATE); //
// Attach listener to button
((Button) findViewById(R.id.buttonClick)).setOnClickListener(this);
}
class LogConnection implements ServiceConnection { //
public void onServiceConnected(ComponentName name, IBinder service) { //
logService = ILogService.Stub.asInterface(service); //
Log.i(TAG, "connected");
}
public void onServiceDisconnected(ComponentName name) { //
logService = null;
Log.i(TAG, "disconnected");
}
}
public void onClick(View button) {
try {
logService.log_d("LogClient", "Hello from onClick()"); //
Message msg = new Message(Parcel.obtain()); //
msg.setTag("LogClient");
msg.setText("Hello from inClick() version 1.1");
logService.log(msg); //
} catch (RemoteException e) { //
Log.e(TAG, "onClick failed", e);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroyed");
unbindService(conn); //
logService = null;
}
}
- LogConnection用于处理远程Service的连接/断开。具体将在后面介绍。
- 它是一个ActionIntent,用于连接到远程Service。它必须与Manifest文件中LogService相应<intent-filter>部分的Action相匹配。
- 把数据加入Intent,它可以在另一端解析出来。
- bindService()请求Android运行时将这个Action绑定到相应的远程Service。在Intent之余,我们也传递一个远程Service的ServiceConnection对象,用以处理实际的连接/断开操作。
- LogConnection用来处理远程Service的连接/断开。它需要以ServiceConnection为基类,提供onServiceConnected()与onServiceDisconnected()的实现。
- 我们需要将这个BoundService转换为LogService类型。为此,调用辅助函数ILogService.Stub.asInterface()。
- onServiceDisconnected()在远程Service失效时触发。可以在里面执行一些必要的清理工作。在这里,我们仅仅将logService设为null,方便垃圾收集器的回收。
- 如果成功绑定了远程Service,那就可以像调用本地方法那样调用它了。在前面LogService的实现部分曾提到,logService.log_d()所做的工作就是将两个字符串传递给log_d()。
- 如前所说,如果需要在远程调用中传递自定义对象,那就必须先把它做成Parcel。既然Message已经是Parcelable对象,将它作为参数传递即可。
- 得到Parcel之后,简单调用logService.log()方法,将它传递给LogService。
- 远程调用可能会很不稳定,因此最好加入一个针对RemoteException的错误处理。
- 在Activity将要销毁之前,解除Service的绑定以释放资源。
到这里,客户端已经完工。它的界面很简单,只有一个触发onClick()调用的按钮。只要用户点击按钮,我们的客户端程序就会调用一次远程Service。