1. 程式人生 > >第一行程式碼學習筆記第二章——探究活動

第一行程式碼學習筆記第二章——探究活動

知識點目錄

知識點回顧

2.1 活動是什麼

是一種包含使用者介面的元件,主要用於和使用者進行互動。

2.2 活動的基本用法

Android Studio一個工作區間只允許開啟一個專案,故點選導航欄File –> Close Project ,新建專案ActivityTest,選擇Add No Activity 手動建立活動。

這裡寫圖片描述

2.2.1 手動建立活動

將專案結構手動切換成Project模式:

這裡寫圖片描述

右擊com.example.activitytest –> New –> Activity –> Empty Activity ,建立FirstActivity。

這裡寫圖片描述

任何活動都應該重寫Activity的onCreate()方法。

2.2.2 建立和載入佈局

Android程式設計講究邏輯和檢視分離,最好每一個活動都能對應一個佈局,佈局就是用來顯示介面的內容。

建立佈局:

右擊app/src/main/res目錄——>New——>Directory,彈出新建目錄的視窗,建立一個名為layout的目錄,然後layout目錄右鍵——>New——>Layout resource file,彈出新建佈局檔案的視窗,將佈局檔案命名為first_layout,根元素預設選擇為LinearLayout。

這裡寫圖片描述

載入佈局:

專案中新增的資原始檔都會在R檔案中生成一個相應的資源id。通過setContentView()方法來載入當前活動的佈局,而在setContentView()方法中我們傳入的是佈局檔案的id。

setContentView(R.layout.first_layout);
2.2.3 在AndroidManifest檔案中註冊

所有的活動都要在AndroidManifest.xml中進行註冊才能生效,但Android Studio會自動幫我們註冊。開啟app/src/main/AndroidManifest.xml檔案。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.activitytest">
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".FirstActivity"
            android:label="This is FirstActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

其中:

package指定了程式的包名;

name指定註冊的是哪個活動;

label指定活動中標題欄的內容,還會成為啟動器(Launcher)中應用程式顯示的名稱;

intent-filter標籤指定當前活動為程式的主活動。

注:如果整個程式沒有指定任何一個活動作為主活動,這個程式仍然可以正常安裝,只是無法在啟動器中看到或者開啟這個程式。這種程式一般都是作為第三方服務提供其他應用在內部進行呼叫的,如支付寶快捷支付服務。

2.2.4 在活動中使用Toast

Toast是Android系統提供的一種資訊提醒方式。資訊在一段時間後會自動消失,並且不會佔用任何螢幕空間。

在first.xml中定義一個Button,作為Toast觸發點。在first.xml方法中新增如下程式碼:

<Button
    android:id="@+id/button_1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Button 1"/>

在onCreate()方法中新增如下程式碼:

 Button button1 = (Button) findViewById(R.id.button_1);
 button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    Toast.makeText(FirstActivity.this,"You Click Button 1",Toast.LENGTH_SHORT).show();
    }
 });

通過findViewById()方法獲取到在佈局檔案中定義的元素,傳入R.id.button_1獲取到Button例項。給button1設定一個監聽器,然後在onClick()方法中執行監聽事件。Toast輸入的快捷鍵是Toast+Tab鍵。makeText()方法中的三個引數:

第一個引數:上下文Context。

第二個引數:Toast顯示的文字內容。

第三個引數:Toast顯示的時長,有兩個內建常量,分別是Toast.LENGTH_SHORT和Toast.LENGTH_LONG。

2.2.5 在活動中使用Menu

在res目錄下新建一個menu資料夾,右擊res目錄——>New——>Directory,輸入資料夾名menu,點選OK。再在menu資料夾下新建一個名為main的選單檔案,右擊menu資料夾——>New——>Menu resource file。然後在main.xml中新增如下程式碼:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/add_item" android:title="Add"/>
    <item android:id="@+id/remove_item" android:title="Remove"/>
</menu>

其中:

item標籤是用來建立具體的某一個選單項;

android:id 給選單項指定一個唯一的識別符號;

android:title 給選單項指定一個名稱。

返回FirstActivity中重寫onCreateOptionMenu()方法,給當前活動建立選單。

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main,menu);
    return true;
}

通過getMenuInflater()得到MenuInflater物件,再呼叫它的inflate()方法就可以給當前活動建立選單了。

其中:

引數一:指定通過哪個資原始檔來建立選單。

引數二:指定選單項新增到哪一個Menu物件中。

返回true,表示允許建立的選單顯示出來;返回false,建立的選單無法顯示。

重寫onOptionsItemSelected()方法,定義選單響應事件。新增如下程式碼:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.add_item:
            Toast.makeText(this, "You Click Add", Toast.LENGTH_SHORT).show();
            break;
        case R.id.remove_item:
            Toast.makeText(this, "You Click Remove", Toast.LENGTH_SHORT).show();
            break;
        default:
    }
    return true;
}

通過item.getItemId()方法來判斷點選的是哪一個選單項,然後在對應的選單項中新增自己相應的邏輯。

標題欄右側一個三點的符號,就是選單按鈕,選單預設是不會顯示出來,只有點選一下選單按鈕才會彈出具體內容,因此它不會佔用任何活動的空間。

執行效果如下:

這裡寫圖片描述

2.2.6 銷燬一個活動
  • 按Back鍵

  • 呼叫finish()方法。

2.3 使用Intent在活動之間穿梭

Intent是Android程式中各元件之間進行互動的一種重要方式,它不僅可以指明前元件想要執行的動作,還可以在不同元件之間傳遞資料。Intent一般可用於啟動活動、啟動服務以及傳送廣播等場景。

2.3.1 使用顯式Intent

右擊com.example.activitytest包——>New——>Activity——>Empty Activity,新建一個SecondActivity,勾選Generate Layout File,佈局檔案命名為second_layout,不勾選Launcher Activity選項。

編輯second_layout.xml檔案:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:id="@+id/button_2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button 2"/>
</LinearLayout>

修改FirstActivity()方法中按鈕的點選事件,程式碼如下:

button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
        startActivity(intent);
    }
});

其中,Intent中的引數:

引數一:啟動活動的上下文;

引數二:指定想要啟動的目標活動。

按Back鍵可以銷燬當前活動,返回上一個活動。

2.3.2 使用隱式Intent

隱式Intent不明確指出要啟動的活動,而是指定一系列更為抽象的action和category等資訊,然後讓系統去分析這個Intent,從而找出合適的活動去啟動。

通過在activity標籤下配置intent-filter的內容,可以指定當前活動能響應的action和category,開啟AndroidManifest.xml,新增如下程式碼:

<activity android:name=".SecondActivity">
    <intent-filter>
        <action android:name="com.example.activitytest.ACTION_START" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

只有action和category中的內容同時匹配Intent中指定的action和category時,這個活動才能響應該Intent。

修改FirstActivity中按鈕的點選事件:

button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent("com.example.activitytest.ACTION_START");
        startActivity(intent);
    }
});

這樣通過隱式的Intent也可以啟動SecondActivity。

每個Intent中只能指定一個action,卻可以指定多個category。

button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
       Intent intent = new Intent("com.example.activitytest.ACTION_START");
       intent.addCategory("com.example.activitytest.MY_CATEGORY");
       startActivity(intent);
    }
});

開啟AndroidManifest.xml,在SecondActivity的intent-filter中新增一個category宣告

<category android:name="com.example.activitytest.MY_CATEGORY" />
2.3.3 更多隱式Intent的用法

使用隱式Intent,不僅可以啟動自己程式內的活動,還可以啟動其他程式的活動。例如可以呼叫系統的瀏覽器來開啟一個網頁。

修改FirstActivity中按鈕點選事件的程式碼,如下:

 button1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setData(Uri.parse("http://www.baidu.com"));
            startActivity(intent);
        }
    });

其中:

Intent的action是Intent.ACTION_VIEW,是一個Android內建的動作;

Uri.parse()方法將網址解析成一個Uri物件;

setData()方法將解析好的Uri物件傳遞進去。

因為intent有setData()方法,所以我們也可以在intent-filter標籤中再配置一個data標籤,用於更精確指定當前活動響應的是什麼型別的資料。data標籤中可以配置以下內容:

  • android:scheme。用於指定資料的協議部分,例如上面的http

  • android:host。用於指定資料的主機名部分,例如上面的www.baidu.com

  • anddroid:port。用於指定資料的埠部分。一般緊隨主機名之後

  • android:path。用於指定主機名和埠之後的部分。如一段網址中跟在域名之後的內容

  • android:mimeType。用於指定可以處理的資料型別,允許使用萬用字元的方式進行指定

只有data標籤中指定的內容和Intent中攜帶的Data完全一致時,當前的活動才能夠響應該Intent。

除了http協議外,還可以指定其他協議。比如geo表示顯示地理位置、tel表示撥打電話。呼叫系統撥號介面示例程式碼如下:

button1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(Intent.ACTION_DIAL);
            intent.setData(Uri.parse("tel:10086"));
            startActivity(intent);
        }
    });
2.3.4 向下一個活動傳遞資料

Intent不僅可以啟動一個活動,還可以在啟動活動的時候傳遞資料,傳遞資料的思路如下:

  • 在啟動頁面中通過Intent提供的一系列putExtra()方法,將傳遞的資料放在Intent中

  • 在被啟動的頁面中通過getIntent獲取Intent物件,再呼叫getXXXExtra()方法獲取值。

比如在FirstActivity中傳一個字串到SecondActivity中,在FirstActivity中的程式碼如下:

 button1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String data = "Hello SecondActivity";
            Intent intent = new Intent(FirstActivity.this, SecondActivity.class );
            intent.putExtra("extra_data", data);
            startActivity(intent);
        }
    });

這裡採用的是顯式Intent的方式來啟動SecondActivity,並通過putExtra()方法傳遞一個字串資料。putExtra()中的兩個引數:

  • 引數一:鍵。用於在後面從Intent中獲取值。

  • 引數二:要傳遞的資料值。

從SecondActivity中取值的程式碼如下:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_second);
    Intent intent = getIntent();
    String data = intent.getStringExtra("extra_data");
}

首先通過getIntent()方法獲取到啟動SecondActivity的Intent,然後呼叫getStringExtra()方法,傳入相應的鍵值,從而可以得到傳遞的資料。因為這裡傳入的是字串資料,那麼就使用getStringExtra()方法來獲取對應的資料;如果傳遞的是整型資料,則使用getIntExtra()方法;如果傳遞的是布林型資料,則使用getBooleanExtra()方法,以此類推。

2.3.5 返回資料給上一個活動

如果在啟動另外一個活動後,想得到被啟動的活動的資料反饋,那麼我們在啟動活動的時候就不能再用startActivity()方法了,而應該使用startActivityForResult()方法,該方法接收兩個引數:
* 引數一:intent

  • 引數二:請求碼。用於在之後的回撥中判斷資料的來源。

修改FirstActivity中按鈕的點選事件:

button1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(FirstActivity.this, SecondActivity.class );
            startActivityForResult(intent,1);
        }
    });

請求碼要確定是一個唯一值,這裡傳入1。接著在SecondActivity中給按鈕註冊點選事件,並在點選事件中新增返回資料的邏輯,程式碼如下:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_second);
    Button button2 = (Button) findViewById(R.id.button_2);
    button2.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent();
            intent.putExtra("data_return", "Hello FirstActivity");
            setResult(RESULT_OK, intent);
            finish();
        }
    });
}

這裡我們構建了一個Intent,但這裡的Intent僅僅只是用於傳遞資料,不啟動其他任何Activity。然後將要反饋的資料放在intent中,然後再呼叫setResult()方法,該方法是專門用於向上一個活動返回資料的。setResult()方法有兩個引數:

  • 引數一:用於向上一個活動返回處理結果。一般只使用RESULT_OK或RESULT_CANCELED。

  • 引數二:帶有資料的Intent。

最後呼叫finish()方法來銷燬當前活動。

因為前面我們使用的是startActivityForResult()方法來啟動的SecondActivity,那麼在SecondActivity被銷燬後會回撥上一個活動的onActivityResult()方法。因為我們需要在FirstActivity中重寫這個方法來得到返回的資料,程式碼如下:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
        case 1:
            if (resultCode == RESULT_OK) {
                String returnData = data.getStringExtra("data_return");
                Log.d(TAG, returnData);
            }
            break;
        default:
    }
}

其中,onActivityResult()方法中帶有三個引數:

  • 引數一:requestCode,請求碼。就是在活動時傳入的請求碼。

  • 引數二:resultCode,結果碼。就是在返回資料時傳入的處理結果。

  • 引數三:data,返回資料的Intent。

由於有可能在一個活動中呼叫startActivityForResult()方法去啟動很多不同的活動,每一個活動返回的資料都會回撥onActivityResult()方法,因此我們需要通過requestCode的值來判斷資料的來源,再通過resultCode的值來判斷處理結果是否成功,最後再從data中取值,這樣才算完成了向上一個活動返回資料的工作。

上面我們是通過點選SecondActivity中的按鈕來返回上一個活動,如果我們是直接按Back鍵返回到FirstActivity,那麼資料就沒法返回了。為了處理這種情況,我們可以通過重寫SecondActivity中的onBackPressed()方法來解決,程式碼如下:

@Override
public void onBackPressed() {
    Intent intent = new Intent();
    intent.putExtra("data_return", "Hello FirstActivity2");
    setResult(RESULT_OK, intent);
    finish();
}

這樣當用戶按下Back鍵,就會去執行onBackPressed()方法中的程式碼,同樣可以將資料返回到FirstActivity中去。

2.4 活動的生命週期

掌握Activity的生命週期對Android開發者來說是至關重要的。

2.4.1 返回棧

Android中的活動是可以層疊的,每啟動一個新的活動,就會覆蓋在原活動之上,當點選Back鍵或執行finish操作時,就會銷燬最上面的活動,下面的一個活動就會重新顯示出來。而Android是使用任務(Task)來管理活動的,一個任務就是一組存放在棧裡的活動的集合,這個棧又被稱作返回棧(Back Stack)。棧是一種後進先出的資料結構,在預設情況下,每當我們啟動了一個新的活動,它都會進入返回棧,然後處於棧頂的位置。

2.4.2 活動狀態

每個活動在其生命週期中最多可能會有4中狀態。

1. 執行狀態

當Activity處於返回棧的棧頂時,這時就處於執行狀態。系統最不願意回收處於執行狀態的活動。

2. 暫停狀態

當Activity不再處於棧頂位置,但仍然可見時,這時活動就會進入暫停狀態。只有在記憶體極低的情況下,系統才會去考慮回收這種活動。

3. 停止狀態

當Activity不再處於棧頂位置,且不可見時,這時活動就會進入停止狀態。系統仍然會為這種活動儲存相應的狀態和成員變數,但當其他地方需要記憶體時,處於停止狀態的活動有可能被系統回收。

4. 銷燬狀態

當Activity從返回棧中移除後,就是處於銷燬狀態。系統非常喜歡回收處於這種狀態的活動,從而來保證機器的記憶體充足。

2.4.3 活動的生存期

Activity中定義了7個回撥方法來闡述活動生命週期的每一個環節:

  • oncreate(),該方法在活動第一次被建立的時候呼叫,主要完成一些初始化操作。例如載入佈局、繫結事件等。

  • onStart() ,該方法在活動由不可見到可見的時候呼叫。

  • onResume() ,該方法是活動準備好和使用者進行互動的時候呼叫。此時活動一定處於棧頂,且是執行狀態。

  • onPause(),該方法在系統準備去啟動或恢復另一個活動的時候呼叫。在這個方法中我們一般會快速的釋放掉一些耗CPU的資源、儲存一些關鍵的資料。

  • onStop(),該方法在活動完全不可見時呼叫。與onPause()的主要區別是,如果新啟動的Activity是一個對話方塊式的活動,那麼onPause()會執行,onStop()不會執行。

  • onDestory(),該方法在活動被銷燬之前呼叫,之後的活動狀態變為銷燬狀態。

  • onRestart(),該方法在活動由停止狀態變為執行狀態之前呼叫,也就是活動被重新啟動。

上面7種方法除了onRestart()方法,其他的都是兩兩相對,從而又可以將活動分為3中生存期:

1. 完整生存期

onCreate() ——> onDestory()

onCreate()到onDestory()就是完整生存期。

2. 可見生存期

onstart() ——> onStop()

onstart()到onStop()就是可見生存期

3. 前臺生存期

onResume() ——> onPause()

onResume()到onPause()就是前臺生存期

為了更好的理解Activity的生命週期,Android官方提供了一張示意圖:

這裡寫圖片描述

2.4.4 體驗活動的生命週期

建立ActivityLifeCycleTest專案,新建NormalActivity和DialogActivity,佈局名分別為actvity_normal.xml和activity_dialog.xml

編輯actvity_normal.xml檔案:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="This is a normal Activity"/>

</LinearLayout>

編輯activity_dialog.xml檔案:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="This is a dialog Activity"/>

</LinearLayout>

到清單檔案中設定DialogActivity的主題:

<activity android:name=".DialogActivity" android:theme="@style/Theme.AppCompat.Dialog"/>

編輯activity_main.xml檔案

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<Button
    android:id="@+id/start_normal_activity"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Start Normal Activity" />

<Button
    android:id="@+id/start_dialog_activity"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Start Dialog Activity" />

</LinearLayout>

在佈局中加入兩個按鈕,分別用於啟動NormalActivity和DialogActivity。

修改MainActivity中的程式碼:

private static final String TAG = "MainActivity";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.d(TAG, "onCreate: ");
    setContentView(R.layout.activity_main);

    Button startDialogActivity = (Button) findViewById(R.id.start_dialog_activity);
    Button startNormalActivity = (Button) findViewById(R.id.start_normal_activity);

    startNormalActivity.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(MainActivity.this, NormalActivity.class);
            startActivity(intent);
        }
    });

    startDialogActivity.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(MainActivity.this, DialogActivity.class);
            startActivity(intent);
        }
    });
}

@Override
protected void onStart() {
    super.onStart();
    Log.d(TAG, "onStart: ");
}

@Override
protected void onResume() {
    super.onResume();
    Log.d(TAG, "onResume: ");
}

@Override
protected void onPause() {
    super.onPause();
    Log.d(TAG, "onPause: ");
}

@Override
protected void onStop() {
    super.onStop();
    Log.d(TAG, "onStop: ");
}

@Override
protected void onDestroy() {
    super.onDestroy();
    Log.d(TAG, "onDestroy: ");
}

@Override
protected void onRestart() {
    super.onRestart();
    Log.d(TAG, "onRestart: ");
}

在onCreate()方法中給兩個按鈕分別新增點選事件,點選第一個按鈕來啟動NormalActivity,點選第二個按鈕來啟動DialogActivity(),然後分別在7個回撥方法中列印log資訊,這樣就可以通過觀察log的方式來直觀地理解活動的生命週期。

apk安裝完成後,MainActivity啟動時,log資訊顯示:

D/MainActivity: onCreate: 
D/MainActivity: onStart: 
D/MainActivity: onResume:

點選第一個按鈕,啟動NormalActivity,log資訊顯示如下:

com.example.activitylifecycletest D/MainActivity: onPause: 
com.example.activitylifecycletest D/MainActivity: onStop: 

因為MainActivity已經被NoramlActivity完全遮蓋住了,所以會走onPause()和onStop()

然後按下Back鍵,回到MainActivity,log資訊顯示如下:

com.example.activitylifecycletest D/MainActivity: onRestart: 
com.example.activitylifecycletest D/MainActivity: onStart: 
com.example.activitylifecycletest D/MainActivity: onResume: 

然後再點選第二個按鈕,啟動DialogActivity,log資訊顯示如下:

com.example.activitylifecycletest D/MainActivity: onPause: 

從log中可以看出只執行了onPause()方法,onStop()並沒有執行,這是因為DialogActivity並沒有將MainActivity完全遮蓋住。此時MainActivity並沒有進入停止狀態,只是進入了暫停狀態。

按下Back鍵,log資訊如下:

com.example.activitylifecycletest D/MainActivity: onResume:

此時也只有onResume()方法執行。

最後在MainActivity介面按下Back鍵退出程式,log資訊顯示如下:

com.example.activitylifecycletest D/MainActivity: onPause: 
com.example.activitylifecycletest D/MainActivity: onStop: 
com.example.activitylifecycletest D/MainActivity: onDestroy: 

依次執行了onPause()、onStop()、onDestory(),最終銷燬MainActivity

2.4.5 活動被回收了怎麼辦

如果活動處於停止狀態時,就有可能因為記憶體不足被系統回收掉,這是我們就需要在回收之前儲存活動的一些臨時資料。Activity中提供了一個onSaveInstanceState()回撥方法,該方法可以保證在活動回收之前被呼叫。因此我們可以在該方法中儲存一些重要的臨時資料。

在MainActivity中新增如下程式碼:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    String tempData = "Something you just typed";
    outState.putString("data_key",tempData);
}

可以看到Bundle儲存資料也是通過鍵值對的形式儲存。

資料儲存下來了,那該從哪裡去取呢?我們發現onCreate()方法中有一個Bundle型別的引數,這個引數儲存了所有儲存的資料。我們可以通過相應的鍵來取出對應的值。

在MainActivity的onCreate()方法中取出值:

//獲取Bundle中儲存的資料
    if (savedInstanceState != null) {
        String tempData = savedInstanceState.getString("data_key");
        Log.d(TAG, tempData);
    }

2.5 活動的啟動模式

啟動模式共有4種,分別為standard、singleTop、singleTask和singleInstance。可以在AndroidManifest.xml中通過給標籤指定android:launchMode屬性來選擇啟動模式。

2.5.1 standard

standard又稱作“標準式”,在不顯式指定的情況下,所有的活動都是使用這種啟動模式。使用standard模式的活動,系統不會在乎這個活動是否已經在返回棧中存在,每次啟動活動都會建立該活動的一個新例項。

開啟ActivityTest專案,修改FirstActivity中的onCreate()方法:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.d(TAG, "Task id is " + getTaskId());
    setContentView(R.layout.first_layout);
    Button button1 = (Button) findViewById(R.id.button_1);
    button1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(FirstActivity.this, FirstActivity.class);
            startActivity(intent);
        }
    });
}

在FirstActivity的基礎上啟動FirstActivity,並新增一條列印資訊,用於列印當前活動的例項。連續點選兩次按鈕,可以看到logcat中列印資訊如下所示:

[email protected]
[email protected]
[email protected]

可以看出每點選一次就會建立一個新的FirstActivity例項。此時返回棧中就有3個FirstActivity例項,因此我們需要連續按3此Back鍵才能退出程式。

2.5.2 singleTop

singleTop又稱作“棧頂複用式”,就是在啟動活動時,如果發現返回棧的棧頂已經是該活動了,那麼就直接使用它,不會再建立新的例項。

修改AndroidManifest.xml中的FirstActivity的啟動模式,如下所示:

<activity
    android:name=".FirstActivity"
    android:launchMode="singleTop"
    android:label="This is FirstActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

執行程式,可以看到logcat資訊如下:

com.example.activitytest/u0a63 for activity com.example.activitytest/.FirstActivity

之後無論你點選多少次都不會再有新的列印資訊出現。因為此時FirstActivity處於棧頂的位置,不會再建立新的例項。但是當FirstActivity不處於棧頂的位置時,這時再啟動FirstActivity是會建立新的例項的。

2.5.3 singleTask

singleTask又稱作“棧內複用式”,每次啟動活動時都會先在返回棧中檢查是否存在該活動的例項,如果發現已經存在則直接使用,並把這個活動之上的所有活動統統出棧,如果沒有發現就會建立一個新的活動例項。

修改AndroidManifest.xml中的FirstActivity的啟動模式,如下所示:

<activity
    android:name=".FirstActivity"
    android:launchMode="singleTask"
    android:label="This is FirstActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

並在FirstActivity中新增onRestart()方法:

@Override
protected void onRestart() {
    super.onRestart();
    Log.d(TAG, "onRestart: ");
}

在SecondActivity中新增onDestroy()方法:

@Override
protected void onDestroy() {
    super.onDestroy();
    Log.d(TAG, "onDestroy: ");
}

執行程式,在FirstActivity中點選進入SecondActivity,然後在SecondActivity中點選回到FirstActivity,檢視logcat中的列印資訊:

[email protected]
[email protected]
com.example.activitytest D/FirstActivity: onRestart: 
com.example.activitytest D/SecondActivity: onDestroy:

從列印資訊中可以看出,SecondActivity在啟動 FirstActivity時,發現返回棧中已經有一個FirstActivity的例項,並且是在SecondActivity下面,此時SecondActivity就會出棧,讓FirstActivity重新處於棧頂的位置。因此FirstActivity中的onRestart和SecondActivity中的onDestroy都會執行,現在任務棧中只剩一個FirstActivity例項,按一下Back鍵就可以退出程式。

2.5.4 singlestance

singlestance又稱作“單例式”。指定為singlestance模式的活動會啟用一個新的返回棧來管理這個活動,有一個單獨的返回棧來管理這個活動,不管是哪個應用程式來訪問這個活動,都共用同一個返回棧,從而達到了共享活動例項的目的。

修改AndroidManifest.xml中的SecondActivity的啟動模式,如下所示:

<activity android:name=".SecondActivity"
    android:launchMode="singleInstance">
    <intent-filter>
        <action android:name="com.example.activitytest.ACTION_START" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="com.example.activitytest.MY_CATEGORY" />
    </intent-filter>
</activity>

將SecondActivity的啟動模式指定為singleInstance。

分別修改FirstActivity、SecondActivity、ThirdActivity中onCreate()方法中的log列印資訊:

Log.d(TAG, "Task id is " + getTaskId());

修改程式碼,在FirstActivity中啟動SecondActivity,在SecondActivity中啟動ThirdActivity,檢視logcat中的列印資訊,如下所示:

com.example.activitytest D/FirstActivity: Task id is 153
com.example.activitytest D/SecondActivity: Task id is 154
com.example.activitytest D/ThirdActivity: Task id is 153

從logcat資訊中可以看出,SecondActivity的任務棧id與FirstActivity和ThirdActivity均不同,所以SecondActivity確實是放在一個單獨的返回棧中。

2.6 活動的最佳實踐

2.6.1 知曉當前是在哪一個活動

在ActivityTest專案中新建一個BaseActivity類。右擊com.example.activitytest包——>New——>Java Class,BaseActivity不需要在AndroidManifest.xml中註冊,所以選擇建立一個普通的Java類就可以了。然後讓BaseActivity繼承AppCompatActivity,並重寫onCreate()方法:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.d("BaseActivity", getClass().getSimpleName());
}

然後分別讓FirstActivity、SecondActivity、ThirdActivity繼承BaseActivity,而不是直接繼承AppCompatActivity。

執行程式,通過點選按鈕分別進入FirstActivity、SecondActivity、ThirdActivity的介面,檢視logcat中的列印資訊:

com.example.activitytest D/BaseActivity: FirstActivity
com.example.activitytest D/BaseActivity: SecondActivity
com.example.activitytest D/BaseActivity: ThirdActivity

每當我們進入到一個活動的介面,該活動的類名就會被打印出來,這樣我們就可以時時刻刻知曉當前介面對應的是哪一個活動了。

2.6.2 隨時隨地退出程式

使用一個集合類對所有的活動進行管理,這樣就可以實現程式隨時隨地退出。

新建一個ActivityCollector類作為活動管理器:

public class ActivityCollector {

    public static List<Activity> activities = new ArrayList<>();

    public static void addActivity(Activity activity) {
        activities.add(activity);
    }

    public static void removeActivity(Activity activity) {
        activities.remove(activity);
    }

    public static void finishAll() {
        for (Activity activity : activities) {
            if (!activity.isFinishing()) {
                activity.finish();
            }
        }
        activities.clear();
    }
}

定義一個addActivity()方法來向List中新增一個活動;

定義一個removeActivity()方法用於從List中移除活動;

定義一個finishAll()方法將List中儲存的活動全部銷燬掉。

修改BaseActivity中的程式碼:

public class BaseActivity extends AppCompatActivity {

    private static final String TAG = "BaseActivity";
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, getClass().getSimpleName());
        ActivityCollector.addActivity(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ActivityCollector.removeActivity(this);
    }
}

在onCreate()方法中呼叫addActivity()方法,表示將當前正在建立的活動新增到活動管理器中;重寫onDestroy()方法,並呼叫removeActivity()方法,表示將一個馬上要銷燬的活動從活動管理器裡移除。

在任何地方想要退出程式,只需要呼叫ActivityCollector.finishAll()方法。例如想在ThirdActivity介面通過點選按鈕直接退出程式,則只需要新增如下程式碼:

Button button3 = (Button) findViewById(R.id.button_3);
    button3.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            ActivityCollector.finishAll();
        }
    });

你還可以在銷燬所有活動的程式碼後面加上殺掉當前進行的程式碼,以保證程式完全退出,殺掉程序的程式碼如下所示:

android.os.Process.killProcess(android.os.Process.myPid());

其中,killProcess()方法用於殺掉一個程序,它接收一個程序id引數,可以通過myPid()方法獲取當前程序的id。需要注意的是,killProcess()方法只能用於殺掉當前程式的程序,不能使用這個方法殺掉其他程式。

2.6.3 啟動活動的最佳寫法

通常情況下我們啟動一個活動的寫法如下:

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("param1", data1);
intent.putExtra("param2", data2);       
startActivity(intent);

但如果我們不知道啟動SecondActivity需要傳遞哪些資料?那我們就需要去看SecondActivity的原始碼或問開發SecondActivity的同事,這樣就顯示比較繁瑣。

為了優化這種問題,我們只需要在開發SecondActivity時就定義一個啟動方法:

public static void actionStart(Context context, String data1, String data2) {
    Intent intent = new Intent(context, SecondActivity.class);
    intent.putExtra("parm1", data1);
    intent.putExtra("parm2", data2);
    context.startActivity(intent);
}

直接在SecondActivity方法中定義一個actionStart()方法,將需要的資料通過引數傳遞過來,然後將它們儲存在Intent中,最後再呼叫startActivity()方法啟動SecondActivity。

這樣我們在其他地方啟動SecondActivity就很方便:

SecondActivity.actionStart(FirstActivity.this,"data1","data2");

2.7 小結與點評

本章主要學習了活動的基本用法,到啟動活動和傳遞資料的方式,再到活動的生命週期,以及活動的啟動模式等。

相關推薦

第一程式碼學習筆記第二——探究活動

知識點目錄 知識點回顧 2.1 活動是什麼 是一種包含使用者介面的元件,主要用於和使用者進行互動。 2.2 活動的基本用法 Android Studio一個工作區間只允許開啟一個專案,故點選導航欄File –> Close

第一程式碼 學習筆記 後臺的默默勞動者--探究服務

10.1 服務是什麼10.2 Android多執行緒程式設計定義執行緒:新建一個類繼承自Thread,然後重寫父類的run()方法,如下所示:class MyThread extends Thread{      public void run(){        //處理具

第一程式碼學習筆記——Material Design實戰(1)

Toolbar 在MD設計中,用ToolBar去取代ActionBar,首先要去style.xml中設定隱藏ActionBar <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar

第一程式碼學習筆記:關於BroadcastReceiver的實現方式

最近在學習郭霖大神的第一行程式碼第二版,在進行自定義廣播部分的例項練習時發現點選按鈕後未能接收到廣播訊息,經過程式碼排查和度娘查詢才知道原來是8.0版本開始已經不支援大部分的靜態註冊廣播了,然後結合網友們提供的方法並進行測試總結下: 方法1 動態註冊 既然靜態

第一程式碼學習筆記---學習任務清單與列表

關於安卓學習任務清單—日期與完成時間 第一階段–java語言 任務 完成日期 部落格記錄 資料參考 其他備註 第二階段–android核心技術 任

第二程式碼學習筆記——第十:後臺默默的勞動者——探究服務

本章要點 Android沿用了諾基亞系統的Symbian作業系統的老習慣,從一開始就支援後臺功能,這使得應用程式即使在關閉的情況下仍然可以在後臺繼續執行。後臺功能屬於四大元件之一,重要程度言不可寓。 10.1 服務是什麼 服務(Service)是A

第二程式碼學習筆記——第一:開始啟程——你的第一行Android程式碼

筆者前言 最近在讀郭霖大神的第二行程式碼,藉助第二行程式碼,在這裡我認真梳理Android知識,為了形成自己的知識體系。堅持寫一系列關於第二行程式碼的學習筆記,一是來提升自己的學習能力,堅持每天學習; 二是給自己整理一份資料,方便以後的查閱與複習。希望能夠幫助

第二程式碼學習筆記——第四:手機平板要相容——探究碎片

本章要點 作為一名專業的Android開發人員,能夠同時相容手機和平板的開發時我們必須要做到的事情。 4.1 碎片是什麼 碎片(Fragment)是一種可以巢狀在活動當中的UI片段,它能讓程式更加合理和充分的利用大螢幕的控制元件。 4.2

第二程式碼學習筆記——第三:軟體也要拼臉蛋——UI開發的點點滴滴

本章要點 使用Android提供的UI來編寫程式介面。本章的內容就是學習UI方面的知識。 3.1 如何編寫程式介面 Android種編寫程式介面的方式: 1. 視覺化編輯器(不推薦) 2. 編寫XML程式碼(推薦) 3.2 常用控制元

第二程式碼學習筆記——第六:資料儲存全方案——詳解持久化技術

本章要點 任何一個應用程式,總是不停的和資料打交道。 瞬時資料:指儲存在記憶體當中,有可能因為程式關閉或其他原因導致記憶體被回收而丟失的資料。 資料持久化技術,為了解決關鍵性資料的丟失。 6.1 持久化技術簡介 資料持久化技術:指那些記憶體中的瞬時

第一程式碼》 第五:全域性大喇叭 筆記(基於Android8.0)

由於Android8.0對廣播機制做了很大的調整理,導致《第一行程式碼》中很多例項無法正常執行,因此我結合書本,自行整理了一下。 廣播需要接收器和傳送器。系統的動作都會發送一條廣播,例如電量的變化,系

學習第一程式碼coolweather專案第二階段的開發工作遇到的瓶頸

     模擬器執行專案之後,介面顯示“正在載入”且無限迴圈,剛開始以為需要一定的時間才能反應,結果並不是,後面我以為是版本的原因,將android studio升級到3.0.1,發現這是一個巨大無比的坑,中間遇到無數的問題,最終只能重灌電腦然後重新下載Android stu

Android《第一程式碼》第5 筆記

第五章主要介紹了Android中的廣播機制。 Android中的每個應用程式都可以對自己感興趣的廣播進行註冊。Android允許應用程式自由地傳送和接收廣播。可以通過Intent傳送廣播,通過廣播接收器(Broadcast Receiver)來接收廣播。 廣播型別分為標準廣

《呂鑫:VC++6.0就業培訓寶典之MFC視頻教程》學習筆記 -- 第二 MFC原理介紹

第一個 寶典 數據類型 對話 視頻 資源管理 bsp 程序開發 第二章 第二章 MFC原理介紹 2.1 第一個Win32軟件 2.2 Win32對話框程序開發 2.3 程序資源管理和Windows數據類型 2.4 Win32環境下的多對話框管理 2.5 初步學習MFC軟件

第一代碼Android-------第二控件部分

.text turn emc count rst size item hold build 一、控件 1、大小     match_parenr:與父布局大小一樣     fill_parent:與match_parent一樣     wrap_content:控件大小剛好

網絡是怎樣連接的學習筆記-第二-連接服務器

開頭 串操作 tcp con com png 還需要 什麽 如果 2.2 連接服務器 2.2.1 連接是什麽意思 連接時發生了什麽 客戶端創建套接字告知服務器我要通信 創建套接字之後,應用程序就會調用 connect。隨後協議棧會將本地的套接字與服務器的套接字進行連接。 在

網路是怎樣連線的學習筆記-第二-收發資料(上)

2.3 收發資料 2.3.1 將 HTTP 請求訊息交給協議棧 當控制流程從 connect 回到應用程式之後,接下來就進入資料收發階段了。 資料收發操作是從應用程式呼叫 write 將要傳送的資料交給協議棧開始的,協議棧收到 資料後執行傳送操作,這一操作包含如下要點。 協議棧並不關心應用程式傳來的

網路是怎樣連線的學習筆記-第二-從伺服器斷開並刪除套接字

2.4 從伺服器斷開並刪除套接字 2.4.1 資料傳送完畢後斷開連線 收發資料結束的時間點應該是應用程式判斷所有資料都已經發送完畢的時候。 這時,資料傳送完畢的一方會發起斷開過程,但不同的應用程式會選擇不同的斷開時機。 以 Web 為例,瀏覽器向 Web 伺服器傳送請求訊息,Web 伺服器再

網路是怎樣連線的學習筆記-第二-IP與乙太網的包收發操作(二)

2.5.3 生成包含接收方 IP 地址的 IP 頭部 IP頭部包含的內容 IP 模組接受 TCP 模組的委託負責包的收發工作,它會生成 IP 頭部並附加在 TCP 頭部前面。 IP 頭部包含的內容如表 2.2 所示,其中最重要的內容就是 IP 地址,它表示這個包應該發到哪裡去。 接受方IP地址:應用程

網絡是怎樣連接的學習筆記-第二-IP與以太網的包收發操作(四)

出現 init 信號 height 這樣的 介質 操作系統 初始化 關於 2.5.9 向集線器發送網絡包 發送信號的半雙工和全雙工模式 加上報頭、起始幀分界符和 FCS 之後,我們就可以將包通過網線發送出去了。 發送信號的操作分為兩種,一種是使用集線器的半雙工模式,另一種是