[Android知识体系]之四大组件:activity(序列化和intent传递对象)

来源:互联网 发布:爱国 知乎 编辑:程序博客网 时间:2024/06/06 12:45

其实activity是最基本要掌握的,但是如果要深究,其实非常多好讲的。

这篇文章是先学习序列化,再对android使用序列化传递对象进行介绍。

第一小节 Serializabale

一、序列化和反序列化的概念

  把对象转换为字节序列的过程称为对象的序列化
  把字节序列恢复为对象的过程称为对象的反序列化
  对象的序列化主要有两种用途:
  1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
  2) 在网络上传送对象的字节序列。

  在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。

  当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。

二、JDK类库中的序列化API

  java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
  
  java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
  
  只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。
  
  对象序列化包括如下步骤:
  1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
  2) 通过对象输出流的writeObject()方法写对象。

  对象反序列化的步骤如下:
  1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
  2) 通过对象输入流的readObject()方法读取对象。

对象序列化和反序列范例

  定义一个Person类,实现Serializable接口
  

 import java.io.Serializable; /**   * <p>ClassName: Person<p>   * <p>Description:测试对象序列化和反序列化<p>   * @author xudp   * @version 1.0 V   * @createTime 2014-6-9 下午02:33:25   */ public class Person implements Serializable {     /**      * 序列化ID      */     private static final long serialVersionUID = -5809782578272943999L;     private int age;     private String name;     private String sex;     public int getAge() {         return age;     }     public String getName() {         return name;     }     public String getSex() {         return sex;     }     public void setAge(int age) {         this.age = age;     }     public void setName(String name) {         this.name = name;     }     public void setSex(String sex) {         this.sex = sex;     } }

序列化和反序列化Person类对象

  import java.io.File;  import java.io.FileInputStream;  import java.io.FileNotFoundException;  import java.io.FileOutputStream;  import java.io.IOException;  import java.io.ObjectInputStream;  import java.io.ObjectOutputStream;  import java.text.MessageFormat; /**  * <p>ClassName: TestObjSerializeAndDeserialize<p>  * <p>Description: 测试对象的序列化和反序列<p>  * @author xudp  * @version 1.0 V  * @createTime 2014-6-9 下午03:17:25  */ public class TestObjSerializeAndDeserialize {     public static void main(String[] args) throws Exception {         SerializePerson();//序列化Person对象         Person p = DeserializePerson();//反序列Perons对象         System.out.println(MessageFormat.format("name={0},age={1},sex={2}",                                                p.getName(), p.getAge(), p.getSex()));     }     /**      * MethodName: SerializePerson       * Description: 序列化Person对象      * @author xudp      * @throws FileNotFoundException      * @throws IOException      */     private static void SerializePerson() throws FileNotFoundException,             IOException {         Person person = new Person();         person.setName("gacl");         person.setAge(25);         person.setSex("男");         // ObjectOutputStream 对象输出流,将Person对象存储到E盘的Person.txt文件中,完成对Person对象的序列化操作         ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(                 new File("E:/Person.txt")));         oo.writeObject(person);         System.out.println("Person对象序列化成功!");         oo.close();     }     /**      * MethodName: DeserializePerson       * Description: 反序列Perons对象      * @author xudp      * @return      * @throws Exception      * @throws IOException      */     private static Person DeserializePerson() throws Exception, IOException {         ObjectInputStream ois = new ObjectInputStream(new FileInputStream(                 new File("E:/Person.txt")));         Person person = (Person) ois.readObject();         System.out.println("Person对象反序列化成功!");         return person;     } }

代码运行结果如下:
代码运行结果1
代码运行结果2

序列化Person成功后在E盘生成了一个Person.txt文件,而反序列化Person是读取E盘的Person.txt后生成了一个Person对象

序列化Person成功后在E盘生成了一个Person.txt文件,而反序列化Person是读取E盘的Person.txt后生成了一个Person对象

三、serialVersionUID的作用

  s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D​:​ ​字​面​意​思​上​是​序​列​化​的​版​本​号​,凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量

1 private static final long serialVersionUID

  实现Serializable接口的类如果类中没有添加serialVersionUID,那么就会出现如下的警告提示
  警告提示
用鼠标点击标号1就会弹出生成serialVersionUID的对话框,如下图所示:

弹出uid提示对话框

serialVersionUID有两种生成方式:

  采用标号2这种方式生成的serialVersionUID是1L,例如:
  

private static final long serialVersionUID = 1L;

采用标号3这种方式生成的serialVersionUID是根据类名,接口名,方法和属性等来生成的,例如:

 private static final long serialVersionUID = 4603642343377807741L;

添加了之后就不会出现那个警告提示了,如下所示:

添加uid不会出现警告提示

扯了那么多,那么serialVersionUID(序列化版本号)到底有什么用呢,我们用如下的例子来说明一下serialVersionUID的作用,看下面的代码:

 import java.io.File;  import java.io.FileInputStream;  import java.io.FileNotFoundException;  import java.io.FileOutputStream;  import java.io.IOException;  import java.io.ObjectInputStream;  import java.io.ObjectOutputStream;  import java.io.Serializable; public class TestSerialversionUID {     public static void main(String[] args) throws Exception {         SerializeCustomer();// 序列化Customer对象         Customer customer = DeserializeCustomer();// 反序列Customer对象         System.out.println(customer);     }     /**      * MethodName: SerializeCustomer       * Description: 序列化Customer对象      * @author xudp      * @throws FileNotFoundException      * @throws IOException      */     private static void SerializeCustomer() throws FileNotFoundException,             IOException {         Customer customer = new Customer("gacl",25);         // ObjectOutputStream 对象输出流         ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(                 new File("E:/Customer.txt")));         oo.writeObject(customer);         System.out.println("Customer对象序列化成功!");         oo.close();     }     /**      * MethodName: DeserializeCustomer       * Description: 反序列Customer对象      * @author xudp      * @return      * @throws Exception      * @throws IOException      */     private static Customer DeserializeCustomer() throws Exception, IOException {         ObjectInputStream ois = new ObjectInputStream(new FileInputStream(                 new File("E:/Customer.txt")));         Customer customer = (Customer) ois.readObject();         System.out.println("Customer对象反序列化成功!");         return customer;     } } /**  * <p>ClassName: Customer<p>  * <p>Description: Customer实现了Serializable接口,可以被序列化<p>  * @author xudp  * @version 1.0 V  * @createTime 2014-6-9 下午04:20:17  */ class Customer implements Serializable {     //Customer类中没有定义serialVersionUID     private String name;     private int age;     public Customer(String name, int age) {         this.name = name;         this.age = age;     }     /*      * @MethodName toString      * @Description 重写Object类的toString()方法      * @author xudp      * @return string      * @see java.lang.Object#toString()      */     @Override     public String toString() {         return "name=" + name + ", age=" + age;     } }

运行结果:
运行结果啊哈
运行结果啊哈2
序列化和反序列化都成功了。

下面我们修改一下Customer类,添加多一个sex属性,如下:

 class Customer implements Serializable {     //Customer类中没有定义serialVersionUID      private String name;      private int age;      //新添加的sex属性      private String sex;      public Customer(String name, int age) {         this.name = name;         this.age = age;     }     public Customer(String name, int age,String sex) {         this.name = name;         this.age = age;         this.sex = sex;     }     /*      * @MethodName toString      * @Description 重写Object类的toString()方法      * @author xudp      * @return string      * @see java.lang.Object#toString()      */     @Override     public String toString() {         return "name=" + name + ", age=" + age;     } }

然后执行反序列操作,此时就会抛出如下的异常信息:

 Exception in thread "main" java.io.InvalidClassException: Customer;  local class incompatible:  stream classdesc serialVersionUID = -88175599799432325,  local class serialVersionUID = -5182532647273106745

意思就是说,文件流中的class和classpath中的class,也就是修改过后的class,不兼容了,处于安全机制考虑,程序抛出了错误,并且拒绝载入。那么如果我们真的有需求要在序列化后添加一个字段或者方法呢?应该怎么办?那就是自己去指定serialVersionUID。在TestSerialversionUID例子中,没有指定Customer类的serialVersionUID的,那么java编译器会自动给这个class进行一个摘要算法,类似于指纹算法,只要这个文件 多一个空格,得到的UID就会截然不同的,可以保证在这么多类中,这个编号是唯一的。所以,添加了一个字段后,由于没有显指定 serialVersionUID,编译器又为我们生成了一个UID,当然和前面保存在文件中的那个不会一样了,于是就出现了2个序列化版本号不一致的错误。因此,只要我们自己指定了serialVersionUID,就可以在序列化后,去添加一个字段,或者方法,而不会影响到后期的还原,还原后的对象照样可以使用,而且还多了方法或者属性可以用。

  下面继续修改Customer类,给Customer指定一个serialVersionUID,修改后的代码如下:

  class Customer implements Serializable {      /**       * Customer类中定义的serialVersionUID(序列化版本号)       */      private static final long serialVersionUID = -5182532647273106745L;      private String name;      private int age;      //新添加的sex属性     //private String sex;     public Customer(String name, int age) {         this.name = name;         this.age = age;     }     /*public Customer(String name, int age,String sex) {         this.name = name;         this.age = age;         this.sex = sex;     }*/     /*      * @MethodName toString      * @Description 重写Object类的toString()方法      * @author xudp      * @return string      * @see java.lang.Object#toString()      */     @Override     public String toString() {         return "name=" + name + ", age=" + age;     } }

重新执行序列化操作,将Customer对象序列化到本地硬盘的Customer.txt文件存储,然后修改Customer类,添加sex属性,修改后的Customer类代码如下:

  class Customer implements Serializable {      /**       * Customer类中定义的serialVersionUID(序列化版本号)       */      private static final long serialVersionUID = -5182532647273106745L;      private String name;      private int age;      //新添加的sex属性     private String sex;     public Customer(String name, int age) {         this.name = name;         this.age = age;     }     public Customer(String name, int age,String sex) {         this.name = name;         this.age = age;         this.sex = sex;     }     /*      * @MethodName toString      * @Description 重写Object类的toString()方法      * @author xudp      * @return string      * @see java.lang.Object#toString()      */     @Override     public String toString() {         return "name=" + name + ", age=" + age;     } }

执行反序列操作,这次就可以反序列成功了,如下所示:
反序列成功

四、serialVersionUID的取值

  serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。
  
  类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的 serialVersionUID,也有可能相同。为了提高serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值

  显式地定义serialVersionUID有两种用途:
    1、 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;
    2、 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。
    

第二小节 Parcelable

一.什么是Parcelable ?

Parcelable,定义了将数据写入Parcel,和从Parcel中读出的接口。一个实体(用类来表示),如果需要封装到消息中去,就必须实现这一接口,实现了这一接口,该实体就成为“可打包的”了。

这个时候就不得不提到android的parcel机制了

1.Android中的新的序列化机制

在Android系统中,定位为针对内存受限的设备,因此对性能要求更高,另外系统中采用了新的IPC(进程间通信)机制,必然要求使用性能更出色的对象传输方式。在这样的环境下,Parcel被设计出来,其定位就是轻量级的高效的对象序列化和反序列化机制。

2.parcel类的背后

在Framework中有parcel类,源码路径是:
Frameworks/base/core/java/android/os/Parcel.java
典型的源码片断如下:

/**  * Write an integer value into the parcel at the current dataPosition(),  * growing dataCapacity() if needed.  */  public final native void writeInt(int val);  /**  * Write a long integer value into the parcel at the current dataPosition(),  * growing dataCapacity() if needed.  */  public final native void writeLong(long val);  

从中我们看到,从这个源程序文件中我们看不到真正的功能是如何实现的,必须透过JNI往下走了。于是,Frameworks/base/core/jni/android_util_Binder.cpp中找到了线索

static void android_os_Parcel_writeInt(JNIEnv* env, jobject clazz, jint val)  {      Parcel* parcel = parcelForJavaObject(env, clazz);      if (parcel != NULL) {          const status_t err = parcel->writeInt32(val);          if (err != NO_ERROR) {              jniThrowException(env, "java/lang/OutOfMemoryError", NULL);          }      }  }  static void android_os_Parcel_writeLong(JNIEnv* env, jobject clazz, jlong val)  {      Parcel* parcel = parcelForJavaObject(env, clazz);      if (parcel != NULL) {          const status_t err = parcel->writeInt64(val);          if (err != NO_ERROR) {              jniThrowException(env, "java/lang/OutOfMemoryError", NULL);          }      }  }  

从这里可以得到的信息时寒暑的视线依赖于parcel指针,因此还需要找到parcel的类定义,不过这里的类 已经是用c++语言实现的了。(不再往下深究)。

其实,parcel背后的动作就是全在一块内存里进行读写操作。本质上把它当成一个serialize就可以了,只是它是在内存中完成的序列化和反序列化,利用的是连续的内存空间,因此会更加高效

二.Parcelable 传递对象

Android序列化对象主要有两种方法:

1.实现Serializable接口,实现Serializable接口是JavaSE本身就支持的;

2.实现Parcelable接口,Parcelable是Android特有的功能,效率比实现Serializable接口高,像用于Intent数据传递也都支持,而且还可以用在进程间通信(IPC),除了基本类型外,只有实现了Parcelable接口的类才能被放入Parcel中。

三.Parcelable接口定义

public interface Parcelable {    //内容描述接口,基本不用管    public int describeContents();    //写入接口函数,打包    public void writeToParcel(Parcel dest, int flags);     //读取接口,目的是要从Parcel中构造一个实现了Parcelable的类的实例处理。因为实现类在这里还是不可知的,所以需要用到模板的方式,继承类名通过模板参数传入。    //为了能够实现模板参数的传入,这里定义Creator嵌入接口,内含两个接口函数分别返回单个和多个继承类实例。    public interface Creator<T> {           public T createFromParcel(Parcel source);           public T[] newArray(int size);       }

四.怎么实现Parcelable接口?

从parcelable接口定义中,我们可以看到,实现parcelable接口,需要我们实现下面几个方法:

1.describeContents方法。内容接口描述,默认返回0就可以;

2.writeToParcel 方法。该方法将类的数据写入外部提供的Parcel中.即打包需要传递的数据到Parcel容器保存,以便从parcel容器获取数据,该方法声明如下:

writeToParcel (Parcel dest, int flags) 具体参数含义见javadoc

3.静态的Parcelable.Creator接口,本接口有两个方法:

createFromParcel(Parcel in) 从Parcel容器中读取传递数据值,封装成Parcelable对象返回逻辑层。

newArray(int size) 创建一个类型为T,长度为size的数组,仅一句话(return new T[size])即可。方法是供外部类反序列化本类数组使用。

五.代码实现

1.封装数据,把实现parcelable接口的Person对象传递到TwoActivity里;

public class DemoActivity extends Activity {    /** Called when the activity is first created. */    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        // 封装数据        Person p = new Person();        p.setId(1);        p.setName("xiaoming");        // 用Intent传递Person对象        Intent i = new Intent(this, TwoActivity.class);        i.putExtra("Person", p);        startActivity(i);    }}

2.TwoActivity获取数据,从DemoActivity传递的Person对象给解析,并打印;

public class TwoActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        // TODO Auto-generated method stub        super.onCreate(savedInstanceState);        Person p = (Person)getIntent().getParcelableExtra("Person");        System.out.println("p_id"+p.getId());        System.out.println("p_name"+p.getName());    }}

3.parcelable接口的实现

 public class Person implements Parcelable{      // 成员变量      private int id;      private String name;      // 1.必须实现Parcelable.Creator接口,否则在获取Person数据的时候,会报错,如下:      // android.os.BadParcelableException:      // Parcelable protocol requires a Parcelable.Creator object called  CREATOR on class com.um.demo.Person     // 2.这个接口实现了从Percel容器读取Person数据,并返回Person对象给逻辑层使用     // 3.实现Parcelable.Creator接口对象名必须为CREATOR,不如同样会报错上面所提到的错;     // 4.在读取Parcel容器里的数据时,必须按成员变量声明的顺序读取数据,不然会出现获取数据出错     // 5.反序列化对象     public static final Parcelable.Creator<Person> CREATOR = new Creator(){         @Override         public Person createFromParcel(Parcel source) {             // TODO Auto-generated method stub             // 必须按成员变量声明的顺序读取数据,不然会出现获取数据出错             Person p = new Person();             p.setId(source.readInt());             p.setName(source.readString());             return p;         }         @Override         public Person[] newArray(int size) {             // TODO Auto-generated method stub             return new Person[size];         }     };     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) {         // TODO Auto-generated method stub         // 1.必须按成员变量声明的顺序封装数据,不然会出现获取数据出错         // 2.序列化对象         dest.writeInt(id);         dest.writeString(name);     } }

好了,parcelable接口的实现,到此结束。

第三小节 Serializabale和parcelable区别

一.作用

Serializable的作用是为了保存对象的属性到本地文件、数据库、网络流、rmi以方便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。而Android的Parcelable的设计初衷是因为Serializable效率过慢,为了在程序内不同组件间以及不同Android程序间(AIDL)高效的传输数据而设计,这些数据仅在内存中存在,Parcelable是通过IBinder通信的消息的载体。

从上面的设计上我们就可以看出优劣了。

二. 效率及选择

Parcelable的性能比Serializable好,在内存开销方面较小,所以在内存间数据传输时推荐使用Parcelable,如activity间传输数据,而Serializable可将数据持久化方便保存,所以在需要保存或网络传输数据时选择Serializable,因为android不同版本Parcelable可能不同,所以不推荐使用Parcelable进行数据持久化

三.高级功能上

Serializable序列化不保存静态变量,可以使用Transient关键字对部分字段不进行序列化,也可以覆盖writeObject、readObject方法以实现序列化过程自定义

四.选择上

serializable是java中的序列化接口,其使用起来简单但是开销很大,序列化和反序列化都需要大量io操作。而parcelable是android中的序列化方式,因此更适合用在android平台上,它的缺点就是使用起来稍微麻烦,但是它效率很高,这是android推荐的序列化方式,因此我们要首选parcelable。parcelable主要用在内存序列化上,通过parcelable将对象序列化到存储设备中或者将对象序列化后通过网络传输也都是可以的,不过这个过程会稍显复杂,因此在这两种情况下还是建议使用serializable。而使用内存时(如:序列化在进程间传递对象)更推荐使用parcelable接口。

第四小节 intent使用

不过结合intent的序列化对象使用在前面几小节已经有结合讲了。所以这里讲解intent的其他知识点(对,没错我就是懒得再开一篇文章讲)。

来看一下几个常见的操作:
启动一个Activity:Context.startActivity(Intent intent);
启动一个Service:Context.startService(Intent service);
绑定一个Service:Context.bindService(Intent service, ServiceConnection conn, int flags);
发送一个Broadcast:Context.sendBroadcast(Intent intent);

我们发现,在这些操作中,都有一个Intent参与其中,看起来像是一个非常重要的组件,那么Intent到底是什么呢?

简单来说,Intent是系统各组件之间进行数据传递的数据负载者。当我们需要做一个调用动作,我们就可以通过Intent告诉Android系统来完成这个过程,Intent就是调用通知的一种操作。

Intent有几个重要的属性,下面我们将会逐一介绍:

1.action,要执行的动作

对于有如下声明的Activity:

<activity android:name=".TargetActivity">      <intent-filter>          <action android:name="com.scott.intent.action.TARGET"/>          <category android:name="android.intent.category.DEFAULT"/>      </intent-filter>  </activity>  

TargetActivity在其<intent-filter>中声明了<action>,即目标action,如果我们需要做一个跳转的动作,就需要在Intent中指定目标的action,如下:

public void gotoTargetActivity(View view) {      Intent intent = new Intent("com.scott.intent.action.TARGET");      startActivity(intent);  }  

当我们为Intent指定相应的action,然后调用startActivity方法后,系统会根据action跳转到对应的Activity。
除了自定义的action之外,Intent也内含了很多默认的action,随便列举几个:

public static final String ACTION_MAIN = "android.intent.action.MAIN";  public static final String ACTION_VIEW = "android.intent.action.VIEW";  public static final String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH";  public static final String ACTION_CALL = "android.intent.action.CALL";  

每一个action都有其特定的用途,下文也会使用到它们。

2.data和extras,即执行动作要操作的数据和传递到目标的附加信息

下面就举一个与浏览器交互的例子:

/**  * 打开指定网页  * @param view  */  public void invokeWebBrowser(View view) {      Intent intent = new Intent(Intent.ACTION_VIEW);      intent.setData(Uri.parse("http://www.google.com.hk"));      startActivity(intent);  }  /**  * 进行关键字搜索  * @param view  */  public void invokeWebSearch(View view) {      Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);      intent.putExtra(SearchManager.QUERY, "android");    //关键字      startActivity(intent);  }  

上面两个方法分别是启动浏览器并打开指定网页、进行关键字搜索,分别对应的action是Intent.ACTION_VIEW和Intent.ACTION_WEB_SEARCH,前者需指定相应的网页地址,后者需指定关键字信息,对于关键字搜索来说,浏览器会按照自己设置的默认的搜索引擎进行搜索。

我们注意到,在打开网页时,为Intent指定一个data属性,这其实是指定要操作的数据,是一个URI的形式,我们可以将一个指定前缀的字符串转换成特定的URI类型,如:“http:”或“https:”表示网络地址类型,“tel:”表示电话号码类型,“mailto:”表示邮件地址类型,等等。例如,我们要呼叫给定的号码,可以这样做:

public void call(View view) {      Intent intent = new Intent(Intent.ACTION_CALL);      intent.setData(Uri.parse("tel:12345678"));      startActivity(intent);  }  

那么我们如何知道目标是否接受这种前缀呢?这就需要看一下目标中<data/>元素的匹配规则了。

在目标<data/>标签中包含了以下几种子元素,他们定义了url的匹配规则:

android:scheme 匹配url中的前缀,除了“http”、“https”、“tel”…之外,我们可以定义自己的前缀。
android:host 匹配url中的主机名部分,如“google.com”,如果定义为“*”则表示任意主机名。
android:port 匹配url中的端口。
android:path 匹配url中的路径。

我们改动一下TargetActivity的声明信息:

<activity android:name=".TargetActivity">      <intent-filter>          <action android:name="com.scott.intent.action.TARGET"/>          <category android:name="android.intent.category.DEFAULT"/>          <data android:scheme="scott" android:host="com.scott.intent.data" android:port="7788" android:path="/target"/>      </intent-filter>  </activity> 

这个时候如果只指定action就不够了,我们需要为其设置data值,如下:

public void gotoTargetActivity(View view) {      Intent intent = new Intent("com.scott.intent.action.TARGET");      intent.setData(Uri.parse("scott://com.scott.intent.data:7788/target"));      startActivity(intent);  }  

此时,url中的每个部分和TargetActivity配置信息中全部一致才能跳转成功,否则就被系统拒绝。

不过有时候对path限定死了也不太好,比如我们有这样的url:(scott://com.scott.intent.data:7788/target/hello)(scott://com.scott.intent.data:7788/target/hi)
这个时候该怎么办呢?我们需要使用另外一个元素:android:pathPrefix,表示路径前缀。
我们把android:path=”/target”修改为android:pathPrefix=”/target”,然后就可以满足以上的要求了。

而在进行搜索时,我们使用了一个putExtra方法,将关键字做为参数放置在Intent中,我们成为extras(附加信息),这里面涉及到了一个Bundle对象。

Bundle和Intent有着密不可分的关系,主要负责为Intent保存附加参数信息,它实现了android.os.Paracelable接口,内部维护一个Map类型的属性,用于以键值对的形式存放附加参数信息。在我们使用Intent的putExtra方法放置附加信息时,该方法会检查默认的Bundle实例为不为空,如果为空,则新创建一个Bundle实例,然后将具体的参数信息放置到Bundle实例中。我们也可以自己创建Bundle对象,然后为Intent指定这个Bundle即可,如下:

public void gotoTargetActivity(View view) {      Intent intent = new Intent("com.scott.intent.action.TARGET");      Bundle bundle = new Bundle();      bundle.putInt("id", 0);      bundle.putString("name", "scott");      intent.putExtras(bundle);      startActivity(intent);  }  

需要注意的是,在使用putExtras方法设置Bundle对象之后,系统进行的不是引用操作,而是复制操作,所以如果设置完之后再更改bundle实例中的数据,将不会影响Intent内部的附加信息。那我们如何获取设置在Intent中的附加信息呢?与之对应的是,我们要从Intent中获取到Bundle实例,然后再从中取出对应的键值信息:

Bundle bundle = intent.getExtras();  int id = bundle.getInt("id");  String name = bundle.getString("name");  

当然我们也可以使用Intent的getIntExtra和getStringExtra方法获取,其数据源都是Intent中的Bundle类型的实例对象。
前面我们涉及到了Intent的三个属性:action、data和extras。除此之外,Intent还包括以下属性:

3.category,要执行动作的目标所具有的特质或行为归类

例如:在我们的应用主界面Activity通常有如下配置:

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

代表该目标Activity是该应用所在task中的初始Activity并且出现在系统launcher的应用列表中。

几个常见的category如下:
Intent.CATEGORY_DEFAULT(android.intent.category.DEFAULT) 默认的category

Intent.CATEGORY_PREFERENCE(android.intent.category.PREFERENCE) 表示该目标Activity是一个首选项界面;

Intent.CATEGORY_BROWSABLE(android.intent.category.BROWSABLE)指定了此category后,在网页上点击图片或链接时,系统会考虑将此目标Activity列入可选列表,供用户选择以打开图片或链接。

在为Intent设置category时,应使用addCategory(String category)方法向Intent中添加指定的类别信息,来匹配声明了此类别的目标Activity。

4.type:要执行动作的目标Activity所能处理的MIME数据类型

例如:一个可以处理图片的目标Activity在其声明中包含这样的mimeType:

<data android:mimeType="image/*" /> 

在使用Intent进行匹配时,我们可以使用setType(String type)或者setDataAndType(Uri data, String type)来设置mimeType。

5.component,目标组件的包或类名称

在使用component进行匹配时,一般采用以下几种形式:

intent.setComponent(new ComponentName(getApplicationContext(), TargetActivity.class));  intent.setComponent(new ComponentName(getApplicationContext(), "com.scott.intent.TargetActivity"));  intent.setComponent(new ComponentName("com.scott.other", "com.scott.other.TargetActivity"));  

其中,前两种是用于匹配同一包内的目标,第三种是用于匹配其他包内的目标。需要注意的是,如果我们在Intent中指定了component属性,系统将不会再对action、data/type、category进行匹配。

今天的activity的intent序列化部分就写到这里吧,我已经疲惫了…….

0 0
原创粉丝点击