關於Android四大組件的學習總結


Activity

Android應用的用戶界面是由Activity類管理的.和其他組件一樣,Activity會用一系列生命周期回調函數通知當前的狀態。

生命周期

Activity的四種狀態

1.運行狀態
2.暫停狀態
3.停止狀態
4.銷毀狀態

Activity的三種生存期

1.完整生存期
2.可見生存期
3.前台生存期

生命周期示意圖

此處輸入圖片的描述

個人理解:完整生存期 > 可見生存期 > 前台生存期
完整生存期由onCreate()方法開始,到onDestory()方法結束,我們應該在onCreate()方法中完成各種初始化操作,比如加載布局、綁定事件等,在onDestory()方法中完成釋放內存的操作,活動將變為銷毀狀態。
可見生存期開始於onStart()方法,到onStop()結束。onStart()在活動由不可見變為可見時調用,onStop()與之相反。在該生存期內,活動總是可見的,但是不一定能夠與用戶進行交互,活動可能處於暫停狀態,所以應該在這兩個回調方法中對用戶可見的資源進行加載和釋放,從而保證處於停止狀態的活動不會占用過多內存。
活動在onResume()和onPause()之間所經歷的就是前台生存期,該生存期內活動始終處於運行狀態,這兩個方法適合管理消耗cup的資源。

Activity的啟動模式

啟動模式一共有四種,分別是standard,singleTop,singleTask,singleInstance,可以在AndroidManifest.xml中通過給< activity >標簽指定android:launchMode屬性來選擇啟動模式。

standard

默認的啟動模式,在該模式下,每當啟動一個新的活動,它就會在返回棧(Android是使用任務(Task)來管理活動的,一個任務就是一組存放在棧里的活動的集合,這個棧也被稱作返回棧(Back Stack))中入棧,並處於棧頂的位置。對於使用standard模式的活動,系統不會在乎這個是否已經在返回棧中存在,每次啟動都會創建該活動的一個新的實例。

singleTop

當活動的啟動模式指定為singleTop,在啟動活動時如果發現返回棧的棧頂已經是該活動,則認為可以直接使用它,不會在創建新的活動實例。

singleTask

在該模式下,每次啟動該活動時系統首先會在返回棧中檢查是否存在該活動的實例,如果發現已經存在則直接使用該實例,並把這個活動之上的所有活動統統出棧,如果沒有發現就會創建一個新的活動實例。

singleInstance

指定為該模式的活動會啟用一個的返回棧來管理這個活動。使用該模式可以解決共享活動實例的問題,因為每個應用程序都有自己的返回棧,同一個活動在不同的返回棧中入棧必然是創建了新的實例,而在該模式下會有一個單獨的返回棧來管理這個活動,不管是哪個應用程序來訪問這個活動,都共用同一個返回棧。

跳轉Flag相關

在 android.content.Intent 中一共定義了20種不同的 flag,其中和 Task 緊密關聯的有四種:
1.FLAG_ACTIVITY_NEW_TASK
2.FLAG_ACTIVITY_CLEAR_TOP
3.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
4.FLAG_ACTIVITY_SINGLE_TOP

在使用這四個 flag 時,一個 Intent 可以設置一個 flag,也可以選擇若干個進行組合。Activity的加載模式受啟動Activity的Intent對象中設置的Flag和manifest文件中Activity的 元素的特性值交互控制。

Intent intent = new Intent(this,xxx.class);
//如果activity在task存在,拿到最頂端,不會啟動新的Activity
intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
//如果activity在task存在,將Activity之上的所有Activity結束掉
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//默認的跳轉類型,將Activity放到一個新的Task中
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//如果activity已經是運行在 Task 的 top,則不會創建新的實例。
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

參考鏈接:Activity跳轉Flag詳解


Service

基本上,任何不涉及用戶界面的操作都應該使用Service。建議每個任務都有一個對應的Service。服務並不是運行在一個獨立的進程當中,而是依賴於創建服務時所在的應用程序進程。當某個應用程序進程被殺掉時,所有依賴於該進程的服務也會停止運行。服務並不會自動開啟線程,所有代碼都是默認運行在主線程當中的,也就是說,我們需要在服務的內部手動創建子線程,並在這里執行具體的任務。

生命周期

一旦在項目的任何位置調用了Context的startService()方法,相應的服務就會啟動起來,並調用onStartCommand()方法。如果這個服務之前沒有創建過,onCreate()方法會先於onStartCommand()方法執行。服務啟動之后會一直保持運行狀態,直到stopService()或者stopSelf()方法被調用。注意雖然每調用一次startService()方法,onStartCommand()就會執行一次,但實際上每個服務都只會存在一個實例。所以不管你調用了多少次startService()方法,只需調用一次stopService()或stopSelf(),服務就會停止下來。

另外,還可以調用Context的bindService()來獲取一個服務的持久連接,這是就會回調服務中的onBind()方法。類似的,如果這個服務之前沒有創建過,onCreate()方法會先於onBind()方法執行。之后,調用方可以獲取到onBind()方法返回的IBinder對象的實例,這樣就能自由地和服務通信了。只要調用放和服務之間的連接沒有斷開,服務就會一直保持運行狀態。

當調用了startService()方法后,又去調用stopService()方法,這是服務中的onDestory()方法就會執行,表示服務已經銷毀了。類似的,當調用了bindService()方法后,又去調用unbindService()方法,onDestory()方法也會執行。

Activity和Service進行通信

被綁定的Service會一直運行,直到所有綁定的客戶端都斷開后才會停止。在同一個應用進程中綁定Service只需獲取對象的引用,並調用對象的方法即可,這種方式稱為本地binder

public class MyLocalService extends Service {
private LocalBinder mLocalBinder = new LocalBinder();

@Override
public IBinder onBind(Intent intent) {
return mLocalBinder;
}

public void doLongRunningOperation() {

}

public class LocalBinder extends Binder {
public MyLocalService getService() {
return MyLocalService.this;
}
}
}
public class MyActivity extends Activity implements ServiceConnection {

private MyLocalService mService;

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

@Override
protected void onResume() {
super.onResume();
Intent bindIntent = new Intent(this, MyLocalService.class);
bindService(bindIntent, this, BIND_AUTO_CREATE);
}

@Override
protected void onPause() {
super.onPause();
if (mService != null) {
unbindService(this);
}
}

@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mService = ((MyLocalService.LocalBinder) iBinder).getService();
}

@Override
public void onServiceDisconnected(ComponentName componentName) {
mService = null;
}

}

兩種啟動方式的對比

  • 通過StartService()啟動Service,Service的onCreate()和onStartCommand()方法就會被執行,之后Service會一直處於運行狀態,但具體運行的是什么,Activity就控制不了了。
    通過bindService()方法將Activity和Service進行綁定,在onServiceConnected()方法中,通過向下轉型可以獲得一個Binder子類的實例,通過調用該實例的public方法,實現Activity對Serivce的控制。
  • 采用Context.startService()方法啟動服務,在服務未被創建時,系統會先調用服務的onCreate()方法,接着調用onStart()方法。采用startService()方法啟動的服務,只能調用Context.stopService()方法結束服務,服務結束時會調用onDestroy()方法。
    采用Context.bindService()方法啟動服務,在服務未被創建時,系統會先調用服務的onCreate()方法,接着調用onBind()方法。這個時候調用者和服務綁定在一起,調用者退出了,系統就會先調用服務的onUnbind()方法,接着調用onDestroy()方法。如果調用者希望與綁定的服務解除綁定,可以調用unbindService()方法,調用該方法導致系統調用服務的onUnbind()和onDestroy()方法.

個人理解:兩種啟動方式適用場景不同,通過Context.startService()啟動的Service適合執行由用戶觸發並且運行時間不確定的操作,可在操作結束后調用stopSelf()自行結束。雖然這種啟動方式沒有提供直接的通信機制,但是使用廣播或者其他方式可以把結果返回給調用組件。然而,通過綁定的方式可以獲取到binder對象,使用它可以直接調用Service對象,通過該對象的public方法,可以實現Activity操作Service。

使用IntentService

異步的,會自動停止的服務
該工具類在Service中包裝了一個處理后台線程的Hanlder。

public class MyIntentService extends IntentService {

/**
* 構造函數必須在其內部調用父類的有參構造函數
*/
public MyIntentService() {
super("MyIntentService");
}

/**
* 實現該抽象方法,處理具體邏輯,不用擔心ANR的問題,因為這個方法已經是在子線程中運行的了
*/
@Override
protected void onHandleIntent(Intent arg0) {
// TODO Auto-generated method stub
}

/**
* 根據IntentService的特性,這個服務在運行結束后會自動停止
*/
@Override
public void onDestroy() {
super.onDestroy();
}

}

個人理解:由於IntentService已經幫我們實現了異步任務,可直接調用其onHandleIntent()在子線程中執行任務,如同使用AsyncTask(),所以誤認為Service就是一個子線程,通過本次學習搞清楚了兩者之間的關系:服務(后台運行,耗時) = Service(脫離用戶界面) + 子線程 (防止主線程被阻塞)


BraodcastReceiver

BraodcastReceiver是沒有狀態的,這意味着BraodcastReceiver對象僅在onReceive()被調用時是有效的。基本上,在BraodcastReceiver的onReceive()方法中唯一應該做的是把調用委托給另一個組件,比如通過Context.startActivity()或者Context.startService()方法。

Andorid中發送廣播事件最常用的方式是通過Context.sendBroadcast()方法給BroadcastReceiver發送Intent對象。

實現BroadcastReceiver的默認方法是在清單文件中聲明它。因此,即便用戶沒有啟動應用程序,也可以使用BroadcastReceiver來通知Service。也可在Activity和Service中以編程的方式注冊BroadcastReceiver。有些廣播Intent只能以編程的方式注冊。

普通廣播和有序廣播

廣播分為兩種類型:普通廣播和有序廣播。
普通廣播會以異步方式發送給所有的接收者,並且沒有指定的接收順序,該方式更加高效,但是缺少有序廣播的一些高級功能。
有序廣播按照特定的順序分發,每次只發給一個注冊的廣播接收器。開發者可以在清單文件中控制接收順序。有序廣播還有另外一個特性:通過使用abortBroadcast(),setResultCode()和setResultData()方法,接收器可以把結果回傳給廣播,或者終止廣播的分發。

本地BroadcastReceiver

如果只是在應用程序進程內發送和接收廣播,使用LocalBroadcastManager更高效,因為不需要跨進程管理操作,也不需要廣播通常涉及的安全問題。

廣播的變體

粘性廣播
定向廣播

啟用和禁止廣播接收器

如果只能在清單文件中定義才能監聽某個廣播,還有另外一種減少對系統負載影響的方法。通過使用PackageManager,可以啟用和禁用應用程序中的組件,這在用戶執行特定動作后才啟動廣播接收器是很有用。只需在清單文件中把組件的android:enabled屬性默認設置為false,並稍后用下面的把他們改為true。

public void enableBroadcastReceiver() {
PackageManager packageManager = getPackageManager();
packageManager.setComponentEnabledSetting(
new ComponentName(this,chargerConnecedListener.class),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);

}

public void disableBroadcastReceiver() {
PackageManager packageManager = getPackageManager();
packageManager.setComponentEnabledSetting(
new ComponentName(this,ChargerConnecedListener.class),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}

個人理解:BroadcastReceiver不同於Activity和Service,沒有生命周期的概念,該類的對象只有在onReceive()被調用時才有效,起到一個橋梁的作用,根據所接收到的數據決定是啟動一個Activity,還是啟動一個Service。動態注冊的BroadcastReceiver適用於僅在應用啟動情況下需要接收廣播的情況,這樣做有利於減少對系統負載的影響,對於僅能夠靜態注冊的方法依然可以通過以上方法達到該目的。


ContentProvider

主要用於在不同的應用程序之間實現數據共享的功能,它提供了一套完整的機制,允許一個程序訪問另一個程序中的數據,同時還能保證被訪問數據的安全性。目前,使用內容提供器是Andorid實現跨程序共享數據的標准方式

ContentResolver基本用法

對於每一個應用程序來說,如果要訪問內容提供器中共享的數據,就一定要借助ContentResolver類,可以通過Context中的getContentResolver()方法獲取到該類的實例。ContentResolver中提供了一系列的方法用於對數據進行CRUD操作。
不同於SQLiteDatabase,ContentResolver的增刪查改方法都是不接受表名參數的,而是使用一個Uri參數代替,這個參數被稱為內容URI。內容URI給內容提供器中的數據建立了唯一標識符,它主要由兩部分組成,權限和路徑。
在得到內容URI字符串后調用Uri.parse()方法,將內容URI字符串解析成Uri對象,調用getContentResolver().query()等方法對數據進行操作。

public class MainActivity extends Activity {
@Override
publicvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

//獲取上下文
Context context = MainActivity.this;

//獲取ContentResolver對象
ContentResolver resolver = context.getContentResolver();

//獲取Uri對象
Uri uri = Uri.parse("content://com.test.MyProvider");

//獲取數據
Cursor c = resolver.query(uri, null, null, null, null);

c.moveToFirst();

for(int i=0; i<c.getCount(); i++){
int index = c.getColumnIndexOrThrow("name");
String src = c.getString(index);
Log.d("", src);
c.moveToNext();
}
}
}

創建ContentProvider

想要實現跨程序共享數據的功能,官方推薦的方式就是使用內容提供器,新建一個類去繼承ContentProvider,將六個抽象方法全部重寫。

public class MyProvider extends ContentProvider {

// 在Create中初始化一個數據庫
@Override
public boolean onCreate() {
SQLiteDatabase db =this.getContext().openOrCreateDatabase("test_db.db3", Context.MODE_PRIVATE, null);
db.execSQL("create table tab(_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)");
ContentValues values =new ContentValues();
values.put("name", "test");
db.insert("tab", "_id", values);
db.close();
returntrue;
}

@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
// 實現query方法
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = this.getContext().openOrCreateDatabase(
"test_db.db3", Context.MODE_PRIVATE, null);
Cursor c = db.query("tab", null, null, null, null, null, null);
return c;
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
}

@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
}


@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}
}

個人理解:在互聯網上搜索“Android 數據存儲”,大多數文章會給出四到五種方式,包括文件,SharedPreferences,數據庫,網絡和ContentProvicer,通過本次學習,我覺得這種說法並不合適,ContentProvicer與其他四種方式不同,與其說它是一種數據存儲方式,不如說它是在其他幾種存儲方式的基礎上進行封裝,提供了一套標准的數據訪問接口,正如它字面上的意思。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com