14.2.1. 绑定到远程Service

优质
小牛编辑
134浏览
2023-12-01

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;

}

}

  1. LogConnection用于处理远程Service的连接/断开。具体将在后面介绍。
  2. 它是一个ActionIntent,用于连接到远程Service。它必须与Manifest文件中LogService相应<intent-filter>部分的Action相匹配。
  3. 把数据加入Intent,它可以在另一端解析出来。
  4. bindService()请求Android运行时将这个Action绑定到相应的远程Service。在Intent之余,我们也传递一个远程Service的ServiceConnection对象,用以处理实际的连接/断开操作。
  5. LogConnection用来处理远程Service的连接/断开。它需要以ServiceConnection为基类,提供onServiceConnected()与onServiceDisconnected()的实现。
  6. 我们需要将这个BoundService转换为LogService类型。为此,调用辅助函数ILogService.Stub.asInterface()。
  7. onServiceDisconnected()在远程Service失效时触发。可以在里面执行一些必要的清理工作。在这里,我们仅仅将logService设为null,方便垃圾收集器的回收。
  8. 如果成功绑定了远程Service,那就可以像调用本地方法那样调用它了。在前面LogService的实现部分曾提到,logService.log_d()所做的工作就是将两个字符串传递给log_d()。
  9. 如前所说,如果需要在远程调用中传递自定义对象,那就必须先把它做成Parcel。既然Message已经是Parcelable对象,将它作为参数传递即可。
  10. 得到Parcel之后,简单调用logService.log()方法,将它传递给LogService。
  11. 远程调用可能会很不稳定,因此最好加入一个针对RemoteException的错误处理。
  12. 在Activity将要销毁之前,解除Service的绑定以释放资源。

到这里,客户端已经完工。它的界面很简单,只有一个触发onClick()调用的按钮。只要用户点击按钮,我们的客户端程序就会调用一次远程Service。