Intent之複雜資料的傳遞


本節引言:

上一節中我們學習了Intent的一些基本使用,知道了Intent的七個屬性,顯式Intent以及 隱式Intent,以及如何自訂隱式Intent,最後也提供了一些常用的系統Intent給大家! 而本節跟大家講解的是Intent傳遞資料的問題~好的,開始本節內容~


1.Intent傳遞簡單資料

還記得我們在Activity那裡學過如何在兩個Activity中互相傳遞簡單資料的方法嗎?

1.png

就是可以直接透過呼叫Intent的putExtra()方法存入數據,然後在取得Intent後呼叫getXxxExtra來獲得 對應類型的資料;傳遞多個的話,可以使用Bundle物件作為容器,透過呼叫Bundle的putXxx先將數據 儲存到Bundle中,然後呼叫Intent的putExtras()方法將Bundle存入Intent中,然後得到Intent以後, 呼叫getExtras()取得Bundle容器,然後呼叫其getXXX來取得對應的資料! 另外資料儲存有點類似Map的<鍵,值>!


2.Intent傳遞陣列

嘿嘿,普通型別倒沒問題,但是如果是陣列咧嘴?解決方法如下:

寫入陣列

bd.putStringArray("StringArray", new String[]{"呵呵","哈哈"});
//可把StringArray換成其他資料型別,例如int,float等等...

讀取陣列

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

3.Intent傳遞集合

嗯,陣列很簡單吧,那我們再來傳下集合~這個就稍微複雜點了,分情況處理:


#1)List<基本資料型別或String>

寫入集合:

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

讀取集合:

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

##2)List< Object>

將list強轉成Serializable類型,然後傳入(可用Bundle做媒介)

#寫入集合:

##putExtras(key, ( Serializable)list)
讀取集合:

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

PS:Object類別需要實作Serializable介面


3)Map<String, Object>,或更複雜的

解決方法是:外層套個List

//傳遞複雜些的參數 
Map<String, Object> map1 = new HashMap<String, Object>();  
# map1.put("key1", "value1");  
map1.put("key2", "value2");  
List<Map<String, Object>> list =1new , Object>>();  
list.add(map1);  

Intent intent = new Intent();  
#intent.setClass(MainActivity.this,ComActivity.class);#Com遠.class);# #Bundle bundle = new Bundle();  

//必須定義一個list用於在budnle中傳遞需要傳遞的ArrayList<Object>,這個是必須要的  
ArrayList bundlelist ;   
bundlelist.add(list);   
bundle.putParcelableArrayList("list",bundlelist);  
#intent.putExtras(bundle); 
##4.Intent傳遞物件
傳遞物件的方式有兩種:將物件轉換為Json字串或透過Serializable,Parcelable序列化 不建議使用Android內建的摳腳Json解析器,可使用fastjson或Gson第三方函式庫!

1)將物件轉換為Json字串

Gson解析的範例:

Model:

public class Author{    private int id;    private String name;

    //...
# #    private int id;
    private String name;
    //...
}



寫入數據:



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

#

讀取資料:

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

2)使用Serializable,Parcelable序列化物件


1.Serializable實作:

①業務Bean實作:Serializable介面,寫上getter和setter方法
②Intent透過呼叫putExtra(String name, Serializable value)傳入物件實例 當然物件有多個的話多個的話,我們也可以先Bundle.putSerializable(x,x);
③新Activity呼叫getSerializableExtra()方法取得物件實例: eg:Product pd = (Product) getIntent().getSerializableExtra("Product");
④呼叫物件get方法取得對應參數


#2.Parcelable實作:

一般流程:

①業務Bean繼承Parcelable介面,重寫writeToParcel方法,將你的物件序列化為一個Parcel物件;
②重寫describeContents方法,內容介面描述,預設回傳0就可以
③實例化靜態內部物件CREATOR實作介面Parcelable.Creator
④同樣式透過Intent的putExtra()方法傳入物件實例,當然多個對象的話,我們可以先 放到Bundle裡Bundle.putParcelable(x,x),再Intent.putExtras()即可

一些解釋:

透過writeToParcel將你的對象映射成Parcel對象,再透過createFromParcel將Parcel對象映射 成你的對象。也可以將Parcel看成是一個流,透過writeToParcel把物件寫到流裡面, 在透過createFromParcel從流裡讀取對象,只不過這個過程需要你來實現,因此寫的 順序和讀的順序必須一致。

實作Parcelable介面的程式碼範例:

//內部描述介面,不需要管理
@Override
public int describeContents(){  
     return 0;  
}  
#       
##      
 
@Override  
public void writeToParcel  
    parcel.writeString(作者);  
    parcel.writeInt(publishTime);  
}  
      

public static final Parcelable.Creator; CREATOR = new Creator() {  
    @Override  
    public Book[] newArray()   
    }  
          
    @Override  
    public Book  Book();    
        mBook.bookName = source.readString();   
        mBook.author = source.readString();    
        mBook.publishTime = source.readInt();   
        則返回mBook;  
    }  
};

Android Studio產生Parcleable外掛:

Intellij/Andriod Studio外掛程式android-parcelable-intellij-plugin 只要ALT+Insert,即可直接產生Parcleable介面程式碼。

另外:Android中大量用到Parcelable對象,實作Parcable接口又是非常繁瑣的,可以用到 第三方的開源框架:Parceler,因為Maven的問題,暫時還沒試。

參考位址:[Android的Parcelable自動產生]

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

#兩者的比較:

  • 1)在使用記憶體的時候,Parcelable比Serializable效能高,所以建議使用Parcelable。
  • 2)Serializable在序列化的時候會產生大量的臨時變量,從而引起頻繁的GC。
  • 3)Parcelable不能使用在要將資料儲存在磁碟上的情況,因為Parcelable不能很好的保證資料的 持續性在外界有變化的情況下。儘管Serializable效率低點,但此時還是建議使用Serializable。

5.Intent傳遞Bitmap

bitmap預設實作Parcelable介面,直接傳遞即可

##實作程式碼:

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


6.傳來傳去不方便,直接定義全域資料

如果是傳遞簡單的數據,有這樣的需求,Activity1 -> Activity2 -> Activity3 - > Activity4, 你想在Activity中傳遞某個資料到Activity4中,怎麼破,一個個頁面傳麼?

顯然不科學是吧,如果你想某個資料可以在任何地方都能取得到,你就可以考慮使用Application全域物件了!

Android系統在每個程式運行的時候創建一個Application對象,而且只會創建一個,所以Application 是單例(singleton)模式的一個類,而Application物件的生命週期是整個程式中最長的,他的生命 週期等於這個程式的生命週期。如果想儲存一些比靜態的值(固定不改變的,也可以變),如果你想使用 Application就需要自訂類別實作Application類,並且告訴系統實例化的是我們自訂的Application 而非系統預設的,而這一步,就是在AndroidManifest.xml中衛我們的application標籤添加:name屬性

關鍵部分程式碼:

1)自訂Application類別:

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


2)AndroidManifest.xml中宣告:

<application android:name=".MyApp" android:icon ="@drawable/icon" 

  android:label="@string/app_name">

3)在需要的地方呼叫:

class Blah extends Activity {

#    @Override
    public void onCreate(Bundle b){
  );
    String state = appState.getState();
        ...
    }
}



#######################################################################################################################################。

高逼格寫法

:在任何位置都能取得到Application全域物件。


Applicaiton是系統的一個元件,他也有自己的一個生命週期,我們可以在onCraete裡得到這個 Application物件。貼下修改後的程式碼吧!

class MyApp extends Application {
#   private String myState;
    private static MyApp        return instance;
    }
    
    
    public String getState(){
               myState = s;
    }
    
    @Override
    public void onCreate(){
        onCreate ##}

然後在任意地方我們就可以直接呼叫:MyApp.getInstance()來取得Application的全域物件!


注意事項:

Application物件是存在於記憶體中的,也就有它可能會被系統殺死,例如這樣的場景:

我們在Activity1中往application中儲存了使用者帳號,然後在Activity2中取得到使用者帳號,並且顯示!

如果我們點擊home鍵,然後過了N久候,系統為了回收記憶體kill掉了我們的app。這個時候,我們重新 打開這個app,這個時候很神奇的,回到了Activity2的頁面,但是如果這個時候你再去獲取Application 裡的用戶帳號,程式就會報NullPointerException,然後crash掉~

之所以會發生上述crash,是因為這個Application物件是全新創建的,可能你以為App是重新啟動的, 其實並不是,只是創建一個新的Application,然後啟動上次用戶離開時的Activity,從而創造App 並沒有被殺死的假象!所以如果是比較重要的數據的話,建議你還是在進行在地化,另外在使用數據的時候 要對變數的值進行非空檢查!還有一點就是:不只Application變數會這樣,單例物件以及公共靜態變數 也會這樣~


7.單例模式傳參

上面的Application就是基於單例的,單例模式的特點就是可以保證系統中一個類別有且只有一個實例。 這樣很容易就能實現,在A中設定參數,在B中直接存取了。這是幾種方法中效率最高的。

範例程式碼:(程式碼來自於網路~)

#①定義一個單例類別

public class XclSingleton  
{  
    //單例模式實例  
    private static Xcl ronized 用於執行緒安全,防止多執行緒同時建立實例  
    public synchronized static XclSingleton getInstance(){  
         XclSingleton();  
        }     
        return #      
    final HashMap<String, Object> mMap;  
    private Xcl因 HashMap<String,Object>();  
    }  
      
    public void put(String key,Object value){  
#        mMap.put(key,value);  
    }    {  
        return mMap.get (key);  
    }  
      
}



②設定參數:



#XclSingleton.get#②設定參數:

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

#本節小結:

好的,關於Intent複雜資料傳輸就到這裡,本節除了講述使用Intent來傳遞複雜資料外,還教了大家 使用Application和單例模式來傳遞參數!相信會對大家在資料傳遞上帶來方便,謝謝~