intent.putExtra的使用與原理分析

在android中用intent傳遞數據是非常常見的操作,我們一般會用intent.putExtra()這個方法來放入自己要傳遞的數據,然後再另一個地方使用getxxx()來獲取,其中intent.putExtra()的傳參類型有很多種:

Intent  putExtra(String name, String[] value)
Intent  putExtra(String name, Parcelable value)
Intent  putExtra(String name, long value)
Intent  putExtra(String name, boolean value)
Intent  putExtra(String name, double value)
Intent  putExtra(String name, Parcelable[] value)
Intent  putExtra(String name, char value)
Intent  putExtra(String name, int[] value)
Intent  putExtra(String name, int value)
Intent  putExtra(String name, double[] value)               
Intent  putExtra(String name, short value)                  
Intent  putExtra(String name, long[] value)                     
Intent  putExtra(String name, boolean[] value)                  
Intent  putExtra(String name, short[] value)                    
Intent  putExtra(String name, String value)                     
Intent  putExtra(String name, Serializable value)                   
Intent  putExtra(String name, float[] value)                    
Intent  putExtra(String name, Bundle value)                     
Intent  putExtra(String name, byte[] value)                     
Intent  putExtra(String name, CharSequence value)                   
Intent  putExtra(String name, char[] value)                     
Intent  putExtra(String name, byte value)                   
Intent  putExtras(Intent src)                   
Intent  putExtras(Bundle extras)

可以看到其可以傳遞的包括基本數據類型(含基本數據類型的數組)、String(包含數組)、Parcelable(包含數組)、Serializable、Bundle、CharSequence、Intent幾種類型的數據。我們點進去看下具體實現:

   public Intent putExtra(String name, String value) {
        if (mExtras == null) {
            mExtras = new Bundle();
        }
        mExtras.putString(name, value);
        return this;
    }
    public Intent putExtra(String name, int value) {
        if (mExtras == null) {
            mExtras = new Bundle();
        }
        mExtras.putInt(name, value);
        return this;
    }
    public Intent putExtra(String name, boolean value) {
        if (mExtras == null) {
            mExtras = new Bundle();
        }
        mExtras.putBoolean(name, value);
        return this;
    }

我們點開瞭三個Intent putExtra(String name, String value),Intent putExtra(String name, int value),Intent putExtra(String name, boolean value)發現其實原理都一樣,他們都通過new Bundle.putxxx()來實現的,也就是說傳進來的這些數據都是通過Bundle這個容器來裝然後傳遞,在Intent類裡面維護瞭一個Bundle對象mExtras,如果intent已經攜帶瞭Bundle對象,那麼直接向裡面存儲數據,否則就新建一個Bundle對象, 點開 mExtras.putxxx()方法我們會發現,

 public void putxxxx(@Nullable String key, value) {
        unparcel();
        mMap.put(key, value);
    }

其實在BaseBundle裡面維護瞭一個ArrayMap

ArrayMap mMap = null;

我們intent的put操作和get操作就是對Bundle裡面的ArrayMap進行mMap.put(key, value);和mMap.get(key);操作,那Bundle到底是個什麼東東呢?我們要實現intent傳遞對象改怎麼做呢?Bundle我們可以看作是一個存儲可傳輸的數據的容器,什麼是可傳輸的呢?我的理解是像基本數據類型本身就可以直接轉換為字節流,所以是可傳輸的,還有就是實現實現序列化接口(String本身就實現瞭Serializable接口),在android中實現序列化接口有兩種方式

實現Serializable接口

實現Parcelable接口
其中實現Serializable接口在javase就已經支持,而Parcelable是Android特有的功能,效率比實現Serializable接口高。那他們有什麼區別?

作用

Serializable的作用是為瞭保存對象的屬性到本地文件、數據庫、網絡流、rmi以方便數據傳輸,當然這種傳輸可以是程序內的也可以是兩個程序間的。而Android的Parcelable的設計初衷是因為Serializable效率過慢,為瞭在程序內不同組件間以及不同Android程序間(AIDL)高效的傳輸數據而設計,這些數據僅在內存中存在,Parcelable是通過IBinder通信的消息的載體。
從上面的設計上我們就可以看出優劣瞭。

效率及選擇

Parcelable的性能比Serializable好,在內存開銷方面較小,所以在內存間數據傳輸時推薦使用Parcelable,如activity間傳輸數據,而Serializable可將數據持久化方便保存,所以在需要保存或網絡傳輸數據時選擇Serializable,因為android不同版本Parcelable可能不同,所以不推薦使用Parcelable進行數據持久化。

總結:所以在傳遞對象時對於需要傳遞的對象的序列化選擇可以加以區分,需要數據持久化的建議實現Serializable接口,隻在內存間數據傳輸時推薦使用Parcelable。

編程實現
-實現Serializable接口
對於對於Serializable,類隻需要實現Serializable接口,並提供一個序列化版本id(serialVersionUID)即可。,有人又會問瞭id(serialVersionUID)幹啥的?
(Java的序列化機制是通過在運行時判斷類的serialVersionUID來驗證版本一致性的。在進行反序列化時,JVM會把傳來的字節流中的serialVersionUID與本地相應實體(類)的serialVersionUID進行比較,如果相同就認為是一致的,可以進行反序列化,否則就會出現序列化版本不一致的異常。添加serialVersionUID使得在序列化時保持版本的兼容性,即在版本升級時反序列化仍保持對象的唯一性。)
下面一個bean是實現Serializable接口的一個樣例:

/**
 * =============================================================================
 * Copyright (c) 2016 yuxin All rights reserved.
 * Packname com.jju.yuxin.disanzhou
 * Created by yuxin.
 * Created time 2016/9/18 0018 下午 10:29.
 * Version   1.0;
 * Describe :
 * History:
 * ==============================================================================
 */
public class Person implements Serializable{

    private static final long serialVersionUID = 1L;

    private int id;
    private String name;

    public Person() {
    }

    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    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;
    }
}

intent數據的傳遞:

 // 封裝數據
        Person p = new Person();
        p.setId(320840);
        p.setName("小夥子");
        Intent i = new Intent(MainActivity.this, FirstActivity.class);
        i.putExtra("Person", p);
        startActivity(i);    

接收數據:

Person p = (Person)getIntent().getSerializableExtra("Person");       
       System.out.println("身份證"+p.getId());
       System.out.println("姓名"+p.getName()); 

-實現Parcelable接口
實現Parcelable接口稍微復雜一些,但效率更高,下面是一個實現Parcelable接口的一個樣例:

 CREATOR = new Parcelable.Creator() {
        @Override
        public Student createFromParcel(Parcel source) {
            return new Student(source);
        }

        @Override
        public Student[] newArray(int size) {
            return new Student[size];
        }
    };
}
" data-snippet-id="ext.e80e81f253c4e7588321284c35d3907f" data-snippet-saved="false" data-codota-status="done">/**
 * =============================================================================
 * Copyright (c) 2016 yuxin All rights reserved.
 * Packname com.jju.yuxin.disanzhou
 * Created by yuxin.
 * Created time 2016/9/18 0018 下午 9:47.
 * Version   1.0;
 * Describe :
 * History:
 * ==============================================================================
 */
public class Student implements Parcelable {

    private int id;
    private String name;

    public Student() {
    }

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }

    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() {
        return 0;
    }

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

    protected Student(Parcel in) {
        this.id = in.readInt();
        this.name = in.readString();
    }

    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
        @Override
        public Student createFromParcel(Parcel source) {
            return new Student(source);
        }

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

intent數據的傳遞:

 // 封裝數據
        Student s = new Student();
        s.setId(320840);
        s.setName("小夥子");
        Intent i = new Intent(MainActivity.this, FirstActivity.class);
        i.putExtra("Student", s);
        startActivity(i); 

數據的接收:

Student s = (Student)getIntent().getParcelableExtra("Student");       
       System.out.println("學號:"+s.getId());
       System.out.println("姓名:"+s.getName()); 

我們發現在實現Parcelable接口比較復雜在於要實現的幾個方法不清楚是什麼意思,我們來看下:

 CREATOR = new Parcelable.Creator() {
        @Override
        public Student createFromParcel(Parcel source) {
            return new Student(source);
        }

        @Override
        public Student[] newArray(int size) {
            return new Student[size];
        }
    };
}" data-snippet-id="ext.256f48df6af3727b54ec6212076d06e2" data-snippet-saved="false" data-codota-status="done">    @Override
    public int describeContents() {
        return 0;
    }

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

    protected Student(Parcel in) {
        this.id = in.readInt();
        this.name = in.readString();
    }

    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
        @Override
        public Student createFromParcel(Parcel source) {
            return new Student(source);
        }

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

我們可以看到在方法中有很多個Parcel對象。這個類是用來封裝數據的容器,封裝後的數據可以通過Intent或IPC傳遞,除瞭基本類型外,隻有實現瞭Parcelable接口的類才能放入parcel中,也就是說實現瞭Parcelable接口後可以把數據打包成Parcel對象對象來傳遞,並且隻有實現瞭Parcelable接口的類才能放入parcel中,那實現瞭Parcelable接口的對象是如何打包和讀取的呢?我們會發現兩個方法

public void writeToParcel(Parcel dest, int flags);
這個方法就是傳入一個空的Parcel對象,然後將我們要存儲的對象的屬性寫進Parcel中去(註意寫入順序,因為在讀取是時要按照寫入順序來讀取)

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

還有一個就是:
- public Student createFromParcel(Parcel source)
從方法名我們就可以看出他是從Parcel對象中獲取值來創建一個我們需要傳遞的的Student 對象

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

它這調用瞭Student的構造方法

  protected Student(Parcel in) {
        this.id = in.readInt();
        this.name = in.readString();
    }

也就是說對於對象傳遞時需要打包成Parcel對象傳遞,而打包和解包的過程都是我們自己來寫的。

題外話:對於實現Parcelable接口實現方法十分容易寫錯,如果你用android studio的話,安裝個*Android
Parcelable code generator*插件在你要實現Parcelable接口的bean上按住
alt+insert選擇Parcelable,就可以快速生成Parcelable代碼

總結上面所有intent的數據傳遞的重寫的方法,我們會發現都是將數據放入Bundle中然後傳遞的,那Bundle究竟是什麼呢?

You May Also Like