帶你從理論到代碼學習Activity

    從今天開始,將不定時給大家更新“帶你從理論到代碼學習XX”系列,顧名思義,就是對安卓開發的某塊點進行講解,從它主要的理論知識到最後使用它開發一個小實例來講解,慢慢循序漸進,闖關下去。廢話不多說,馬上進入主題。

    

1. Activity的理論知識

作爲經常打交到的組件之一,xml寫完佈局,然後就在Activity裏進行邏輯處理,雖然簡單,但或者這部分理論知識(生命週期、啓動模式和顯式/隱式啓動Activity)能讓你有“原來是這樣”的豁然開朗之感,也可能對你面試有幫助。

 

生命週期

Activity的生命週期要說的其實就是三點:正常情況下的生命週期,異常情況下的生命週期,還有它的一個各個生命週期方法的流程圖說明。

(1)正常的生命週期

    正常情況下的生命週期沒什麼好說的,就是:onCreate()--onStart()--onResume()--onPause()--onStop()--onDestroy()

 

(2)異常的生命週期

    首先明確,什麼纔是異常的情況,那就是當一些資源相關的系統配置發生改變(比如旋轉屏幕、調出打字鍵盤等)或者系統內存不足導致優先級很低的Activity被關閉,這兩種情況的發生都會讓Activity處於異常的生命週期。

    異常情況下的生命週期如下圖所示:

    相比正常的生命週期,出現了兩個不一樣的方法,onSaveInstanceState()和onRestoreInstance()。onSaveInstanceState()只在異常終止時執行,在onStop()前調用,保存當前Activity的狀態(視圖結構,比如TextView的名字內容和選中位置等)。而onRestoreInstance()則是在Activity重新創建時進行恢復這些狀態數據,在onStart()後調用。onSaveInstanceState()和onRestoreInstance()用法如下:

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

        /**
         * 也可以在onCreate()方法裏進行恢復數據,
         * 但要先判斷bundle是否爲空
         */
        if (savedInstanceState != null) {
            String data = savedInstanceState.getString("data");
            Log.d(TAG, "onCreate: " + data);
        }

    }

    /**
     * 保存數據
     * @param outState
     */
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("data", "數據");

    }

    /**
     * 還是建議調用onRestoreInstanceState來恢復比較好
     * @param savedInstanceState
     */
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        String data = savedInstanceState.getString("data");
        Log.d(TAG, "onRestoreInstanceState: " + data);
    }

    那麼,如果想要在異常情況下不讓Activity重新創建的話,可以設置android:configChanges屬性,它的值跟含義如圖所示:

    至於說到的Activity的優先級則是:前臺Activity(正在和用戶進行交互的Activity),優先級是最高的;可見但非前臺Activity(運行中的ActivityA彈出對話框形式的ActivityB,致使當前ActivityA可見但不能與用戶交互),優先級次之;後臺Activity則是優先級最低的。

    

(3)生命週期的各個方法之間的切換

    直接上圖:

   

相信已經很熟悉了,不用我多說了,還有不明白的自行去搜索解決吧。

 

啓動模式

Activity的啓動模式也是老生常談了,standard(標準模式)、singleTop(棧頂複用模式)、singleTask(棧內複用模式)和singleInstance(單實例模式)。

 

(1)這裏說說singleTask,因爲它還與taskAffinity屬性密切相關。taskAffinity屬性相當於一個棧名,singleTask模式下活動被啓動時,最先開始有一個任務棧匹配的過程,就是先根據taskAffinity值來找到該活動需要的任務棧,如果有該棧,則直接使用,沒有則創建一個新的棧。接着在該棧中再來尋找是否存在活動實例。如果不指定taskAffinity值,則默認爲taskAffinity值爲包名。

 

(2)指定啓動模式有兩種方式:

    intent.addFlags(intent, Flags標誌位)或者android:lanuchMde="啓動模式"。使用addFlags的方式優先級比xml的高,但常用的方式還是xml的設置方式。常用的標誌位有:

     

啓動Activity

(1)顯示啓動

    非常簡單,需明確指定啓動對象的組件信息:

    intent.setClass(this,XXX.class)

 

(2)隱式啓動

    隱式啓動稍微複雜一點,需要Intent匹配目標組件的IntentFilter中設置的過濾信息(action、category和data)。

    action:Intent中的action必須存在,且必須和IntentFilter中的其中一個action相同;

 

    category:Intent中可不設置category,但如果設置了,不管有幾個,每個都要與IntentFilter中的相同;爲什麼Intent不設置category也可以成功啓動,是因爲系統在調用startActivity()或者startActivityForResult時默認爲Intent加上“android.intent.category.DEFAULT”這個category,所以你的IntentFilter裏當然也要設置好DEFAULT這個category。

 

    data:Intent的data必須和IntentFilter中的某一個data相同。data由mineType和URI兩部分組成。mimeType表示媒體類型,比如:image/*、audio/*、video/*等等。URI包含多個數據,它的結構如下:

    <scheme>://<host>:<post>/[<path>|<pathPrefix>|<pathPattern>]

     

    scheme:URI模式,比如http、file和content,如果沒有設置scheme,URI無效;

    host:URI主機名,如果host沒指定,URI無效;

    port:URI端口號;

    path:完整路徑信息;

    pathPrefix:路徑的前綴信息;

    pathPattern:完整路徑信息,但它裏面可包含"*",表示0個或多個任意字符;

 

    IntentFilter的設置如下:

    

 注意:

    隱式啓動可能會因匹配不到合適的Activity而報錯,所以要提前判斷是否存在相匹配的Activity,方法有:PackageManager.resolveActivity()與PackageManager.queryIntentActivity()以及Intent.resolveActivity()。

    設置data時,IntentFilter中如果沒有定義URI,則默認爲content或者file,所以Intent要設置conten或者file:intent.setDataAndType(Uri.parse("file://abc"),"image/png")。

這樣Activity的理論知識就講完了,理論懂了就得開始寫代碼了,這纔是重中之重。

 

實現一個登錄頁面且跳轉到主頁面

登錄頁LoginActivity.java代碼:

public class LoginActivity extends AppCompatActivity {

    private LinearLayout loginBtn;

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

        loginBtn = findViewById(R.id.login_btn);
        loginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.activity1");
                intent.addCategory("com.example.activity1");
                intent.addCategory("android.intent.category.DEFAULT");//可加可不加

                startActivity(intent);
            }
        });
    }
}

 

可以看到在Intent中設置了action,category和data,其中DEFAULT的category不設置也是可以的。

登錄頁佈局文件activity_login.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp"
    android:paddingBottom="16dp"
    android:background="#ffeeeeee"
    android:orientation="vertical"
    tools:context=".LoginActivity">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

    <ImageView
        android:id="@+id/logo_iv"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:src="@drawable/movie"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_gravity="center_horizontal" />

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/login_circle_layout"
        android:layout_marginTop="20dp">

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <ImageView
                android:id="@+id/user_iv"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:src="@drawable/user"
                android:layout_margin="5dp"
                android:layout_gravity="center_vertical" />

            <EditText
                android:id="@+id/et_user"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="@null"
                android:layout_gravity="center_vertical"
                android:hint="手機號/郵箱/用戶名" />
        </LinearLayout>

        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#ffeeeeee"/>

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <ImageView
                android:id="@+id/pwd_iv"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:src="@drawable/pwd"
                android:layout_margin="5dp" />

            <EditText
                android:id="@+id/et_pwd"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:inputType="textPassword"
                android:ems="10"
                android:layout_weight="1"
                android:background="@null"
                android:layout_gravity="center_vertical"
                android:hint="登錄密碼" />
        </LinearLayout>

    </LinearLayout>

    <LinearLayout
        android:id="@+id/login_btn"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/login_btn_press"
        android:layout_marginTop="20dp">

        <TextView
            android:id="@+id/tv_login"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="登錄"
            android:layout_gravity="center_horizontal"
            android:layout_margin="10dp"
            android:textSize="18dp"
            />
    </LinearLayout>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="6">
    </LinearLayout>

</LinearLayout>

    這基本上是寫一個登錄頁面的套路了,效果如下圖:

    最後就是AndroidManifest.xml文件,因爲我們是要LoginActivity跳轉到MainActivity,所以MainActivity的IntentFilter要跟LoginActivity裏設置的Intent相匹配:

	<activity android:name=".MainActivity">

           <intent-filter>
               <action android:name="com.example.activity1"/>
               <category android:name="com.example.activity1"/>
               <category android:name="android.intent.category.DEFAULT" />

           </intent-filter>
    </activity>

 代碼很簡單,很容易理解,最後運行代碼,成功跳轉。

 

 

                                                                                  掃一掃  關注我的公衆號

                                                                           感興趣獲取源碼就關注公衆號吧!

                                                                           歡迎大家來關注,一起學習安卓!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章