上一篇复习了Android生命周期和本地Service的使用,这一篇继续总结一下Android远程Service的使用,远程Service就是在新的进程中开启service,这样会遇到一个问题,就是进程间通信的问题。Android系统的进程之间不能共享内存,那怎么传递对象呢,需要把对象弄成操作系统可以识别的形式,在Android中,可以采用AIDL来公开服务的接口,采用远程过程调用(Remote Procedure Call,RPC)和代理模式来实现跨进程通信。AIDL:Android Interface Definition Language,即Android接口描述语言,ADT会根据aidl文件在gen目录下生成对应的java接口文件。我们需要手工创建一个Service的子类并实现生成的java接口,然后在AndroidManifest.xml文件中进行配置。远程服务可以为多个客户端服务,由于涉及到数据通信,一般采用bindService的方式。
下面我们通过一个demo来看看AIDL是如何实现的。
首先创建服务端Android工程。目录结构如图

代码如下
User.java,为了实现跨进程数据传递,需要实现Parcelable 接口,是一种序列化方式。

public class User implements Parcelable {

	private int id;
	private String name;

	public User() {
	}

	public User(Parcel parcel) {
		this.id = parcel.readInt();
		this.name = parcel.readString();
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public int describeContents() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void writeToParcel(Parcel dest, int flags) {
		//顺序需与构造函数中read保持一致
		dest.writeInt(id);
		dest.writeString(name);
	}

	public static final Parcelable.Creator<User> CREATOR = new Creator<User>() {

		@Override
		public User createFromParcel(Parcel source) {
			return new User(source);
		}

		@Override
		public User[] newArray(int size) {
			return new User[size];
		}
	};

}

User.adil

parcelable User;

IRemoteService.aidl

/** 

远程的服务
IRemoteService.aidl
*/
interface IRemoteService {
    //返回基本类型
	int getId();
	//返回对象
	User getUser();

}

RemoteService.java

public class RemoteService extends Service {

	@Override
	public void onCreate() {
		Log.i(this.getClass().getName(), "onCreate");
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.i(this.getClass().getName(), "onStartCommand");
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public void onDestroy() {
		Log.i(this.getClass().getName(), "onDestroy");
	}

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

	@Override
	public boolean onUnbind(Intent intent) {
		Log.i(this.getClass().getName(), "onUnbind");
		return super.onUnbind(intent);
	}

	@Override
	public void onRebind(Intent intent) {
		Log.i(this.getClass().getName(), "onRebind");
		super.onRebind(intent);
	}

	IRemoteService.Stub mRemoteServiceBinder = new IRemoteService.Stub() {

		@Override
		public User getUser() throws RemoteException {
			User user = new User();
			user.setId(123456);
			user.setName("alexzhou");
			return user;
		}

		@Override
		public int getId() throws RemoteException {
			return 123456;
		}

	};
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.alexzhou.aidl.server"
    android:installLocation="internalOnly"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="10" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".RemoteServiceActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- android:process="name" name的值是随便取的 ,android:exported:是否允许被其它程序调用-->
        <service
            android:name="com.alexzhou.aidl.server.RemoteService"
            android:exported="true"
            android:process=":remote" >
            <intent-filter>
                <action android:name="com.alexzhou.service.REMOTE_SERVICE" />
            </intent-filter>
        </service>

    </application>

</manifest>

服务端的Activty是自动生成的。没写任何其他代码,这里就不贴出来了。
接着需要创建一个客户端Android工程,目录结构如下图:

代码如下:
先把User.java,User.aidl,IRemoteService.aidl三个文件复制到客户端,注意包名必须跟服务端所在的包名一致。
创建客户端主界面类ClientActivity.java

public class ClientActivity extends Activity implements OnClickListener{

	private TextView callbackView;
	private Button bindButton;
	private boolean isBind;
	private final String REMOTE_SERVICE_ACTION = "com.alexzhou.service.REMOTE_SERVICE";

	private IRemoteService mRemoteService;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        findViews();
        setListeners();
        callbackView.setText("no callback");
    }

    private void findViews() {
    	callbackView = (TextView)this.findViewById(R.id.callback);
    	bindButton = (Button)this.findViewById(R.id.bind);

    }

    private void setListeners() {
    	bindButton.setOnClickListener(this);
    }

	@Override
	public void onClick(View view) {
		switch(view.getId()) {
			case R.id.bind:
				this.bindService(new Intent(REMOTE_SERVICE_ACTION), mConntectin, Context.BIND_AUTO_CREATE);
				callbackView.setText("binding...");
				break;
		}
	}

	private ServiceConnection mConntectin = new ServiceConnection() {

		@Override
		public void onServiceDisconnected(ComponentName arg0) {
			callbackView.setText("Disconnected!");
		}

		@Override
		public void onServiceConnected(ComponentName name, IBinder binder) {
		    mRemoteService = IRemoteService.Stub.asInterface(binder);
		    isBind = true;
			try {
				int id = mRemoteService.getId();
				User user = mRemoteService.getUser();
				StringBuffer buffer = new StringBuffer();
				buffer.append("id:");
				buffer.append(id);
				buffer.append("name");
				buffer.append(user.getName());
				callbackView.setText(buffer.toString());
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	};

	@Override
	protected void onDestroy() {
		if(isBind) {
			this.unbindService(mConntectin);
			isBind = false;
		}
		super.onDestroy();
	}
}

布局文件main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

 <TextView android:id="@+id/callback"
        	android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	/>

	<Button android:id="@+id/bind"
        	android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	android:text="@string/bind_remote_service_text"/>

</LinearLayout>

现在运行你的客户端程序,点击绑定远程服务按钮,如果一切顺利,将会看到服务端返回的信息。如图:

通过eclipse控制台Devices视图,可以看到远程服务进程已启动,如图:

可能会遇到的问题和解决办法:
(1)AIDL unable to start service not found
客户端和服务端activty包名相同了,改成不同就可以
(2)Not allowed to bind to service Intent
在服务端配置文件中把android:exported = false 改成 true,android:exported表示是否允许被其它程序调用
(3)Binder invocation to an incorrect interface
客户端aidl文件的包名跟service的包名不一样,改成一样的就ok

android使用tcpdump抓包

最近游戏在接qq opensdk的时候调用一个cgi一直不成功,文档描述太简单,我们调用的又是互娱这边msdk的api,由msdk调用opensdk相关api,中间跨了两部门,为了...

阅读全文

Android.mk文件解读

我们在Android平台写c/c++程序的时候需要用到Android.mk(Makefile),一般用来编译c/c++源码、引用第三方头文件和库,生成程序所需的so文件。下面是一个cocos2...

阅读全文

Android性能优化案例研究(下)

去掉冗余的图层 为 了去掉重绘我们必须首先理解它从哪里产生的。这就轮到Hierarchy Viewer和Tracer for OpenGL大显身手的时候了。Hierarchy Viewer是ADT工具...

阅读全文

欢迎留言