1. 程式人生 > >Activity之間的通訊方式

Activity之間的通訊方式

參考部落格:

Activity之間的資料通訊方式主要有以下????種:

  • Intent
  • 藉助類的靜態變數
  • 藉助全域性變數/Application
  • 藉助外部工具 
    – 藉助SharedPreference 
    – 使用Android資料庫SQLite 
    – 赤裸裸的使用File 
    – Android剪下板
  • 藉助Service

(1) 在Intent跳轉時攜帶資料 
Intent是Android四大元件(Activity、Service、BroadcastReceiver、ContentProvider)之間通訊的紐帶,在Intent中攜帶資料也是四大元件之間資料通訊最常用、最普通的方式。常規寫法如下:

//建立用於封裝資料的Bundle物件
Bundle bundle = new Bundle();
bundle.putString("name", "WangJie");
bundle.putInt("age", 23);

Intent intent = new Intent(MainActivity.this, SecondActivity.class);
//將Bundle物件嵌入Intent中
intent.putExtras(bundle);
startActivity(intent);

更簡潔,也是更智慧的寫法是:

//建立Intent物件
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
//程式自動建立Bundle,然後將對Intent新增的資料裝載在Bundle中,對使用者透明
intent.putExtra("name", "WangJie");
intent.putExtra("age", 23);
startActivity(intent);

在SecondActivity中獲取intent跳轉時攜帶的資料:

//intent要用this的getIntent()獲取
Intent intent = getIntent();
//用intent.getXXXExtra("key-name")或是intent.getXXXExtra("key-name", default-value)獲取值
String name = intent.getStringExtra("key1");
int age = intent.getIntExtra("key2", 0);

(2)藉助類的靜態變數來實現 
由於類的靜態成員可以通過“className.fileName”來訪問,故而可以供兩個Activity訪問從而實現Activity之間的資料通訊: 
在MainActivity中:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //先檢視一下未更改的值
        SecondActivity.age = 23;

        Button btn = (Button) findViewById(R.id.button);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //到SecondActivity中檢視對age更改是否有效
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });
    }
}

在SecondActivity中:

public class SecondActivity extends AppCompatActivity {
    //宣告為靜態file
    static int age = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        //在MainActivity中更改了age,所以這裡肯定不是"1"
        Toast.makeText(this, "在MianActivity中更改了age後的值 = " + age, Toast.LENGTH_SHORT).show();
    }
}
  • 看結果: 
    SecondActivity中的結果

(3)藉助全域性變數來實現/Application 
和類的靜態變數類似,但是這個類作為單獨第三個類(最好是寫一個Application類):

public class ForExampleClass {
    //此處宣告一個public static 成員來實現資料通訊
    public static int age = 0;
}

在一個Activity中對類的靜態變數進行訪問和更改:

//先檢視一下未更改的值
Toast.makeText(this, "age = " + ForExampleClass.age, Toast.LENGTH_SHORT).show();
//在一個Activity中對類的靜態變數進行變更
ForExampleClass.age = 23;

然後在另一個Activity中訪問該變數,來驗證這種通訊方式:

//在另一個Activity中訪問更改後的變數來驗證
Toast.makeText(this, "更改後的age = " + ForExampleClass.age, Toast.LENGTH_SHORT).show();
  • 看結果: 
    在前一個Activity中變更前的值: 
    改前 
    在後一個Activity中變更後的值: 
    改後 
    這是使用全域性變數的本質,但是Java是程式設計思想不建議這樣的寫法,所以最好是將ForExampleClass繼承Application,在應用的所有Activity都可以訪問,並且要用get\set方法進行訪問,可以看一下 
    @彬彬的部落格Android使用全域性變數和@鄔良歡Android入門篇三:使用靜態變數在Activity之間傳遞資料。 
    其實上邊“(2)藉助類的靜態變數來實現”和“(3)藉助全域性變數來實現”很類似,只是在藉助類的靜態變數時接收資訊的Activity中宣告靜態file,在別的Activity中做更改,而藉助全域性變數(建議繼承Application,此時就是所謂的“使用Application”,因為Application類在本應用所有Activity中都可以訪問,但是要注意記憶體洩漏的問題)是另外宣告一個類,所有Activity共享這個類而已

下面這裡附上使用Activity來完成資料傳遞的Demo,這種方法適用於我們要用intent來傳輸大資料時,因為intent的傳輸資料容量最多10m?反正當時我多傳幾張圖片就報錯,查了一下定義一個什麼檔案通過get/set操作來獲取。但是這種方法也有它的弊端:一個Application裡只能有一個andoid:.name的定義,如果存在多個不同的話只能把它們都合併到同一個類裡面。

(4)藉助外部儲存來實現通訊

(4-1)使用SharedPreference實現Activity之間的資料通訊

        SharedPreference是Android中最簡單的檔案本地化儲存方式,Android API也提供相當簡單的方式來進行讀寫操作。下面簡單看一下SharedPreference的使用:

首先,我們在MainActivity中先檢視一下SharedPreference檔案中的初始值,然後做更改:

public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        //獲取Application的SharedPreference物件
        SharedPreferences sp = getApplication().getSharedPreferences("myInfo", 0);
        //get方法獲取值,如果沒有儲存對應的key-value則返回get方法給的預設值
        String name = sp.getString("name", "null");
        int age = sp.getInt("age", 0);
        boolean isStudent = sp.getBoolean("isStudent", false);
        //檢視一下初始值
        Toast.makeText(this, "MianActivity中:\n" + "name = " + name
                + "\nage = " + age + "\nisStudent = " + isStudent, Toast.LENGTH_SHORT).show();
 
        //在寫入時要先獲取SharedPreference的Editor物件,經過Editor進行寫入
        SharedPreferences.Editor editor = sp.edit();
        editor.putString("name", "WangJie");
        editor.putInt("age", 23);
        editor.putBoolean("isStudent", true);
        //put完成後一定要commit(),否則不會生效
        editor.commit();
 
        Button btn = (Button) findViewById(R.id.button);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });
    }
}
然後,在SecondActivity中再次檢視:
public class SecondActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
 
        //獲取Application的SharedPreference物件
        //注意"name"值一定要一致,否則會新建一個SharedPreference物件(本地儲存新增一個檔案)
        SharedPreferences sp = getApplication().getSharedPreferences("myInfo", 0);
        String name = sp.getString("name", "null");
        int age = sp.getInt("age", 0);
        boolean isStudent = sp.getBoolean("isStudent", false);
 
        Toast.makeText(this, "SecondActivity中檢視:\n" + "name = " + name
                + "\nage = " + age + "\nisStudent = " + isStudent, Toast.LENGTH_SHORT).show();
    }
}

        結果看圖:

MainActivity結果SecondActivity

        不足之處在於——從SharedPreference的API就可以看出,採用SharedPreference只能存取標準資料型別的變數值int、float、long、boolean、String。對與一些複雜型別的就捉襟見肘了。

(4-2)使用SQLite來實現資料共享通訊

         和SharedPreference類似,SQLite也是Android提供的一種資料持久化操作方式之一。SQLite是遵循SQL標準的資料庫,支援標準SQL語句進行操作,當然Android也為對SQL不熟悉的開發者提供了相應的API(但這些API大多需要傳入很多引數,個人覺得很不好用,還是建議學學 SQL,畢竟Android也只用一些簡單的增刪改查)。

資料庫是Application的私有檔案,如果其他應用想訪問該資料要用過ContentProvider來實現。比如你用系統的錄音機元件即時搞一段音訊資訊,它不是返回可能大到恐怖的錄音資料,而是會返回給你一個Uri,它標明瞭這份資料在ContentProvider的地址資訊,拿著這個Uri,領取資料就好,就像一張門票。不同Activity可以通過訪問或更改資料庫中對應的資料項來實現兩個(多個Activity)之間的資料通訊。

這裡就不舉例使用了,以後有機會會專門說說SQLite的使用。

(4-3)直接使用File來實現

        其實從本質屬性來講,使用SharedPreference和SQLite來存取資料都是使用File來存取資料的方式——SharedPreference是存放在data/data/應用包名/shared_prefs目錄下字尾為.xml的檔案,SQLite是存放在data/data/應用包名/databases目錄下的字尾為.db3的檔案。只是Android系統為這些檔案存取專門格式化了存取格式而已,本質上還是檔案讀寫。

        當然,如果你足夠淡定,也可以用赤裸裸的File來儲存。如果這個檔案存在手機私有目錄下,那就內部使用,放在SD卡上,那就可以所有應用,一切分享。但是這樣暴力的方式需要你在檔案讀寫時進行大量的額外工作。

     基於這樣外部儲存的資料傳輸,優缺點顯而易見,它解決了困擾Intent的傳輸路徑複雜,不利於傳輸大批量資料的問題,但同時,它有留下了效率隱患,複雜了程式設計模型。因為面對外部儲存,開發者必須要考慮效率問題,很多時候,多執行緒就會被提上議程,這樣,想不麻煩,都不行鳥。

(5)藉助Service來實現

既然存在外部太慢,那麼還是在記憶體級別解決問題好了,這時候,你可能就需要請出Android四大元件之一的Service了。Service設計的本意,就是提供一些後臺的服務,資料存取,也可以歸於其職責的一部分。

Service是提供了直連機制,呼叫的Activity,可以通過bindService方法,與目標Service建立一條資料通路,拿到IBinder。這樣,通過Android提供的IPC模型(程序間通訊),就可以進行遠端方法的呼叫和資料的傳輸了。

通過這種模式,可以解決一定問題,但是對於Service來說,實在是太大才小用了,Service的專長,不是在資料,還是在邏輯。對於傳資料而言,Service還是重量了一點,不但是有連線耗精力,傳輸經由IPC,寫起來也夠費勁。而且作為元件,Service隨時可能死掉,你還是要費勁心機的處理資料的持久化,得不償失。