Intent类相当于Android平台中应用程序之间的通信网络,属于后期绑定,这就意味着将Intent映射和传递给能够处理特定任务的组件是在运行时时行的,而不是在构建或者编译时。
Intent包含3个元素,action.category,data,以及一个额外的可选元素集合。动作和类别都是String,数据是以Uri对象的形式定义的。Uri是通用的URI,包括方案(scheme)、授权(authority)和可选的路径。
Extras 传递给Intent的额外数据,以Bundle的形式定义。
Component 指定Intent使用的显式包名和类名,可选,通常是从动作、类别和类型导出的。
Type 指定显式的MIME类型(与从Uri解析相对)
Category 有关Intent的其他元数据如:android.intent.category.LAUNCHER.
Data以URI 形式表示的数据
Action 指示动作完全限定String如:android.intent.action.MAIN
组件可能过下列两种方式之一调用Intent。
隐式Intent调用是指在调用中由平台决定调用那个组件最合适处理该Intent。这种调用是通过一个Intent的解析过程来实现的。该解析过程使用了动作、数据和类别。显式调用是指在调用中通过代码直接指定哪个组件应该处理该Intent。显式调用是通过指定接收器的Class或者ComponetName实现的(其中ComponetName是一个包的String和类的String)。
解析Intent
有3种类型的Android组件可以注册成为Intent句柄:Activity、BroadcastReceiver和Service。这些组件通常在AndroidManifest.xml文件中使用〈intent-filter>元素向平台注册。成为特定Intent类型的目标。
每一个
在解析了动作和类别之后,下面该解析Intent中的数据了。数据或是一个显式的MIME类型,或是方案、授权和路径的组合。无论是哪一种数据形式,都可以从一个Uri对象导出。定义IntentFilter类时会有一些类型、方案、授权和路径方面的条件,以决定IntentFilter将与那些Intent匹配。Android中的解析过程比较复杂,具体规则如下:
(1)如果有方案而没有类型,则包含任何类型的Intent都匹配。
(2)如是有类型没有方案,则包含任何方案的Intent都匹配。
(3)如果方案和类型都没有,则只有既不包含方案也不包含类型的Intent匹配。
(4)如果指定了授权,则也必须指定方案。
(5)如果指定了路径,则也必须指定方案和授权。
BOOT_COMPLETED表明开机动作执行完成以后调用某个Intent。
常用在Android应用程序中Intent动作和Uri组合说明。
动作 Uri 说明
Intent.ACTION_VIEW geo:latitude,longitude 打开地图应用程序并显示指定的纬度和经度
Intent.ACTION_VIEW geo:0,0?=street+address 打开地图程序并显示相应的地址。
Intent.ACTION_CALL tel:phone_number 打开电话应用程序并拨打指定的电话号码
Intent.ACTION_DIAL tel:phone_number 打开电话应用程序并拨下指定的号码(但不打出)
Intent.ACTION_DIAL voicemail: 打开电话应程序并拨下该语音信箱的电话号码(但不打出)
Intent.ACTION_VIEW http://web_address 打开浏览器应用程序并显示指定的URL
Intent.ACTION_VIEW https://web_address 打开浏览器应用程序并显示指定的URL
Intent.ACTION_WEB_SEARCH plain_text 打开浏览器应用程序并使用Google搜索引擎
如何广播事件
使用Context类提供的 众多方法之一,Intent还可以向所有已配置的接收器广播事件。要注册以接收Intent广播,你需要实现一个BroadcastReceiver。
创建你自己的Intent广播接收器时,你需要扩展Android提供的BroadcastReceiver类,并实现抽象的OnReceive(Context c,Inent i)方法。用Context.startService()启动。
在Android中,Service旨在实现两种目的:运行后台任务或者公开一个远程对象以实现进程间的通信。
实现进程间的通信
Android通过其特有的方法IPC,实现了在不同进程中运行的组件之间的通信。
Android提供了自己的IDL(Interface DefinitionLanguage)你可以创建IDL文件。这些IDL文件会成为aidl工具的输入。该工具用于生成一个java接口和内部Stub类,反过来,你可以使用该Java接口和内部Stub类创建可远程访问的对象。
AIDL文件有特定的语法,允许你定义带有返回类型和参数的方法(与典型Java接口不同的是你不能定义静态字段)。定义方法时,你必须为所有的非基本类型指定方向标记,每个参数的方向标记为以下三种情况之一:in,out或inout。基本类型的方向标记只允许是in.
这个AIDL是用来作RPC调用的,也就是你从一个进程里调用另外一个进程里的函数。android还提供了aidl工具来专门帮你生成aidl相关文件
通过代码来实现这个数据传输过程是冗长乏味的,Android提供了AIDL工具来处理这项工作。
AIDL IPC机制是面向接口的,像COM或Corba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。
- Calls made from the local process are executed in the same thread that is making the call. If this is your main UI thread, that thread continues to execute in the AIDL interface. If it is another thread, that is the one that executes your code in the service. Thus, if only local threads are accessing the service, you can completely control which threads are executing in it (but if that is the case, then you shouldn't be using AIDL at all, but should instead create the interface by implementing a Binder).
- Calls from a remote process are dispatched from a thread pool the platform maintains inside of your own process. You must be prepared for incoming calls from unknown threads, with multiple calls happening at the same time. In other words, an implementation of an AIDL interface must be completely thread-safe.
- The
oneway
keyword modifies the behavior of remote calls. When used, a remote call does not block; it simply sends the transaction data and immediately returns. The implementation of the interface eventually receives this as a regular call from theBinder
thread pool as a normal remote call. Ifoneway
is used with a local call, there is no impact and the call is still synchronous.
AIDL使用简单的语法来声明接口,描述其方法以及方法的参数和返回值。这些参数和返回值可以是任何类型,甚至是其他AIDL生成的接口。
需要特别注意的是, 对于非基本数据类型,也不是String和CharSequence类型的,需要有方向指示,包括in、out和inout,in表示由客户端设置,out表示由服务端设置,inout是两者均可设置。
import com.demo.Person;
interface IMyService {
void savePersonInfo(in Person person);
List<Person> getAllPerson();
}
private LinkedList<Person> personList = new LinkedList<Person>();
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final IMyService.Stub mBinder = new IMyService.Stub(){
@Override
public void savePersonInfo(Person person) throws RemoteException {
if (person != null){
personList.add(person);
}
}
@Override
public List<Person> getAllPerson() throws RemoteException {
return personList;
}
};
}
public static abstract class Stub extends android.os.Binder implements com.demo.IMyService
1) void writeToParcel(Parcel dest, int flags) 将需要序列化存储的数据写入外部提供的Parcel对象dest。而看了网上的代码例子,个人猜测,读取Parcel数据的次序要和这里的write次序一致,否则可能会读错数据。具体情况我没试验过!
2) describeContents() 没搞懂有什么用,反正直接返回0也可以
3) static final Parcelable.Creator对象CREATOR 这个CREATOR命名是固定的,而它对应的接口有两个方法:
createFromParcel(Parcel source) 实现从source创建出JavaBean实例的功能
newArray(int size) 创建一个类型为T,长度为size的数组,仅一句话(return new T[size])即可。估计本方法是供外部类反序列化本类数组使用。
private String name;
private String telNumber;
private int age;
public Person() {}
public Person(Parcel pl){
name = pl.readString();
telNumber = pl.readString();
age = pl.readInt();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTelNumber() {
return telNumber;
}
public void setTelNumber(String telNumber) {
this.telNumber = telNumber;
}
public int getAge() {
return age;
}
public void setAge( int age) {
this.age = age;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeString(telNumber);
dest.writeInt(age);
}
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];
}
};
}
然后创建Person.aidl文件,注意这里的parcelable和原来实现的Parcelable 接口,开头的字母p一个小写一个大写:
parcelable Person;
3. 抛出的异常是不能返回给调用者(跨进程抛异常处理是不可取的)。
3. 客户端获取接口
private ServiceConnection mRemoteConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mRemoteService = IMyService.Stub.asInterface(service);
}
public void onServiceDisconnected(ComponentName className) {
mRemoteService = null;
}
};
* Cast an IBinder object into an com.demo.IMyService interface,
* generating a proxy if needed.
*/
public static com.demo.IMyService asInterface(android.os.IBinder obj) {...}
unbindService(mRemoteConnection);
} else{
bindService( new Intent( "com.demo.IMyService"),
mRemoteConnection, Context.BIND_AUTO_CREATE);
}
mIsRemoteBound = !mIsRemoteBound;
new View.OnClickListener(){
private int index = 0;
@Override
public void onClick(View view) {
Person person = new Person();
index = index + 1;
person.setName( "Person" + index);
person.setAge(20);
person.setTelNumber( "123456");
try {
mRemoteService.savePersonInfo(person);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
listPersonButton.setOnClickListener(
new View.OnClickListener(){
@Override
public void onClick(View view) {
List<Person> list = null;
try {
list = mRemoteService.getAllPerson();
} catch (RemoteException e) {
e.printStackTrace();
}
if (list != null){
StringBuilder text = new StringBuilder();
for(Person person : list){
text.append( "\nPerson name:");
text.append(person.getName());
text.append( "\n age :");
text.append(person.getAge());
text.append( "\n tel number:");
text.append(person.getTelNumber());
}
inputPersonEdit.setText(text);
} else {
Toast.makeText(ServiceActivity. this, "get data error",
Toast.LENGTH_SHORT).show();
}
}
});
Permission权限
如果Service在AndroidManifest.xml中声明了全局的强制的访问权限,其他引用必须声明权限才能来start,stop或bind这个service.
另外,service可以通过权限来保护她的IPC方法调用,通过调用checkCallingPermission(String)方法来确保可以执行这个操作。
AndroidManifest.xml的Service元素
< intent-filter >
< action android:name ="com.demo.IMyService" />
</ intent-filter >
</ service >
The name of the process where the service is to run. Normally, all components of an application run in the default process created for the application. It has the same name as the application package. The <application> element's process attribute can set a different default for all components. But component can override the default with its own process attribute, allowing you to spread your application across multiple processes.