Android Intent之復(fù)雜數(shù)據(jù)的傳遞

2023-03-31 13:46 更新

本節(jié)引言:

上一節(jié)中我們學(xué)習(xí)了Intent的一些基本使用,知道了Intent的七個(gè)屬性,顯式Intent以及 隱式Intent,以及如何自定義隱式Intent,最后還給大家提供了一些常用的系統(tǒng)Intent! 而本節(jié)跟大家講解的是Intent傳遞數(shù)據(jù)的問(wèn)題~好的,開(kāi)始本節(jié)內(nèi)容~


1.Intent傳遞簡(jiǎn)單數(shù)據(jù)

還記得我們?cè)贏ctivity那里學(xué)過(guò)如何在兩個(gè)Activity中互相傳遞簡(jiǎn)單數(shù)據(jù)的方法嗎?

就是可以直接通過(guò)調(diào)用Intent的putExtra()方法存入數(shù)據(jù),然后在獲得Intent后調(diào)用getXxxExtra獲得 對(duì)應(yīng)類(lèi)型的數(shù)據(jù);傳遞多個(gè)的話,可以使用Bundle對(duì)象作為容器,通過(guò)調(diào)用Bundle的putXxx先將數(shù)據(jù) 存儲(chǔ)到Bundle中,然后調(diào)用Intent的putExtras()方法將Bundle存入Intent中,然后獲得Intent以后, 調(diào)用getExtras()獲得Bundle容器,然后調(diào)用其getXXX獲取對(duì)應(yīng)的數(shù)據(jù)! 另外數(shù)據(jù)存儲(chǔ)有點(diǎn)類(lèi)似于Map的!


2.Intent傳遞數(shù)組

嘿嘿,普通類(lèi)型倒沒(méi)問(wèn)題,但是如果是數(shù)組咧?解決方法如下:

寫(xiě)入數(shù)組

bd.putStringArray("StringArray", new String[]{"呵呵","哈哈"});
//可把StringArray換成其他數(shù)據(jù)類(lèi)型,比如int,float等等...

讀取數(shù)組

String[] str = bd.getStringArray("StringArray")

3.Intent傳遞集合

嗯,數(shù)組很簡(jiǎn)單吧,那我們?cè)賮?lái)傳下集合~這個(gè)就稍微復(fù)雜點(diǎn)了,分情況處理:


1)List

寫(xiě)入集合:

intent.putStringArrayListExtra(name, value)
intent.putIntegerArrayListExtra(name, value)

讀取集合:

intent.getStringArrayListExtra(name)
intent.getIntegerArrayListExtra(name)

2)List

將list強(qiáng)轉(zhuǎn)成Serializable類(lèi)型,然后傳入(可用Bundle做媒介)

寫(xiě)入集合:

putExtras(key, (Serializable)list)

讀取集合:

(List<Object>) getIntent().getSerializable(key)

PS:Object類(lèi)需要實(shí)現(xiàn)Serializable接口


3)Map,或更復(fù)雜的

解決方法是:外層套個(gè)List

//傳遞復(fù)雜些的參數(shù) 
Map<String, Object> map1 = new HashMap<String, Object>();  
map1.put("key1", "value1");  
map1.put("key2", "value2");  
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();  
list.add(map1);  

Intent intent = new Intent();  
intent.setClass(MainActivity.this,ComplexActivity.class);  
Bundle bundle = new Bundle();  

//須定義一個(gè)list用于在budnle中傳遞需要傳遞的ArrayList<Object>,這個(gè)是必須要的  
ArrayList bundlelist = new ArrayList();   
bundlelist.add(list);   
bundle.putParcelableArrayList("list",bundlelist);  
intent.putExtras(bundle);                
startActivity(intent); 

4.Intent傳遞對(duì)象

傳遞對(duì)象的方式有兩種:將對(duì)象轉(zhuǎn)換為Json字符串或者通過(guò)Serializable,Parcelable序列化 不建議使用Android內(nèi)置的摳腳Json解析器,可使用fastjson或者Gson第三方庫(kù)!


1)將對(duì)象轉(zhuǎn)換為Json字符串

Gson解析的例子:

Model:

public class Author{
    private int id;
    private String name;
    //...
}

public class Author{
    private int id;
    private String name;
    //...
}

寫(xiě)入數(shù)據(jù):

Book book=new Book();
book.setTitle("Java編程思想");
Author author=new Author();
author.setId(1);
author.setName("Bruce Eckel");
book.setAuthor(author);
Intent intent=new Intent(this,SecondActivity.class);
intent.putExtra("book",new Gson().toJson(book));
startActivity(intent);

讀取數(shù)據(jù):

String bookJson=getIntent().getStringExtra("book");
Book book=new Gson().fromJson(bookJson,Book.class);
Log.d(TAG,"book title->"+book.getTitle());
Log.d(TAG,"book author name->"+book.getAuthor().getName());

2)使用Serializable,Parcelable序列化對(duì)象


1.Serializable實(shí)現(xiàn):

①業(yè)務(wù)Bean實(shí)現(xiàn):Serializable接口,寫(xiě)上getter和setter方法
②Intent通過(guò)調(diào)用putExtra(String name, Serializable value)傳入對(duì)象實(shí)例 當(dāng)然對(duì)象有多個(gè)的話多個(gè)的話,我們也可以先Bundle.putSerializable(x,x);
③新Activity調(diào)用getSerializableExtra()方法獲得對(duì)象實(shí)例: eg:Product pd = (Product) getIntent().getSerializableExtra("Product");
④調(diào)用對(duì)象get方法獲得相應(yīng)參數(shù)


2.Parcelable實(shí)現(xiàn):

一般流程:

①業(yè)務(wù)Bean繼承Parcelable接口,重寫(xiě)writeToParcel方法,將你的對(duì)象序列化為一個(gè)Parcel對(duì)象;
②重寫(xiě)describeContents方法,內(nèi)容接口描述,默認(rèn)返回0就可以
③實(shí)例化靜態(tài)內(nèi)部對(duì)象CREATOR實(shí)現(xiàn)接口Parcelable.Creator
④同樣式通過(guò)Intent的putExtra()方法傳入對(duì)象實(shí)例,當(dāng)然多個(gè)對(duì)象的話,我們可以先 放到Bundle里Bundle.putParcelable(x,x),再I(mǎi)ntent.putExtras()即可

一些解釋:

通過(guò)writeToParcel將你的對(duì)象映射成Parcel對(duì)象,再通過(guò)createFromParcel將Parcel對(duì)象映射 成你的對(duì)象。也可以將Parcel看成是一個(gè)流,通過(guò)writeToParcel把對(duì)象寫(xiě)到流里面, 在通過(guò)createFromParcel從流里讀取對(duì)象,只不過(guò)這個(gè)過(guò)程需要你來(lái)實(shí)現(xiàn),因此寫(xiě)的 順序和讀的順序必須一致。

實(shí)現(xiàn)Parcelable接口的代碼示例:

//Internal Description Interface,You do not need to manage  
@Override  
public int describeContents() {  
     return 0;  
}  

@Override  
public void writeToParcel(Parcel parcel, int flags){  
    parcel.writeString(bookName);  
    parcel.writeString(author);  
    parcel.writeInt(publishTime);  
}  

public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>() {  
    @Override  
    public Book[] newArray(int size) {  
        return new Book[size];  
    }  

    @Override  
    public Book createFromParcel(Parcel source) {  
        Book mBook = new Book();    
        mBook.bookName = source.readString();   
        mBook.author = source.readString();    
        mBook.publishTime = source.readInt();   
        return mBook;  
    }  
};

Android Studio生成Parcleable插件:

Intellij/Andriod Studio插件android-parcelable-intellij-plugin 只要ALT+Insert,即可直接生成Parcleable接口代碼。

另外:Android中大量用到Parcelable對(duì)象,實(shí)現(xiàn)Parcable接口又是非常繁瑣的,可以用到 第三方的開(kāi)源框架:Parceler,因?yàn)镸aven的問(wèn)題,暫時(shí)還沒(méi)試。

參考地址:[Android的Parcelable自動(dòng)生成]

3.兩種序列化方式的比較:

兩者的比較:

  • 1)在使用內(nèi)存的時(shí)候,Parcelable比Serializable性能高,所以推薦使用Parcelable。
  • 2)Serializable在序列化的時(shí)候會(huì)產(chǎn)生大量的臨時(shí)變量,從而引起頻繁的GC。
  • 3)Parcelable不能使用在要將數(shù)據(jù)存儲(chǔ)在磁盤(pán)上的情況,因?yàn)镻arcelable不能很好的保證數(shù)據(jù)的 持續(xù)性在外界有變化的情況下。盡管Serializable效率低點(diǎn),但此時(shí)還是建議使用Serializable。

    • *

5.Intent傳遞Bitmap

bitmap默認(rèn)實(shí)現(xiàn)Parcelable接口,直接傳遞即可

實(shí)現(xiàn)代碼:

Bitmap bitmap = null;
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putParcelable("bitmap", bitmap);
intent.putExtra("bundle", bundle);

6.傳來(lái)傳去不方便,直接定義全局?jǐn)?shù)據(jù)

如果是傳遞簡(jiǎn)單的數(shù)據(jù),有這樣的需求,Activity1 -> Activity2 -> Activity3 -> Activity4, 你想在Activity中傳遞某個(gè)數(shù)據(jù)到Activity4中,怎么破,一個(gè)個(gè)頁(yè)面?zhèn)髅矗?/p>

顯然不科學(xué)是吧,如果你想某個(gè)數(shù)據(jù)可以在任何地方都能獲取到,你就可以考慮使用 Application全局對(duì)象了!

Android系統(tǒng)在每個(gè)程序運(yùn)行的時(shí)候創(chuàng)建一個(gè)Application對(duì)象,而且只會(huì)創(chuàng)建一個(gè),所以Application 是單例(singleton)模式的一個(gè)類(lèi),而且Application對(duì)象的生命周期是整個(gè)程序中最長(zhǎng)的,他的生命 周期等于這個(gè)程序的生命周期。如果想存儲(chǔ)一些比靜態(tài)的值(固定不改變的,也可以變),如果你想使用 Application就需要自定義類(lèi)實(shí)現(xiàn)Application類(lèi),并且告訴系統(tǒng)實(shí)例化的是我們自定義的Application 而非系統(tǒng)默認(rèn)的,而這一步,就是在AndroidManifest.xml中衛(wèi)我們的application標(biāo)簽添加:name屬性!

關(guān)鍵部分代碼:

1)自定義Application類(lèi):

class MyApp extends Application {
    private String myState;
    public String getState(){
        return myState;
    }
    public void setState(String s){
        myState = s;
    }
}

2)AndroidManifest.xml中聲明:

<application android:name=".MyApp" android:icon="@drawable/icon" 
  android:label="@string/app_name">

3)在需要的地方調(diào)用:

class Blah extends Activity {
    @Override
    public void onCreate(Bundle b){
        ...
    MyApp appState = ((MyApp)getApplicationContext());
    String state = appState.getState();
        ...
    }
}

高逼格寫(xiě)法

:在任何位置都能獲取到Application全局對(duì)象。

Applicaiton是系統(tǒng)的一個(gè)組件,他也有自己的一個(gè)生命周期,我們可以在onCraete里獲得這個(gè) Application對(duì)象。貼下修改后的代碼吧!

class MyApp extends Application {
    private String myState;
    private static MyApp instance;

    public static MyApp getInstance(){
        return instance;
    }

    public String getState(){
        return myState;
    }
    public void setState(String s){
        myState = s;
    }

    @Override
    public void onCreate(){
        onCreate();
        instance = this;
    }

}

然后在任意地方我們就可以直接調(diào)用:MyApp.getInstance()來(lái)獲得Application的全局對(duì)象!


注意事項(xiàng):

Application對(duì)象是存在于內(nèi)存中的,也就有它可能會(huì)被系統(tǒng)殺死,比如這樣的場(chǎng)景:

我們?cè)贏ctivity1中往application中存儲(chǔ)了用戶賬號(hào),然后在Activity2中獲取到用戶賬號(hào),并且顯示!

如果我們點(diǎn)擊home鍵,然后過(guò)了N久候,系統(tǒng)為了回收內(nèi)存kill掉了我們的app。這個(gè)時(shí)候,我們重新 打開(kāi)這個(gè)app,這個(gè)時(shí)候很神奇的,回到了Activity2的頁(yè)面,但是如果這個(gè)時(shí)候你再去獲取Application 里的用戶賬號(hào),程序就會(huì)報(bào)NullPointerException,然后crash掉~(yú)

之所以會(huì)發(fā)生上述crash,是因?yàn)檫@個(gè)Application對(duì)象是全新創(chuàng)建的,可能你以為App是重新啟動(dòng)的, 其實(shí)并不是,僅僅是創(chuàng)建一個(gè)新的Application,然后啟動(dòng)上次用戶離開(kāi)時(shí)的Activity,從而創(chuàng)造App 并沒(méi)有被殺死的假象!所以如果是比較重要的數(shù)據(jù)的話,建議你還是進(jìn)行本地化,另外在使用數(shù)據(jù)的時(shí)候 要對(duì)變量的值進(jìn)行非空檢查!還有一點(diǎn)就是:不止是Application變量會(huì)這樣,單例對(duì)象以及公共靜態(tài)變量 也會(huì)這樣~


7.單例模式傳參

上面的Application就是基于單例的,單例模式的特點(diǎn)就是可以保證系統(tǒng)中一個(gè)類(lèi)有且只有一個(gè)實(shí)例。 這樣很容易就能實(shí)現(xiàn),在A中設(shè)置參數(shù),在B中直接訪問(wèn)了。這是幾種方法中效率最高的。

范例代碼:(代碼來(lái)自于網(wǎng)上~)

①定義一個(gè)單例類(lèi)

public class XclSingleton  
{  
    //單例模式實(shí)例  
    private static XclSingleton instance = null;  

    //synchronized 用于線程安全,防止多線程同時(shí)創(chuàng)建實(shí)例  
    public synchronized static XclSingleton getInstance(){  
        if(instance == null){  
            instance = new XclSingleton();  
        }     
        return instance;  
    }     

    final HashMap<String, Object> mMap;  
    private XclSingleton()  
    {  
        mMap = new HashMap<String,Object>();  
    }  

    public void put(String key,Object value){  
        mMap.put(key,value);  
    }  

    public Object get(String key)  
    {  
        return mMap.get(key);  
    }  

} 

②設(shè)置參數(shù):

XclSingleton.getInstance().put("key1", "value1");  
XclSingleton.getInstance().put("key2", "value2");  

本節(jié)小結(jié):

好的,關(guān)于Intent復(fù)雜數(shù)據(jù)傳輸就到這里,本節(jié)除了講述使用Intent來(lái)傳遞復(fù)雜數(shù)據(jù)外,還教了大家 使用Application和單例模式來(lái)傳遞參數(shù)!相信會(huì)對(duì)大家在數(shù)據(jù)傳遞方面帶來(lái)方便,謝謝~

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)