TShopping

 找回密碼
 註冊
搜索
查看: 10027|回復: 0
打印 上一主題 下一主題

[教學] Activity你應該知道的一切(activity finish 要兩次問題)

  [複製鏈接]
跳轉到指定樓層
1#
發表於 2018-9-18 23:05:59 | 只看該作者 |只看大圖 回帖獎勵 |倒序瀏覽 |閱讀模式
 
Push to Facebook
Activity簡介
Activity是一種展示型組件,用於向用戶直接展示一個界面,並且可以接受用戶的輸入信息從而進行交互.Activity是最重要的一組組件,對用戶來說,Activity是一個Android的用戶的全部,因為其他三大組件對於用戶來說都是不可感知的.Activity的啟動由Intent觸發,其中Intent可以分為顯示Intent和隱式Intent.Activity是具有生命週期的。一個Acticity組件可以有不同的啟動模式,不同的啟動模式具有不同的效果.Intent可以用於Activity之間進行數據的傳遞.Activity組件是可以停止的,在實際開發過程中可以通過活性的光潔度方法來結束Activity的運行.Activity扮演的是一種前台界面的角色


Activity的生命週期

首先來看一張Activity的生命週期圖:


正常情況下的生命週期分析
(1)第一次啟動一個Activity,回調方法:onCreate-> onStart->的onResume



(2)當用戶按住主頁鍵的時候,回調如下:onPause->的onStop



(3)當用戶再次返回到原Activity的時候,回調方法如下:
onRestart-> onStart-> onResume



(4)當用戶按背面鍵回退時,回調方法如下:onPause-> onStop->的onDestroy



異常情況下的生命週期分析


(1)系統配置發生改變導致Activity被殺死並重新創建

比如說,當橫豎屏切換的時候,Activity就會被銷毀並重新創建,當然我們也可以阻止系統重新創建Activity。當系統配置發生變化是,在的onStop之前會調用的onSaveInstanceState方法來保存當前Activity的狀態。當Activity被重新創建後,系統會調用onRestoreInstanceState方法來恢復之前保存的狀態,這個方法在調用onStart方法之後執行。

看代碼:


  1. package note.com.chapter_01;

  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.util.Log;
  5. import android.view.View;
  6. import android.widget.Button;
  7. import butterknife.ButterKnife;
  8. import butterknife.InjectView;

  9. /**
  10. * Created by zhoujian on 16/9/11.
  11. */
  12. public class SecondActivity extends Activity {

  13.     private static final String TAG = "SecondActivity";
  14.     @InjectView(R.id.bt_back)
  15.     Button mBtBack;
  16.     private String mName;

  17.     @Override
  18.     protected void onCreate(Bundle savedInstanceState) {
  19.         super.onCreate(savedInstanceState);
  20.         setContentView(R.layout.activity_second);
  21.         ButterKnife.inject(this);
  22.         Log.e(TAG, "onCreate()方法执行了");
  23.         if (savedInstanceState != null) {
  24.             mName = savedInstanceState.getString("name");
  25.             Log.e(TAG, "onCreate=" + mName);
  26.         }
  27.         clickEvent();
  28.     }

  29.     @Override
  30.     protected void onSaveInstanceState(Bundle outState) {
  31.         super.onSaveInstanceState(outState);
  32.         outState.putString("name", "周建");
  33.         Log.e(TAG,"onSaveInstanceState方法执行了");
  34.     }

  35.     @Override
  36.     protected void onRestoreInstanceState(Bundle savedInstanceState) {
  37.         super.onRestoreInstanceState(savedInstanceState);
  38.         //快捷键:option+command+f   快速提取变量
  39.         String mName = savedInstanceState.getString("name");
  40.         Log.e(TAG, "onRestoreInstanceState=" + mName);
  41.     }

  42.     private void clickEvent() {
  43.         mBtBack.setOnClickListener(new View.OnClickListener() {
  44.             @Override
  45.             public void onClick(View view) {
  46.                 finish();
  47.             }
  48.         });
  49.     }

  50.     @Override
  51.     protected void onRestart() {
  52.         super.onRestart();
  53.         Log.e(TAG, "onRestart()方法执行了");
  54.     }

  55.     @Override
  56.     protected void onStart() {
  57.         super.onStart();
  58.         Log.e(TAG, "onStart()方法执行了");
  59.     }

  60.     @Override
  61.     protected void onResume() {
  62.         super.onResume();
  63.         Log.e(TAG, "onResume()方法执行了");
  64.     }

  65.     @Override
  66.     protected void onPause() {
  67.         super.onPause();
  68.         Log.e(TAG, "onPause()方法执行了");
  69.     }

  70.     @Override
  71.     protected void onStop() {
  72.         super.onStop();
  73.         Log.e(TAG, "onStop()方法执行了");
  74.     }

  75.     @Override
  76.     protected void onDestroy() {
  77.         super.onDestroy();
  78.         Log.e(TAG, "onDestroy()方法执行了");
  79.     }
  80. }
複製代碼

當橫豎屏切換的時候,Activity會被殺死並重新創建,運行結果截圖如下:



系統配置中內容很多,如何當某項內容發生改變後,我們不想系統重新創建Activity,可以給Activity指定configChanges屬性,比如如果不想讓屏幕旋轉時重新創建,可以給onfigChanges屬性添加定位這個值。

  1. android:configChanges="orientation"
複製代碼

android:configChanges的屬性有很多,具體讀者可以查閱相關文檔。

  1. android:configChanges="orientation|mcc|mnc|locale|touchscreen|keyboard
  2.              |keyboardHidden|navigation|screenLayout|fontScale|uiMode|screenSize
  3.              |smallestScreenSize|layoutDirection">
複製代碼

(2)資源內存不足導致低優先級的Activity被殺死,導致Ativity被銷毀並重新創建

優先級從高到低如下:

正在和用戶交互的Activity優先級最高。
可見但非前台的Activity,比如Activity中彈出了一個對話框,導致Activity可見但是位於後台無法和用戶交互。
已經被暫停的Activity,優先級最低。

Activity的啟動模式

默認情況下,當我們多次啟動同一個Activity的時候,系統會創建多個實例並把它們一一放入任務棧中。任務棧是一種“先進後出”的棧結構
.Android中的四種啟動模式:標準,singleTop,singleTask,singleInstance


標準:標準模式,也是默認的啟動模式
。每次啟動一個Activity都會重新創建一個實例,不管這個實例是否存在在這種模式下,誰啟動了這個Activity,這個Activity就運行在啟動它的那個Activity的任務棧中。
        
  1. <activity
  2.             android:name=".MainActivity"
  3.             android:launchMode="standard">
  4.             <intent-filter>
  5.                 <action android:name="android.intent.action.MAIN"/>
  6.                 <category android:name="android.intent.category.LAUNCHER"/>
  7.             </intent-filter>
  8.         </activity>
複製代碼

比如說連續兩次啟動MainActivity,然後執行adb shell dumpsys activity命令查看任務棧情況



可以看出此時只有一個任務棧,任務棧為當前包名,任務棧中有3個MainActivity(原本的一個MainActivity和啟動兩次)

singleTop:棧頂复用模式

。如果新的Activity的實例已經存在並且位於棧頂,那麼此Activity被不會重新創建³³
如果新的Activity的實例已經存在但不是位於棧頂,那麼此Activity仍然會重新創建。
        
  1. <activity
  2.             android:name=".MainActivity"
  3.             android:launchMode="singleTop">
  4.             <intent-filter>
  5.                 <action android:name="android.intent.action.MAIN"/>
  6.                 <category android:name="android.intent.category.LAUNCHER"/>
  7.             </intent-filter>
  8.         </activity>
複製代碼

比如說連續兩次啟動MainActivity,然後執行adb shell dumpsys activity命令查看任務棧情況

因為MainActivity已經位於棧頂了,兩次啟動MainActivity的時候,不會重新創建,的此時棧任務中應該只有一個實例
看運行應用說明教學:



只有一個任務棧,任務棧中只有一個實例

singleTask:棧內復用模式

當一個具有singleTask模式的Activity請求啟動後,比如說ActivityA,系統首先會尋找是否存在A想要的任務棧,如果不存在A所需的任務棧,就會重新創建一個任務棧,然後創建甲的實例並把甲放入任務棧中。如果存在一個所需的任務棧,這是要看甲是否在棧中有實例存在,如果存在,就把甲調到棧頂並調用它的onNewIntent方法,如果不存在,就創建一個的實例並把一個壓入棧中。
        
  1. <activity
  2.             android:name=".MainActivity"
  3.             android:launchMode="singleTask">
  4.             <intent-filter>
  5.                 <action android:name="android.intent.action.MAIN"/>
  6.                 <category android:name="android.intent.category.LAUNCHER"/>
  7.             </intent-filter>
  8.         </activity>
複製代碼

比如說連續兩次啟動MainActivity,然後執行adb shell dumpsys activity命令查看任務棧情況
因為MainActivity實例調經棧頂,並且兩次調用它的onNewIntent方法
看運行截圖:



兩次調用onNewIntent方法:



singleInstance:實例單模式

除具有singleTask模式的所有特徵外,singleInstance模式的Activity只能單獨位於一個任務棧中。

指定啟動模式
有兩種方式指定啟動模式,方式第二種優先級高於第一種
第一種的英文在清單文件為Activity指定啟動模式

        
  1. <activity
  2.             android:name=".MainActivity"
  3.             android:launchMode="singleTask">
  4.             <intent-filter>
  5.                 <action android:name="android.intent.action.MAIN"/>
  6.                 <category android:name="android.intent.category.LAUNCHER"/>
  7.             </intent-filter>
  8.         </activity>
複製代碼

第二種是通過給Intent設置標誌位來為Activity指定啟動模式

  1. Intent mIntent =new Intent(MainActivity.this,MainActivity.class);               
  2. mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  3. startActivity(mIntent);
複製代碼

Activity的Flags
FLAG_ACTIVITY_NEW_TASK
作用是為Activity指定singleTask啟動模式,和在清單文件中指定效果相同。
FLAG_ACTIVITY_SINGLE_TOP
作用是為Activity指定singleTop啟動模式,和在清單文件中指定效果相同。
FLAG_ACTIVITY_CLEAR_TOP
具有此標記位的Activity,當它啟動時,在同一個任務棧中所有位於它上面的Activity都要出棧。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
具有這個標記位的Activity不會出現在歷史Activity的列表中,等同於指定的android:excludeFromRecents =“真”。

Activity的顯示調用和隱式調用
顯示調用:指定明確被啟動對象的組件信息
隱式調用:不需要指定組件信息,隱式調用需要Intent能夠匹配目標組件的IntentFilter中所設置的過濾信息,如果不匹配則無法調用目標Activity
  1. <activity android:name=".SecondActivity">
  2.             <intent-filter>

  3.                 <action android:name="com.zhoujian.define"/>
  4.                 <action android:name="com.zhoujian.start"/>

  5.                 <category android:name="com.zhoujian.text"/>
  6.                 <category android:name="com.zhoujian.cool"/>
  7.                 <category android:name="android.intent.category.DEFAULT"/>

  8.                 <data android:mimeType="text/plain"/>
  9.                
  10.             </intent-filter>
  11. </activity>
複製代碼

動作匹配的規則
要求意向中的作用存在且必須和過濾規則中的其中一個相同

類別匹配的規則
可以不設置,要設置的話,每一個都必須和過濾規則中的任一個相同

數據匹配的規則
狀語從句:行動的匹配規則類似,如果過濾規則中定義了數據,那麼意向中必須要定義可匹配的數據

下面給出匹配規則

  1. Intent mIntent = new Intent();
  2. mIntent.setAction("com.zhoujian.define");
  3. mIntent.addCategory("com.zhoujian.text");
  4. mIntent.addCategory("com.zhoujian.cool");
  5. mIntent.setDataAndType(Uri.parse("file://abc"),"text/plain");
  6. startActivity(mIntent);
複製代碼

Intent在Activity間傳遞數據
Intent傳遞簡單數據
在原Activity發送數據

  1. Intent  intent = new Intent(MainActivity.this,SecondActivity.class);
  2. Bundle bundle = new Bundle();
  3. bundle.putString("name","周建");
  4. bundle.putInt("age",25);
  5. intent.putExtras(bundle);
  6. startActivity(intent);
複製代碼

在目標的Activity接受數據
  1. Intent intent = getIntent();
  2. bundle = intent.getExtras();
  3. String mName = bundle.getString("name");
  4. int mAge = bundle.getInt("age");
  5. Log.d(TAG, "姓名:"+mName+",年龄:"+mAge);
複製代碼


Intent傳遞的JavaBean
實現Serializable接口接口
  1. package note.com.chapter_01;

  2. import java.io.Serializable;

  3. /**
  4. * Created by zhoujian on 2016/12/21.
  5. */

  6. public class Person implements Serializable
  7. {
  8.     private static final long serialVersionUID = 1L;
  9.     private int age;
  10.     private String name;

  11.     public int getAge()
  12.     {
  13.         return age;
  14.     }

  15.     public void setAge(int age)
  16.     {
  17.         this.age = age;
  18.     }

  19.     public String getName()
  20.     {
  21.         return name;
  22.     }

  23.     public void setName(String name)
  24.     {
  25.         this.name = name;
  26.     }

  27.     @Override
  28.     public String toString()
  29.     {
  30.         return "Person{" +"age=" + age + ", name='" + name + '\'' + '}';
  31.     }
  32. }
複製代碼

在原Activity發送數據

  1. Person person= new Person();
  2. person.setName("周建");
  3. person.setAge(25);
  4. Intent  intent = new Intent(MainActivity.this,SecondActivity.class);
  5. intent.putExtra("person",person);
  6. startActivity(intent);
複製代碼

在目標的Activity接受數據

  1. Intent intent = getIntent();
  2. Person mPerson = (Person)intent.getSerializableExtra("person");
  3. Log.d(TAG, mPerson.toString());
複製代碼


實現Parcelable接口

  1. package note.com.chapter_01;

  2. import android.os.Parcel;
  3. import android.os.Parcelable;

  4. /**
  5. * Created by zhoujian on 2016/12/21.
  6. */

  7. public class User  implements Parcelable
  8. {
  9.     private int age;
  10.     private String name;
  11.     public User()
  12.     {
  13.     }
  14.     public int getAge() {
  15.         return age;
  16.     }
  17.     public void setAge(int age) {
  18.         this.age = age;
  19.     }
  20.     public String getName() {
  21.         return name;
  22.     }

  23.     public void setName(String name) {
  24.         this.name = name;
  25.     }
  26.     @Override
  27.     public int describeContents() {
  28.         return 0;
  29.     }
  30.     @Override
  31.     public void writeToParcel(Parcel dest, int flags) {
  32.         dest.writeInt(this.age);
  33.         dest.writeString(this.name);
  34.     }
  35.     protected User(Parcel in) {
  36.         this.age = in.readInt();
  37.         this.name = in.readString();
  38.     }

  39.     public static final Creator<User> CREATOR = new Creator<User>()
  40.     {
  41.         @Override
  42.         public User createFromParcel(Parcel source)
  43.         {
  44.             return new User(source);
  45.         }

  46.         @Override
  47.         public User[] newArray(int size)
  48.         {
  49.             return new User[size];
  50.         }
  51.     };

  52.     @Override
  53.     public String toString()
  54.     {
  55.         return "User{" + "age=" + age + ", name='" + name + '\'' + '}';
  56.     }
  57. }
複製代碼

在原Activity發送數據

  1. User user = new User();
  2. user.setAge(25);
  3. user.setName("周建");

  4. Intent  intent = new Intent(MainActivity.this,SecondActivity.class);
  5. intent.putExtra("user",user);
  6. startActivity(intent);
複製代碼

在目標的Activity接受數據

  1. Intent intent = getIntent();
  2. User mUser = (User) intent.getParcelableExtra("user");
  3. Log.d(TAG, mUser.toString());
複製代碼

Intent傳遞集合
在原Activity發送數據
  1. ArrayList<Person> personArrayList = new ArrayList<Person>();

  2. Person Aperson= new Person();
  3. Aperson.setName("周建");
  4. Aperson.setAge(25);
  5. personArrayList.add(Aperson);

  6. Person Bperson= new Person();
  7. Bperson.setName("zhoujian");
  8. Bperson.setAge(28);
  9. personArrayList.add(Bperson);

  10. Intent  intent = new Intent(MainActivity.this,SecondActivity.class);
  11. intent.putExtra("personArrayList",(Serializable)personArrayList);
  12. startActivity(intent);
複製代碼


在目標的Activity接受數據

  1. Intent intent = getIntent();
  2. ArrayList<Person> mList = (ArrayList<Person>) intent.getSerializableExtra("personArrayList");
  3. Log.d(TAG, mList.toString());
複製代碼

onActivityResult
MainActivity.java

  
  1. private void clickEvent() {
  2.         mButton.setOnClickListener(new View.OnClickListener() {
  3.             @Override
  4.             public void onClick(View view) {
  5.                 Intent intent = new Intent(MainActivity.this, SecondActivity.class);
  6.                 //requestCode
  7.                 startActivityForResult(intent, INTENT_FLAG);
  8.             }
  9.         });

  10.     }

  11.     @Override
  12.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {


  13.         if (resultCode == RESULT_OK)
  14.         {
  15.             switch (requestCode)
  16.             {
  17.                 case INTENT_FLAG:
  18.                     String result = data.getStringExtra("msg");
  19.                     Toast.makeText(this, result, Toast.LENGTH_SHORT).show();
  20.                     break;
  21.             }
  22.         }
  23.     }
複製代碼

SecondActivity.java

  
  1. private void clickEvent() {
  2.         mBtBack.setOnClickListener(new View.OnClickListener() {
  3.             @Override
  4.             public void onClick(View view) {
  5.                 Intent intent = new  Intent();
  6.                 intent.putExtra("msg","我来自第二个界面");
  7.                 setResult(RESULT_OK,intent);
  8.                 finish();
  9.             }
  10.         });
  11.     }
複製代碼

以上就是有關Activity的基礎知識的總結



 

臉書網友討論
*滑块验证:
您需要登錄後才可以回帖 登錄 | 註冊 |

本版積分規則



Archiver|手機版|小黑屋|免責聲明|TShopping

GMT+8, 2024-4-25 23:24 , Processed in 0.073898 second(s), 25 queries .

本論壇言論純屬發表者個人意見,與 TShopping綜合論壇 立場無關 如有意見侵犯了您的權益 請寫信聯絡我們。

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回復 返回頂部 返回列表