`
byandby
  • 浏览: 1697403 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

android 向远程服务传递复杂类型

阅读更多
   在看这篇之前 建议看看 【android 服务概述 本地服务示例】http://byandby.iteye.com/blog/1026110 和 【android AIDL服务】http://byandby.iteye.com/blog/1026193 和这两篇有点关系。

   向服务传递复杂的类型
要向服务传递复杂的类型,需要执行比传递 Java原语类型更多的工作。在开始这一工作之前,应该了解一下AIDL对非原语类型的支持。
     AIDL 支持String和CharSequence。
     AIDL 支持传递其他AIDL接口,但你引用的每个AIDL接口都需要一个 import语句(即使引用的AIDL位于相同的包中)。
     AIDL 支持 java.util.List 和 java.util.Map,但具有一些限制。集合中项的数据类型包括Java原语、String、CharSequence 或 android.os.Parcelable。无需为List或Map提供import语句,但需要为Parcelable提供import语句。
     除了字符串以外,非原语类型需要一个方向指示符。方向指示符包括 in、out和intout。in表示值由客户端设置,out表示值由服务设置,inout表示客户端和服务都设置了该值。
Parcelable 接口告诉Android 运行时在封送 (marshalling) 和解封送 (unmarshalling) 过程中如果序列化和反序列化对象。 下面是我们的 Person类 它实现了 Parcelable接口。
package com.syh;

import android.os.Parcel;
import android.os.Parcelable;

public class Person implements Parcelable {
	private int age;
	private String name;
	public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {

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

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

	};

	public Person() {
	}

	private Person(Parcel in) {
		readFromParcel(in);
	}

	@Override
	public int describeContents() {
		return 0;
	}

	@Override
	public void writeToParcel(Parcel dest, int flags) {
		dest.writeInt(age);
		dest.writeString(name);
	}

	public void readFromParcel(Parcel in) {
		age = in.readInt();
		name = in.readString();
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getName() {
		return name;
	}

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


要开始实现此接口,首先在Eclipse 中创建新Android项目 StockQuoteService2。将 CreateActivity设置为 MainActivity并使用包 com.syh。然后将Person.java文件添加到新项目的 com.syh包下。

Parcelable 接口定义在封送/解封送过程中混合和分解对象的契约。Parcelable接口的底层是Parcel容器对象。Parcel类是一种最快的序列化/反序列化机制,专为Android中的进程间通信而设计。该类提供了一些方法来将成员容纳到容器中,以及从容器展开成员。要为进程间通信正确地实现对象,必须执行以下操作。
(1) 实现Parcelable接口。这意味着要实现 writeToParcel()和readFromParcel()。写入方法将对象写入到包裹(parcel)中,而读取方法从包裹中读取对象。请注意,写入属性的顺序必须与读取顺序相同。
(2) 向该类添加一个名为 CREATOR 的 static final 属性。该属性需要实现 android.os.Parcelable.Creator<T>接口。
(3) 为Parcelable提供一个构造函数,该函数知道如何从 Parcel创建对象。
(4) 在与包含复杂类型的 .java文件匹配的 .aidl文件中定义 Parcelable类。AIDL编译器在编译AIDL 文件时将查找此文件。下面是我们的 Person.aidl文件,是.aidl文件不是java文件。
package com.syh;
parcelable Person;
 

   说明:在看到Parcelable时可能会引起以下疑问,为什么Android不使用内置的Java序列化机制?事实是,Android团队认为Java中的序列化太慢了,难以满足Android的进程间通信的需求。所以该团队构建了Parcelable解决方案。Parcelable方法要求显示序列化类的成员,但最终序列化对象的速度将快得多。
另请注意,Android提供了两种机制来将数据传递给另一个进程。第一种是使用Intent将bundle对象传递给活动,第二种是将Parcelable传递给服务。这两种机制不可互换,不要混淆。也就是说,Parcelable无法传递给活动。( Activity)如果希望启动一个活动并向其传递一些数据,可以使用Bundle。Parcelable只能用作AIDL定义的一部分。


   项目中每个Parcelable 都需要一个 .aidl文件。在本例中,我们只有一个Parcelable,那就是 Person。
   现在在远程服务中使用Person类。为了简单起见,我们将修改 IStockQuoteService,以接受类型为Person的输入参数。思路是,客户端将Person传递给服务,告诉它谁请求了报价。新的 IStockQuoteService.aidl文件内容如下:是 .aidl文件不是java文件
package com.syh;
import com.syh.Person;
interface IStockQuoteService
{
	String getQuote(in String ticker, in Person requester);
}


getQuote()方法现在接受两个参数:股票代号和Person,后者指定谁发出了请求。请注意,我们在参数上包含了方向指示符,因为这些参数属于非原语类型,我们还为Person类提供了一条 Import语句。Person类与服务定义位于同一个包中 (com.syh)。
下面来看一下我们的布局文件吧, 啥都没有 就一个 TextView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="This is where the service would ask for help"
    />
</LinearLayout>


我们的服务类 StockQuoteService2.java
package com.syh;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class StockQuoteService2 extends Service {

	private NotificationManager notificationMgr;

	public class StockQuoteServiceImpl extends IStockQuoteService.Stub {

		@Override
		public String getQuote(String ticker, Person requester)
				throws RemoteException {
			return "Hello" + requester.getName() + "! Quote for " + ticker
					+ " is 20.0";
		}
	}

	@Override
	public void onCreate() {
		super.onCreate();
		notificationMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
		displayNotificationMessage("onCreate() called in StockQuoteService2");
	}

	@Override
	public void onDestroy() {
		displayNotificationMessage("on Destroy() called in StockQuoteService2");
		super.onDestroy();

	}

	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);
	}

	@Override
	public IBinder onBind(Intent intent) {
		displayNotificationMessage("onBind() called in StockQuoteService2");
		return new StockQuoteServiceImpl();
	}

	private void displayNotificationMessage(String message) {
		Notification notification = new Notification(R.drawable.emblem_note,
				message, System.currentTimeMillis());

		PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
				new Intent(this, MainActivity.class), 0);

		notification.setLatestEventInfo(this, "StockQuoteService2", message,
				contentIntent);

		notificationMgr.notify(R.id.app_notification_id, notification);
	}
}


我们使用了通知,通过状态栏 通知用户后台服务的不同状态,来看看运行效果。









下面是实现服务还需要完成的其他操作。
(1) 将note图像文件添加到/res/drawable 目录,随便一个图像就OK。
(2) 需要为通知管理器NotificationManager提供一个应用程序级唯一ID(整数)。要创建唯一ID,可以向字符串资源文件 /res/values/strings.xml 添加一个项ID。当调用notify()方法时,会将唯一ID 传递给通知管理器。

最后我们还需要修改 AndroidManifest.xml 来声明我们的服务 StockQuoteService2




    好了到这里我们服务端就OK 了,下面来 弄客户端 新建一个名为 StockQuoteClient2的项目。使用 com.sayed作为包名,使用MainActivity作为活动名。 客户端就简单了,把服务端的 Person.java、IStockQuoteService.aidl、Person.aidl文件复制到客户端的com.sayed 包下。

来看看我们客户端的布局文件。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<Button
	android:id="@+id/bindBtn"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:text="Bind"/>

<Button
	android:id="@+id/callBtn"
	android:layout_height="wrap_content"
	android:layout_width="wrap_content"
	android:text="Call Again"/>

<Button
	android:id="@+id/unbindBtn"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:text="UnBind"/>
</LinearLayout>


我们的MainActivity类
package com.sayed;

import com.syh.IStockQuoteService;
import com.syh.Person;
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.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {
	private static final String TAG = "StockQuoteClient2";
	private IStockQuoteService stockService = null;
	private Button bindBtn = null;
	private Button callBtn = null;
	private Button unbindBtn = null;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		bindBtn = (Button) findViewById(R.id.bindBtn);
		bindBtn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				bindService(new Intent(IStockQuoteService.class.getName()),
						serConn, Context.BIND_AUTO_CREATE);
				bindBtn.setEnabled(false);
				callBtn.setEnabled(true);
				unbindBtn.setEnabled(true);
			}
		});

		callBtn = (Button) findViewById(R.id.callBtn);
		callBtn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				callService();
			}
		});
		callBtn.setEnabled(false);

		unbindBtn = (Button) findViewById(R.id.unbindBtn);
		unbindBtn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				unbindService(serConn);
				bindBtn.setEnabled(true);
				callBtn.setEnabled(false);
				unbindBtn.setEnabled(false);
			}
		});
	}

	private void callService() {
		try {
			Person person = new Person();
			person.setAge(33);
			person.setName("Sayed");
			String response = stockService.getQuote("GOOG", person);
			Toast.makeText(this, "Value from service is " + response,
					Toast.LENGTH_LONG).show();
		} catch (RemoteException e) {
			Log.e("MainActivity", e.getMessage(), e);
		}
	}

	private ServiceConnection serConn = new ServiceConnection() {

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			Log.v(TAG, "onServiceConnected() called");
			stockService = IStockQuoteService.Stub.asInterface(service);
			callService();
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			Log.v(TAG, "onServiceDisconnected() called");
			stockService = null;
		}
	};
}


   为了避免大家把 .aidl文件放错位置, 来2张项目截图,请大家找准位置,看准包名。
客户端



服务端



源码已上传
  • 大小: 26.3 KB
  • 大小: 21.7 KB
  • 大小: 25.5 KB
  • 大小: 10.7 KB
  • 大小: 20.9 KB
  • 大小: 19.6 KB
  • 大小: 11.8 KB
4
9
分享到:
评论
1 楼 RidYuan 2013-05-07  
 

相关推荐

    传递复杂数据类型的AIDL服务的客户端

    总之,"传递复杂数据类型的AIDL服务的客户端"示例展示了如何利用AIDL在客户端和服务器之间传递自定义的复杂数据类型,这对于构建需要进行进程间通信的大型Android应用是非常重要的。理解并掌握AIDL可以帮助开发者更...

    android访问远程数据库

    - **WebService**:一种更为复杂的远程调用技术,基于 SOAP 协议,可以实现跨平台、跨语言的服务调用。 #### 三、WebServer 的搭建 为了实现 Android 客户端与远程数据库之间的数据交换,我们需要先搭建一个 Web...

    Android远程服务小demo

    在实际开发中,AIDL不仅可以用于简单的方法调用,还可以支持更复杂的类型(如List、Map等),甚至可以传递自定义对象。但要注意,由于IPC的限制,跨进程通信可能会有性能开销,因此尽量减少不必要的数据传输。 总的...

    Android调用远程服务(AIDL)

    Android调用远程服务(AIDL, Android Interface Definition Language)就是一种这样的机制,它允许开发者定义接口,使得客户端应用能够与运行在另一个进程中(即远程服务端)的应用组件进行通信。下面我们将详细探讨...

    Android_远程service

    远程Service是Android中一种特殊的服务类型,它可以被其他应用调用和控制。为了实现这一点,远程Service需要实现AIDL(Android Interface Definition Language),这是一种接口定义语言,用于描述服务提供的方法。...

    Android AIDL基础-利用AIDL实现客户端向服务端传递基本类型

    3. 如果需要传递复杂数据结构,确保它们实现了Parcelable或Serializable接口。 以上就是利用AIDL实现客户端向服务端传递基本类型的基本步骤和知识点。在实际开发中,AIDL能够帮助我们构建稳定、高效的多进程应用,...

    android 使用AIDL和远程服务实现进程通信的学习

    本文将深入探讨如何利用Android Interface Definition Language (AIDL) 和远程服务(Remote Service)来实现进程间的通信。AIDL是Android提供的一种接口定义语言,用于生成允许不同进程间进行方法调用的代码。 **...

    基于Android的远程医疗监测系统设计.pdf

    在远程医疗监测系统中,MQTT协议用于报警信息的推送,确保在紧急情况下,能够快速、可靠地将报警信息传递给医生或患者。 【远程医疗服务系统应用前景】 随着移动技术的发展和互联网的普及,远程医疗服务系统具有...

    android 本地服务通信 AIDL service服务与activity之间的通信

    本示例“android 本地服务通信 AIDL service服务与activity之间的通信”通过一个学号查询学生的例子,清晰地展示了如何利用AIDL实现在Activity和Service之间的数据传递。 首先,我们需要了解AIDL的基本概念。AIDL...

    Android Service学习之AIDL, Parcelable和远程服务

    在Android中,如果你需要在Intent或者Binder中传递复杂对象,Parcelable比Serializable效率更高。实现Parcelable接口需要编写大量的样板代码,但它提供了更高效的序列化和反序列化方式。实现Parcelable的基本步骤...

    Android访问WCF服务源码(WCF服务端)

    这些类分别对应于SOAP消息中的简单类型、复杂类型和数组类型。 6. **异常处理**: 在整个过程中,要捕获可能发生的异常,例如网络问题、解析错误等,并提供适当的错误处理机制。 7. **注意安全与性能**: 访问远程...

    利用JSON WebService实现Android访问远程数据库.pdf

    其文本格式独立于语言,可以在不同平台之间轻松传递数据,特别是在异步应用程序中,例如,Android客户端与服务器之间的数据交换。相较于传统的XML格式,JSON更加轻量,解析也更为高效。在移动设备资源受限的条件下,...

    android_service服务器

    `RemoteCallbackList`用于管理远程回调,让服务可以向多个客户端发送更新。 四、服务的生命周期管理 1. **服务的生命周期管理非常重要,不正确地管理可能导致内存泄漏或资源浪费**。当服务不再需要时,应用应通过`...

    android开启服务的两种方式

    服务主要用于处理那些不需要用户交互但需要持续运行的任务,比如下载大文件、播放音乐或与远程服务器通信。本篇文章将深入探讨两种启动Android服务的方式:startService和bindService,以及IntentService类的使用。 ...

    安卓AIDL相关-androidstudio下使用aidl接口传递自定义对象.rar

    在AIDL中,你可以定义基本数据类型,如int、String等,也可以定义自定义的复杂对象。但传递自定义对象时,需要注意以下几点: 1. 自定义对象必须实现Parcelable接口:在Android中,只有实现了Parcelable接口的对象...

    Android例子源码音乐播放器aidl实现进程间的通讯

    为了优化,可以考虑减少不必要的IPC调用,或者使用更高效的数据传递方式,比如使用Parcelable接口传递复杂对象。 通过深入研究这个源码,开发者可以学习到如何有效地在Android应用中实现进程间通信,这对于开发涉及...

Global site tag (gtag.js) - Google Analytics