Chinaunix首页 | 论坛 | 博客
  • 博客访问: 14851
  • 博文数量: 5
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 90
  • 用 户 组: 普通用户
  • 注册时间: 2014-06-14 17:09
文章分类

全部博文(5)

文章存档

2014年(5)

我的朋友

分类: Android平台

2014-06-16 08:33:34

Android中的android.os.Parcelable接口用于替代Java序列化Serializable接口,Fragment以及Activtity之间都需要传递数据,有时甚至包含结构非常复杂的对象,这就需要先将这个对象序列化成二进制流,然后再进行传递了。


比如Fragment1向Fragment2传递数据,下面是Fragment1中创建Fragment2并传送数据的方法:

1
2
3
4
5
6
7
8
9
10
Fragment2 fragment = new Fragment2();
Bundle bundle = new Bundle();
bundle.putParcelable("name", name);
fragment2.setArguments(bundle);
     
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.container, fragment2)
            .addToBackStack(null)
            .commit();

在Fragment2中,直接得到这个Parcelable对象即可:

1
ParcelableName name = getArguments().getParcelable("name");

不过,既然Java已经有了Serializable,那还需要Parcelable干什么呢?而且Serializable接口使用起来也非常简洁。


原因有三个,第一是效率,第二是效率,第三还是效率:

  1. Serializable用了很多反射,细心的人都知道,反射比正常的调用要慢100多倍

  2. Serializable会创建很多临时对象,这些临时对象会导致很多次垃圾回收,影响效率


有细心的人士做过测试,基本上Parcelable要比Serializable快上10-20倍。下面这个图是比较结构

下面是android.os.Parcelable接口的定义,相比java.io.Serializable要复杂很多,不过,为了效率,你也只能忍了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface Parcelable {
    public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001;
    public static final int CONTENTS_FILE_DESCRIPTOR = 0x0001;
    public int describeContents();
    public void writeToParcel(Parcel dest, int flags);
      
    public interface Creator {
        public T createFromParcel(Parcel source);
        public T[] newArray(int size);
    }
      
    public interface ClassLoaderCreator extends Creator {
        public T createFromParcel(Parcel source, ClassLoader loader);
    }
}

看起来你至少需要实现两个方法describeContents()和writeToParcel():

  1. 第一个方法返回数字,一般返回0就好,只有FileDescriptor有特殊,上面的常量有定义。至于这有什么用,我也没有找到相关的信息,如果有读者理解,请留言告知我。

  2. 第二个方法用于将对象写入到Parcel对象中。详见下面的例子。


接着自己来实现一个包含了姓和名两个String字段的对象,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
    import android.os.Parcel;
    import android.os.Parcelable;
  
    public class ParcelableName implements Parcelable {
        private String mSurname;
        private String mGivenName;
      
        public ParcelableName(String surname, String givenName) {
            mSurname = surname;
            mGivenName = givenName;
        }
      
        // 私有方法,因为我们不应该将参数是Parcel的构造函数暴露出去
        private ParcelableName(Parcel source) {
            this(source.readString(), source.readString());
        }
      
        @Override
        public int describeContents() {
            return 0;
        }
      
        public String getSurname() {
            return mSurname;
        }
      
        public String getGivenName() {
            return mGivenName;
        }
      
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(mSurname);
            dest.writeString(mGivenName);
        }
     
        // 通过这个接口来创建Parcel对象,调用了私有的构造函数
        public static final Parcelable.Creator CREATOR 
            = new Creator() {
      
            @Override
            public ParcelableName createFromParcel(Parcel source) {
                return new ParcelableName(source);
            }
      
            @Override
            public ParcelableName[] newArray(int size) {
                return new ParcelableName[0];
            }
        };
    }

这里使用了Parcel.writeString()方法来将一个对象写入到序列化对象中,使用了Parcel.readString()从序列化对象中读取数据,一定要注意的是这里的写入和读取是有顺序的:先写的要先读。


注意,这里我们创建了一个私有的构造函数,这个构造函数的参数是Parcel对象,我们还创建了一个CREATOR的类变量,这个对象专门用于从序列化对象中创建ParcelableName对象,这是为了尽可能向外界隐藏序列化对象的实现细节,这种方式需要仔细琢磨,才能有所领悟。

值得提一下的是,Parcelable接口中还有一个ClassLoaderCreator接口,里面的createFromParcel()的第二个参数是一个ClassLoader对象,意味着我们可以反序列化不同的ClassLoader中的对象。

这种代码写起来的确是挺麻烦的,有一个开源项目通过Anotation+代码生成的方法可以简化定义Parcelable对象的过程:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    @Parcel
    public class Example {
        String mSurname;
        String mGivenName;
     
        public Example(){ }
     
        public Example(String surname, String givenName) {
            mSurname = surname;
            mGivenName = givenName;
        }
     
        public String getSurname() { return mSurname; }
        public String getGivenName() { return mGivenName; }
    }

看起来简单多了,不过话说回来,如果你需要序列化的对象比较小,而且次数不多,不影响效率,你还是可以继续使用Serializable接口的,毕竟编码和维护的代价都小得多。

阅读(896) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~