Android四大元件——Activity——Activity的生命週期

虞美人體重90發表於2022-04-17

Activity狀態:

每個Activity在其生命週期中最多可能有四種狀態

1.執行狀態:處於棧頂時。初次建立處於棧頂時依次呼叫:onCreate(),onStart(),onResume()。由不可見重新處於棧頂時依次呼叫:onRestart(),onStart(),onResume()。由可見重新處於棧頂時呼叫:onResume()

2.暫停狀態:可見卻不在棧頂。例如在MainActivity中啟動一個對話方塊式Activity時。該對話方塊式Activity處於棧頂,但MainActivity仍然可見,只是MainActivity不處於棧頂。呼叫onPause()。如下圖:

3.停止狀態:不可見並且不在棧頂時。例如在MainActivity中啟動一個普通的Activity時,該Activity會徹底覆蓋MainActivity,致使MainActivity不可見,這時MainActivity便進入了停止狀態。依次呼叫onPause(),onStop()

4.銷燬狀態:從棧中移除後。例如在MainActivity介面按下返回鍵,這時是將MainActivity從棧頂移除,這樣被移除後的MainActivity就成了銷燬狀態。依次呼叫onPause(),onStop(), onDestroy()

 

 

 onCreate():該方法會在Activity第一次被建立的時候呼叫。我們一般都會重寫此方法,完成一些Activity的初始化操作,如載入佈局,繫結事件等。該方法使得Activity建立。

 onStart():該方法在Activity由不可見變為可見時呼叫。使得Activity可見。

 onResume():該方法在Activity準備好和使用者進行互動時呼叫。此時的Activity一定返回棧的棧頂,並且處於執行狀態。使得Activity獲取焦點,可以進行操作(互動)

 onPause():該方法在系統準備去啟動或者恢復另一個Activity的時候呼叫,我們通常會在這個方法中將一些消耗CPU的資源釋放掉,以及儲存一些關鍵資料,但這個方法的執行速度一定要快,不然會影響到新的棧頂Activity的使用。使得Activity失去焦點,不能互動。

 onStop():該方法在Activity完全不可見的時候呼叫。它和onPause()方法的主要區別在於,如果啟動一個新的Activity是一個對話方塊式的Activity,那麼onPause()方法會得到新的執行,而onStop()方法並不會執行。使得Activity不可見

 onDestroy():該方法在Activity被銷燬之前呼叫,之後Activity的狀態將變為銷燬狀態。使得Activity銷燬。

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

1.建立三個Activity:MainActivity , AActivity(普通的Activity) , BActivity(對話方塊式Activity)

怎麼將 BActivity設成對話方塊式?在AndroidManifest.xml中的<.activity>標籤中

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

通過android:theme="@style/Theme.AppCompat.Dialog"指定當前Activity主題為對話方塊式。

2.修改MainActivity的程式碼:

package com.java.androidstudy;

import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {
    private Button normal_btn;
    private Button dialog_btn;
    private String tag = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(tag,"onCreate");
        setContentView(R.layout.activity_main);
        initView();
        normal_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this,AActivity.class);
                startActivity(intent);
            }
        });

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

    private void initView(){
        normal_btn = findViewById(R.id.a_btn);
        dialog_btn = findViewById(R.id.b_btn);
    }

    @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()");
    }

}

點選執行後,如下圖:

 

 

 此刻觀看日誌:

 

 

 可以發現當MainActivity第一次被建立時會依次執行onCreate(),onStart(),onResume()

 

點選第一個按鈕:啟動普通的Activity,這時會啟動AActivity,該AActivity入棧,位於棧頂,會將MainActivity完全遮擋住,因此onPause() 和 onStop()都會得到執行。

 

 

 檢視日誌:

 

 

 依次執行了onPause() 和 onStop()

 

點選退出

之前被遮擋住的MainActivity此時處於停止狀態,點選退出時,AActivity出棧,MainActivity重新處於棧頂,由停止狀態變為執行時狀態。所以先呼叫onRestart(),之後會依次執行onStart()和onResume()。注意,此時onCreate()方法不會執行,因為MainActivity沒有重新建立。

 

 

 

 

點選第二個按鈕:啟動對話方塊式Activity。如下圖:

觀察日誌:

 

 

  此時只執行了onPause(),沒有執行onStop()方法。這是因為對話方塊式的BActivity並沒有完全遮擋住MainActivity,此時的MainActivity只是進入了暫停狀態。(之前啟動AActivity時,MainActivity進入了停止狀態。),相應的,按下返回鍵,也只有onResume()方法會得到執行。如下:

 

 

最後在MainActivity介面按下返回鍵,此時日誌資訊如下:

 

依次執行onPause(),onStop(),onDestroy().銷燬MainActivity.

 

 

具體實踐:

1.onCreate()和onDestory()的使用

onCreate():第一次建立Activity的時候呼叫

onDestory():銷燬Activity的時候呼叫

  修改MainActivity的佈局:新增兩個輸入。

 現在的輸入框,若是我輸入到一半,突然退出,再次進入就要重新輸入。但是我不想那麼麻煩,我希望輸入框能自動顯示上次退出前的內容。

分析:在Activity銷燬前將輸入框中的內容儲存,再次啟動Activity時將儲存的內容顯示到輸入框。(l類似於記住密碼功能)

Activity銷燬前儲存資料:需要在onDestroy()中操作

Activity再次啟動時顯示資料:需要在onCreate中操作

 

程式碼如下:

package com.java.androidstudy;

import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {
    private Button normal_btn;
    private Button dialog_btn;
    private String tag = "MainActivity";
    private EditText user;
    private EditText password;

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

    }

    private void initView(){
        normal_btn = findViewById(R.id.a_btn);
        dialog_btn = findViewById(R.id.b_btn);
        user = findViewById(R.id.user);
        password = findViewById(R.id.password);
    }

    private void displayinfo(){
        String content_user = getSharedPreferences("myinfo",0).getString("user","");
        String content_pwd = getSharedPreferences("myinfo",0).getString("pwd","");
        user.setText(content_user);
        password.setText(content_pwd);
    }

    @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()");
        //獲取輸入的內容
        String content_user = user.getText().toString();
        String content_password = password.getText().toString();

        //使用SharedPerences儲存資料
        SharedPreferences.Editor editor = getSharedPreferences("myinfo",0).edit();
        editor.putString("user",content_user);
        editor.putString("pwd",content_password);
        editor.apply();
    }

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

}

 執行效果如下:

 

 會自動將上次退出時的內容顯示出來。

2.onStart()和onStop的使用

onStart():此時可見,沒有焦點,也就是不可以進行操作

onStop():此時不可見。沒有焦點

 

package com.java.androidstudy;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
/*
有這樣一個需求:當正在看電影的時候,突然打來電話,如果接聽電話,而電影也在播放,雜音十分影響使用感。
        現在需要接聽電話時電影自動暫停,結束通話電話後電影繼續播放。 分析:接電話時會依次執行onPause()和onStop()方法,所以我們可以在onStop()方法中暫停電影 結束通話電話回答該介面時會依次執行onRestart(),onStart(),onResume。我們可以在onStart()中繼續播放電影
*/ public class AActivity extends AppCompatActivity { private static final String TGA = "AActivity"; private TextView play; private Button play_control; private boolean isPlay = false; private boolean isStopAtAmin = false;//是否是因為生命週期的變化而主動停止的 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_a); Log.d(TGA,"onCreate"); initView(); initListener(); } private void initView() { play = findViewById(R.id.play); play_control = findViewById(R.id.play_control); } private void initListener(){ play_control.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(isPlay){//如果當前狀態是播放的,點選該按鈕後就暫停播放,並將按鈕上的文字顯示為播放 stop(); }else { //如果當前狀態是暫停的,點選該按鈕後變為播放狀態,並將按鈕上的文字顯示為暫停 play(); } } }); } private void stop() { Log.d(TGA,"暫停電影"); play.setText("電影已經暫停!!!"); play_control.setText("播放"); isPlay = false; } private void play() { Log.d(TGA,"播放電影"); play.setText("正在播放電影!!!"); play_control.setText("暫停"); isPlay = true; } @Override protected void onStart() { super.onStart(); Log.d(TGA,"onStart()"); if (isStopAtAmin){ //如果是因為生命週期結束停止的,在重新回到棧頂時播放電影 play(); isStopAtAmin = false; } } @Override protected void onStop() { super.onStop(); Log.d(TGA,"onStop() "); if (isPlay){//如果當前是播放的,那麼我們需要將這個電影停掉。 stop(); isStopAtAmin = true; } } @Override protected void onResume() { super.onResume(); Log.d(TGA,"onResume()"); } @Override protected void onPause() { super.onPause(); Log.d(TGA,"onPause()"); } @Override protected void onRestart() { super.onRestart(); Log.d(TGA,"onRestart()"); } @Override protected void onDestroy() { super.onDestroy(); Log.d(TGA,"onDestroy()"); } }

 

執行如下:

 

 點選播放電影:

 

 此刻接聽電話:右下圖,可以發現依次執行了onPause(),onStop()。然後暫停電影

 

 此刻結束通話電話:可以看到依次執行了onRestart()onStart(),onResume()。並在start()方法中自動播放電影。

 

 3.onResume() 和 onPause()

onResume():onstart()可見之後在這裡獲取到焦點,可以進行操作。

onPause():失去焦點,不可以操作。

當從FirstActivity跳轉到SecondActivity後,SecondActivity至於棧頂處於可見狀態,而FirstActivity進入停止狀態(如果SecondActivity是對話方塊式的Activity,則FirstActivity進入暫停狀態),在SecondActivity介面點選返回鍵後,SecondActivity出棧(銷燬),這時FirstActivity重新處於棧頂,執行onRestart()方法,再依次執行 onStart(),onResume()。

注意:這裡不止對話方塊式Activity(透明主題也可以),只要FirstActivity仍然可見,就不會執行onStop()方法使之進入停止狀態(該狀態不可見),而是隻執行onPasue()方法使之進入暫定狀態(該狀態失去焦點,不可點選)

程式碼:

 

package com.java.androidstudy;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class FirstActivity extends AppCompatActivity {
    private static final String TGA = "FirstActivity";
    private  Button btn_first;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_b);
        Log.d(TGA,"onCreate");
        initView();
        initListener();
    }

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

    private void initView() {
        btn_first = findViewById(R.id.btn_first);
    }

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

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

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

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

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

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

 

然後將SecondActivity的主題改成透明主題:

        <activity android:name=".SecondActivity"
            android:theme="@android:style/Theme.Translucent">
        </activity>

執行效果:

 

 點選按鈕後:

 

 可以看到第二個頁面彈出來了,但是第一個頁面仍然可見,只是失去了焦點,不能點選。

看看日誌:

 FirstActivity只是進入了暫停狀態,失去焦點。

點選返回鍵,使得SecondActivity銷燬。檢視日誌:

這時FirstActivity重新進入棧頂,獲取焦點。

 

橫豎屏切換Activity生命週期的變化

 MainActivity:

package com.java.androidstudy;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;


public class MainActivity extends AppCompatActivity {
    private String tag = "MainActivity";

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

    @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()");
    }

}

執行後:

 

 檢視日誌:

現在旋轉成橫屏:

 

 再來看看完整的生命週期變化:

 

 紅框內的為橫屏後的變化。

可以看到:在點選橫屏後,是先將原Activity銷魂,再建立新的Activity

再將其從橫屏切換為豎屏:

 

 與豎屏切換橫屏一樣,都會先銷燬Activity再重新建立。

 

橫豎屏切換的問題:

如:將看到一半的電影切換成橫屏,因為切換後是建立新的Activity,所以會造成進度條清零,需要手動去拉進度條,這種體驗無疑是極差的

如:

package com.java.androidstudy;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.SeekBar;


public class MainActivity extends AppCompatActivity {
    private String tag = "MainActivity";
    private SeekBar progress;

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

    }

    private void initView() {
        progress = findViewById(R.id.progress);
        progress.setMax(100);
        progress.post(new Runnable() {
            @Override
            public void run() {
                progress.setProgress(0); //每次初始化的時候進度條清零
            }
        });
    }

    @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()");
    }

}

 

切換前:

 

 此時進度條到一半。

切換成橫屏:

 

 可以看見進度條自動清零了。

 

怎麼解決:

第一種:禁止螢幕旋轉,指定螢幕方向。(適合只有一種螢幕狀態的應用開發配置)

        <activity android:name=".MainActivity"
            android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 android:screenOrientation="landscape"  //指定為橫屏
 android:screenOrientation="portrait"   //指定為豎屏

這樣指定後的橫豎屏切換不會銷燬原來的Activity再建立新的Activity。

第二種:設定配置資訊改變不影響。(適合有兩種/多種螢幕狀態的應用開發,如:視訊播放器)
        <activity android:name=".MainActivity"
            android:configChanges="keyboardHidden|screenSize|orientation">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 android:configChanges="keyboardHidden|screenSize|orientation"   //對鍵盤隱藏,螢幕大小,螢幕方向都不敏感
當其中一個發生變化時,生命週期不受影響。

相關文章