描述:
1、表示用戶交互的一個界面(活動),每一個activity對應一個界面。
2、是所有View的容器。
3、有個ActivityManager的管理服務類,用于維護與管理Activity的啟動與銷毀。Activity啟動時,會把Activity的引用放入任務棧中。
4、一個應用程序可以被別的應用程序的activity開啟此時,是將此應用程序的引用加入到了開啟的那個activity的任務棧中了。
創建Activity
1、定義類繼承自Activity類;
2、在清單文件中Application節點中聲明<activity>節點;
啟動Activity
顯示啟動:
啟動當前應用內的Activity,這樣的啟動方式比較快速。
Intent intent = new Intent(this, OtherActivity.class);
startActivity(intent);
隱示啟動
一般用于調用其他應用的Activity,創建Intent后指定動作和數據以及類型。
1、啟動系統自帶應用
// ex:撥打電話(Android6.0注意申請權限)
Intent intent = new Intent();
intent.setAction(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel://123456"));
startActivity(intent);
//ex:打開網頁
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
2、啟動第三方應用
//1、啟動主Acitvity
//Intent intent = new Intent(Intent.ACTION_MAIN);
//intent.addCategory(Intent.CATEGORY_LAUNCHER);
//有 <intent-filter>標簽的Activity默認 android:exported="true"
Intent intent = new Intent();
//參數是包名,類全限定名,注意直接用類名不行
ComponentName cn = new ComponentName("com.example.liucun.myapplication",
"com.example.liucun.myapplication.MainActivity");
intent.setComponent(cn);
startActivity(intent);
//2、啟動非主Activity
//必須要在manifest里面設置exported屬性為true,表示此activity對外公開,才能直接跳轉。
Intent intent = new Intent();
intent.setClassName("com.example.liucun.myapplication",
"com.example.liucun.myapplication.test.SecondActivity");
startActivity(intent);
Activity生命周期
1、Acitivity三種狀態
a、運行:activity在最前端運行;
b、暫停:activity可見,但前端還有其他activity,注意:在當前Activitiiy彈出的對話框是Activity的一部分,彈出時,不會執行onPause方法;
c、停止:activity不可見,完全被覆蓋;
2、生命周期相關的方法(都是系統自動調用,都以 on 開頭):
a. onCreate: 創建時調用,或者程序在暫停、停止狀態下被殺死之后重新打開時也會調用;
b. onStart: onCreate之后或者從停止狀態恢復時調用;(界面可見)
c. onResume: onStart之后或者從暫停狀態恢復時調用,從停止狀態恢復時由于調用onStart,也會調用onResume(界面獲得焦點);
d. onPause: 進入暫停、停止狀態,或者銷毀時會調用(界面失去焦點);
e. onStop: 進入停止狀態,或者銷毀時會調用;(界面不可見)
f. onDestroy: 銷毀時調用;
g. onRestart: 從停止狀態恢復時調用;
橫豎屏切換與信息的保存和恢復
1、切換橫豎屏時,會自動查找layout-port 、layout-land中的布局文件。
2、默認情況下,切換時,將執行摧毀onPause onStop onDestroy,再重置加載新的布局onCreate onStart onResume。
3、切換時如果要保存數據, 可以重寫: onSaveInstanceState();
恢復數據時, 重寫: onRestoreInstanceState()。
4、固定橫屏或豎屏:android:screenOrientation="landscape"
橫豎屏切換, 不摧毀界面(程序繼續執行) android:configChanges="orientation|keyboardHidden|screenSize"
5、保存信息狀態的相關方法:
a. onSaveInstanceState:
在Activity被動的摧毀或停止的時候調用(如橫豎屏切換,來電),用于保存運行數據,可以將數據存在在Bundle中;
b. onRestoreInstanceState:
該方法在Activity被重新繪制的時候調用,例如改變屏幕方向,onSavedInstanceState可為onSaveInstanceState保存的數據
private String temp;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 從savedInstanceState中恢復數據, 如果沒有數據需要恢復savedInstanceState為null
if (savedInstanceState != null) {
temp = savedInstanceState.getString("temp");
System.out.println("onCreate: temp = " + temp);
}
}
// 將數據保存到outState對象中, 該對象會在重建activity時傳遞給onCreate方法
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("temp", temp);
}
Activity四種啟動模式
任務棧的概念
一個手機里面有多少個任務棧:
一般情況下,有多少個應用正在運行,就對應開啟多少個任務棧;
一般情況下,每開啟一個應用程序就會創建一個與之對應的任務棧;
二般情況下,如launchMode為 singleInstance,就創建自己單獨的任務棧;
任務棧的作用
1、它是存放Activity的引用的,Activity不同的啟動模式,對應不同的任務棧的存放;
2、可通過getTaskId()來獲取任務棧的ID,如果前面的任務棧已經清空,新開的任務棧ID+1,是自動增長的;
啟動模式
在AndroidManifest.xml中的<activity>標簽中可以配置android:launchMode屬性,用來控制Actvity的啟動模式;在Android系統中我們創建的Acitivity是以棧的形式呈現的:
1、standard:默認的,每次調用startActivity()啟動時都會創建一個新的Activity放在棧頂;
2、singleTop:啟動Activity時,指定Activity不在任務棧棧頂就創建,如在棧頂,則不會創建,會調用onNewInstance(),復用已經存在的實例
3、singleTask:在任務棧里面只允許一個實例,如果啟動的Activity不存在就創建,如果存在直接跳轉到指定的Activity所在位置,如:棧內有ABCD,D想創建A, 即A上的BCD相應的Activity將移除;
4、singleInstance:(單例)開啟一個新的任務棧來存放這個Activity的實例,在整個手機操作系統里面只有一個該任務棧的實例存在,此模式開啟的Activity是運行在自己單獨的任務棧中的;
啟動模式的使用場景
1、singleTop
a:消息推送,通知欄彈出Notification,點擊Notification跳轉到指定Activity,但是如果我現在頁面就停留在那個指定的Activity,會再次打開我當前的Activity,這樣返回的時候回退的頁面和當前頁面一樣,感官上就會很奇怪。
b:登錄的時候,登錄成功跳轉到主頁,按下兩次登錄按鈕,生成了兩個主頁。一些有啟動延遲的頁面(往往是動畫,網絡造成)也會有這樣的情況。
2、singleTask
a:應用的MainActivity可以使用singleTask模式,保證只有一個主頁實例的存在。并且也可以配合整個APP的退出,即在任何頁面結束當前應用可以跳轉到singleTask模式的MainActivity,然后再finish() MainActivity。
3、singleInstance
a:Launcher,即桌面
b:呼叫來電界面
應用程序、進程、任務棧的區別
1、應用程序
四大組件的集合在清單文件中都放在application節點下,對于終端用戶而言,會將其理解為activity。
2、進程
操作系統分配的獨立的內存空間,一般情況下,一個應用程序會對應一個進程,特殊情況下,會有多個進程,一個應用程序會對應一個或多個進程。
3、任務棧:
task stack(back stack)后退棧,記錄用戶的操作步驟,維護用戶的操作體驗,專門針對于activity而言的,只用于activity;一般使用standard,其他情況用別的。
Activity內存管理
Android系統在運行多個進程時,如果系統資源不足,會強制結束一些進程,優先選擇哪個進程來結束是有優先級的。
會按照以下順序殺死:
1、空: 進程中沒有任何組件;
2、后臺:進程中只有停止狀態的Activity;
3、服務:進程中有正在運行的服務;
4、可見:進程中有一個暫停狀態的Activity;
5、前臺:進程中正在運行一個Activity;
一個任務棧所有Activity在退出的時候進程不會銷毀, 會保留一個空進程方便以后啟動. 但在內存不足時進程會被銷毀;
Activity中不要在Activity做耗時的操作, 因為Activity切換到后臺之后(Activity停止了), 內存不足時, 也容易被銷毀;
空進程:任務棧清空,意味著程序退出了,但進程留著,這個就是空進程,容易被系統回收;
Activity內存泄漏
非靜態內部類和匿名內部類持有Activity的引用
在Java中,內部類的定義與使用一般為成員內部類與匿名內部類,他們的對象都會隱式持有外部類對象的引用,影響外部類對象的回收。
GC只會回收沒有被引用或者根集不可到達的對象,內部類在生命周期內始終持有外部類的對象的引用,造成外部類的對象始終不滿足GC的回收條件,反映在內存上就是內存泄露。如:Activity的內存泄露。
ex:
public class SampleActivity extends Activity {
private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 十分鐘后發送一條消息
mLeakyHandler.postDelayed(new Runnable() {
@Override
public void run() { /* ... */ }
}, 1000 * 60 * 10);
// Go back to the previous Activity.
finish();
}
}
當我們執行了Activity的finish方法,被延遲的消息會在被處理之前存在于主線程消息隊列中10分鐘,而這個消息中又包含了Handler的引用,而Handler是一個匿名內部類的實例,其持有外面的SampleActivity的引用,所以這導致了SampleActivity無法回收,進行導致SampleActivity持有的很多資源都無法回收,這就是我們常說的內存泄露。
注意上面的new Runnable這里也是匿名內部類實現的,同樣也會持有SampleActivity的引用,也會阻止SampleActivity被回收。
解決方案:
1、將內部類定義為static;
2、使用弱引用修飾外部類Activity。
public class SampleActivity extends Activity {
private static class MyHandler extends Handler {
private final WeakReference<sampleactivity> mActivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference<sampleactivity>(activity);
}
@Override
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if (activity != null) {
// ...
}
}
}
private final MyHandler mHandler = new MyHandler(this);
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
finish();
}