「Android」四大組件,你真的都掌握了?

前言

  • Android 的四大組件指:Activity Service Broadcast ContentProvider
  • 本文將着重分析 Activity 和 Service

內容

1、 Activity


  • 提供一個界面讓用戶點擊和各類滑動操做

Activity

1.1 生命週期

  • 啓動Activity: onCreate()--->onStart()--->onResume(),Activity進入運行狀態。
  • Activity退居後臺: 當前Activity轉到新的Activity界面或按Home鍵回到主屏: onPause()--->onStop(),進入停滯狀態。
  • Activity返回前臺: onRestart()--->onStart()--->onResume(),再次回到運行狀態。
  • Activity退居後臺,且系統內存不足, 系統會殺死這個後臺狀態的Activity,若再次回到這個Activity,則會走onCreate()-->onStart()--->onResume()
  • 鎖定屏與解鎖屏幕 只會調用onPause(),而不會調用onStop方法,開屏後則調用onResume()

生命週期

1.1.1 兩個活動間跳轉

  • Activity A 啓動另外一個Activity B,回調以下: Activity A 的onPause() → Activity B的onCreate()onStart()onResume() → Activity A的onStop(); 若是B是透明主題又或則是個DialogActivity,則不會回調A的onStop

如何將一個 Activity 設置成窗口的樣式? 只須要給咱們的 Activity 配置以下屬性便可: android:theme="@android:style/Theme.Dialog"html

1.1.2 onSaveInstanceState

Activity 的 onSaveInstanceState() 和 onRestoreInstanceState()並非生命週期方法,它們不一樣於 onCreate()、onPause()等生命週期方法,它們並不必定會被觸發。java

  • 調用條件

調用

  • 應用場景

應用

lateinit var textView: TextView [注1]
var gameState: String? = null override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    gameState = savedInstanceState?.getString(GAME_STATE_KEY)
    setContentView(R.layout.activity_main)
    textView = findViewById(R.id.text_view)
}

override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    textView.text = savedInstanceState?.getString(TEXT_VIEW_KEY)
}

override fun onSaveInstanceState(outState: Bundle?) {
    outState?.run {
        putString(GAME_STATE_KEY, gameState)
        putString(TEXT_VIEW_KEY, textView.text.toString())
    }
    super.onSaveInstanceState(outState)
}
複製代碼

1.1.3 拓展

Koltin中屬性在聲明的同時也要求要被初始化,不然會報錯。 例如如下代碼:linux

private var name0: String //報錯
private var name1: String = "xiaoming" //不報錯
private var name2: String? = null //不報錯
複製代碼

但是有的時候,我並不想聲明一個類型可空的對象,並且我也沒辦法在對象一聲明的時候就爲它初始化,那麼這時就須要用到Kotlin提供的延遲初始化。 Kotlin中有兩種延遲初始化的方式。一種是lateinit var,一種是by lazy。   

android

1.2 啓動模式


LaunchMode 說明
standard 系統在啓動它的任務中建立 activity 的新實例
singleTop 若是activity的實例已存在於當前任務的頂部,則系統經過調用其onNewIntent(),不然會建立新實例
singleTask 系統建立新 task 並在 task 的根目錄下實例化 activity。但若是 activity 的實例已存在於單獨的任務中,則調用其 onNewIntent() 方法,其上面的實例會被移除棧。一次只能存在一個 activity 實例
singleInstance 相同 singleTask,activity始終是其task的惟一成員; 任何由此開始的activity 都在一個單獨的 task 中打開[注2]

 

1.2.1 standard

  • 默認模式,能夠不用寫配置。在這個模式下,都會默認建立一個新的實例。所以,在這種模式下,能夠有多個相同的實例,也容許多個相同Activity疊加。

1.2.2 singleTop

  • 能夠有多個實例,可是不容許多個相同Activity疊加。即,若是Activity在棧頂的時候,啓動相同的Activity,不會建立新的實例,而會調用其onNewIntent方法。

1.2.3 singleTask

  • 只有一個實例。在同一個應用程序中啓動他的時候,若Activity不存在,則會在當前task建立一個新的實例,若存在,則會把task中在其之上的其它Activity destory掉並調用它的onNewIntent方法。
  • 若是是在別的應用程序中啓動它,則會新建一個task,並在該task中啓動這個Activity,singleTask容許別的Activity與其在一個task中共存,也就是說,若是我在這個singleTask的實例中再打開新的Activity,這個新的Activity仍是會在singleTask的實例的task中。

1.2.4 singleInstance

  • 只有一個實例,而且這個實例獨立運行在一個task中,這個task只有這個實例,不容許有別的Activity存在。

1.2.5 task

  • 咱們知道,一個應用中可能有多個 Activity,而這些 Activity 被以棧的形式管理。當咱們新打開 Activity 或者按返回時,會致使 Activity 的入棧/出棧。
  • Task 是指在執行特定任務時與用戶交互的一系列 Activity。 這些 Activity 按照各自的打開順序排列在堆棧中。


1.3 啓動過程

  • 這裏須要結合源碼本身看,不作展開,針對面試的話,只需記住方法/調用鏈便可

1.3.1 ActivityThread.java


  • 在Android中它就表明了Android的主線程,注意是表明而不是說它就是一個Thread類
  • 它是建立完新進程以後(確定是在一個線程中啊),main函數被加載,而後執行一個loop的循環使當前線程進入消息循環,而且做爲主線程。
  • 接下來還會初始化不少必要的屬性. 它的不少成員變量和內部類十分的重要,深刻理解這些成員變量有助於咱們進一步的認識Android系統的各個組件的交互。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    ActivityInfo aInfo = r.activityInfo;
    if (r.packageInfo == null) {
        //step 1: 建立LoadedApk對象
        r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                Context.CONTEXT_INCLUDE_CODE);
    }
    ... //component初始化過程

    java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
    //step 2: 建立Activity對象
    Activity activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
    ...

    //step 3: 建立Application對象
    Application app = r.packageInfo.makeApplication(false, mInstrumentation);

    if (activity != null) {
        //step 4: 建立ContextImpl對象
        Context appContext = createBaseContextForActivity(r, activity);
        CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
        Configuration config = new Configuration(mCompatConfiguration);
        //step5: 將Application/ContextImpl都attach到Activity對象
        activity.attach(appContext, this, getInstrumentation(), r.token,
                r.ident, app, r.intent, r.activityInfo, title, r.parent,
                r.embeddedID, r.lastNonConfigurationInstances, config,
                r.referrer, r.voiceInteractor);

        ...
        int theme = r.activityInfo.getThemeResource();
        if (theme != 0) {
            activity.setTheme(theme);
        }

        activity.mCalled = false;
        if (r.isPersistable()) {
            //step 6: 執行回調onCreate
            mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        } else {
            mInstrumentation.callActivityOnCreate(activity, r.state);
        }

        r.activity = activity;
        r.stopped = true;
        if (!r.activity.mFinished) {
            activity.performStart(); //執行回調onStart
            r.stopped = false;
        }
        if (!r.activity.mFinished) {
            //執行回調onRestoreInstanceState
            if (r.isPersistable()) {
                if (r.state != null || r.persistentState != null) {
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                            r.persistentState);
                }
            } else if (r.state != null) {
                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
            }
        }
        ...
        r.paused = true;
        mActivities.put(r.token, r);
    }

    return activity;
}

複製代碼

1.3.2 拓展

  • Instrumentation

Android instrumentation是Android系統裏面的一套控制方法或者」鉤子「。 這些鉤子能夠在正常的生命週期(正常是由操做系統控制的)以外控制Android控件的運行。 它們同時能夠控制Android如何加載應用程序。git

Base class for implementing實現 application instrumentation code工具代碼. When running with instrumentation turned on, this class will be instantiated for被實例化 you before any of the application code, allowing you to monitor all of the interaction交互 the system has with the application. An Instrumentation implementation is described to the system through an AndroidManifest.xml's tag.github

AMS是系統的引導服務,應用進程的啓動、切換和調度、四大組件的啓動和管理都須要AMS的支持。面試

  • ActivityStarter

ActivityStarter是加載Activity的控制類,收集全部的邏輯來決定如何將Intent和Flags轉爲Activity並將其與Task和Stack關聯。算法

  • ActivityStackSupervisor

AMS 經過操做ActivityStackSupervisor來管理Activitysql

  • ActivityStack

ActivityStack從名稱來看是跟棧相關的類,其實它是一個管理類,用來管理系統全部Activity的各類狀態。它由ActivityStackSupervisor來進行管理的,而ActivityStackSupervisor在AMS中的構造方法中被建立。數據庫

  • ApplicationThread

它是ActivityThread的私有內部類,也是一個Binder對象。在此處它是做爲IApplicationThread對象的server端等待client端 的請求而後進行處理,最大的client就是AMS.

1.4 關於 Content 和 Application

Content 和 Application

1.4.1 Context 須知

  • Context 從字面上理解就是上下文的意思,在實際應用中它也確實是起到了管理 上下文環境中各個參數和變量的總用,方便咱們能夠簡單的訪問到各類資源。

Context

  • Context繼承關係

Context





2、Service


  • 能夠在後臺執行長時間運行操做而沒有用戶界面的應用組件

  • Service 分爲兩種工做狀態,一種是啓動狀態,主要用於執行後臺計算;另外一種是綁定狀態,主要用於其餘組件和 Service 的交互。

  • 對於同一 app 來講,默認狀況下 Service 和 Activity 是在同一個線程中的,main Thread (UI Thread)。

    Service



2.1啓動過程

啓動過程

2.1.1 註冊Service

  • Service仍是運行在主線程當中的,因此若是須要執行一些複雜的邏輯操做,最好在服務的內部手動建立子線程進行處理,不然會出現UI線程被阻塞的問題

2.1.2 ActivityThread.java

  • Service 的啓動

@UnsupportedAppUsage
private void handleCreateService(CreateServiceData data) {
    ···
    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = packageInfo.getAppFactory()
                .instantiateService(cl, data.info.name, data.intent);
    } 
    ···

    try {
        if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        context.setOuterContext(service);

        Application app = packageInfo.makeApplication(false, mInstrumentation);
        service.attach(context, this, data.info.name, data.token, app,
                ActivityManager.getService());
        service.onCreate();
        mServices.put(data.token, service);
        try {
            ActivityManager.getService().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    } 
    ··· 
}
複製代碼
  • ContextWrapper

Proxying代理 implementation實現 of Context that simply簡單的 delegates委託 all of its calls to another Context. Can be subclassed to歸爲 modify修改 behavior without changing the original Context.

ContextImpl做爲Context的抽象類,實現了全部的方法,咱們常見的getResources(),getAssets(),getApplication()等等的具體實現都是在ContextImpl的

  • AMP/AMN/AMS AT/ATP/ATN

這塊內容太多,好比能夠看看這個 AMP/AMN/AMS AT/ATP/ATN

Zygote 是android系統應用中一個至關重要的進程,其主要功能是執行Android應用程序。在android系統中運行新的應用,須要跟Zygote進程(擁有應用程序運行時所須要的各類元素和條件)結合後才能執行。

Zygote進程運行時,會初始化Dalvik虛擬機,並啓動它。android的應用程序是由java編寫的,不能直接以本地進程的形態運行在linux上,只能運行在Dalvik虛擬機中。而且每一個應用程序都運行在各自的虛擬機中,應用程序每次運行都要從新初始化並啓動虛擬機,這就至關耗時。在android中,應用程序運行前,Zygote進程經過共享已運行的虛擬機的代碼與內存信息,縮短應用程序運行所耗費的時間。而且,它會事先將應用程序要使用的android Fromework中的類和資源加載到內存中,並組織造成所用資源的連接信息。新運行的 android 應用程序在使用所須要的資源時沒必要每次從新造成資源的連接信息,這樣提升程序運行速度。

在android中,使用Zygote進程的目的?對於手機,爲了是應用程序在有限的資源型有更快的運行響應速度,提升資源利用率和設備使用時間。android使用Zygote來有效的減小系統負擔,提升運行速度。



2.2 綁定過程

綁定過程

2.2.1 只使用startService啓動服務的生命週期

  • 若是僅僅只是爲了開啓一個後臺任務,那麼可使用startService方法。

  • 若是想獲取Service中提供的代理對象,那麼必須經過bindService方法,進行綁定服務。 使用場景好比:音樂播放器,第三方支付等。

2.2.2 只使用BindService綁定服務的生命週期

  • Service 的 onRebind(Intent)方法, 若是在 onUnbind() 方法返回 true 的狀況下會執行,不然不執行。

2.2.3 同時使用startService()啓動服務、BindService()綁定服務的生命週期

  • 在Activity中能夠經過startService和bindService方法啓動Service。

2.2.4 ActivityThread.java

private void handleBindService(BindServiceData data) {
    Service s = mServices.get(data.token);
    ···
    if (s != null) {
        try {
            data.intent.setExtrasClassLoader(s.getClassLoader());
            data.intent.prepareToEnterProcess();
            try {
                if (!data.rebind) {
                    IBinder binder = s.onBind(data.intent);
                    ActivityManager.getService().publishService(
                            data.token, data.intent, binder);
                } else {
                    s.onRebind(data.intent);
                    ActivityManager.getService().serviceDoneExecuting(
                            data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                }
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        } 
        ···
    }
}
複製代碼


2.3 生命週期

生命週期

說明
START_NOT_STICKY 若是系統在 onStartCommand() 返回後終止服務,則除非有掛起 Intent 要傳遞,不然系統不會重建服務。這是最安全的選項,能夠避免在沒必要要時以及應用可以輕鬆重啓全部未完成的做業時運行服務
START_STICKY 若是系統在 onStartCommand() 返回後終止服務,則會重建服務並調用 onStartCommand(),但不會從新傳遞最後一個 Intent。相反,除非有掛起 Intent 要啓動服務(在這種狀況下,將傳遞這些 Intent ),不然系統會經過空 Intent 調用 onStartCommand()。這適用於不執行命令、但無限期運行並等待做業的媒體播放器(或相似服務
START_REDELIVER_INTENT 若是系統在 onStartCommand() 返回後終止服務,則會重建服務,並經過傳遞給服務的最後一個 Intent 調用 onStartCommand()。任何掛起 Intent 均依次傳遞。這適用於主動執行應該當即恢復的做業(例以下載文件)的服務


2.4 Service 與 Activity 通訊

Service 與 Activity 通訊



2.5 啓用前臺服務

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
複製代碼
Notification notification = new Notification(icon, text, System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, title, mmessage, pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
複製代碼

2.6 IntentService

IntentService是Service的子類,是一個異步的,會自動中止的服務,很好解決了傳統的Service中處理完耗時操做忘記中止並銷燬Service的問題

  • 區別

intentservice的優勢





3、BroadcastReceiver


  • 一種普遍運用在應用程序之間傳輸信息的機制,經過發送Intent來傳送咱們的數據

  • 內部通訊實現機制:經過android系統的Binder機制.

BroadcastReceiver

3.1 target 26 以後,沒法在 AndroidManifest 顯示聲明大部分廣播,除了一部分必要的廣播,如:

  1. ACTION_BOOT_COMPLETED :關於BOOT_COMPLETED廣播-自啓動
  2. ACTION_TIME_SET :與時間相關,計時廣播
  3. ACTION_LOCALE_CHANGED : locale信息改變以後,會發廣播消息Intent.ACTION_LOCALE_CHANGED
LocalBroadcastManager.getInstance(MainActivity.this).registerReceiver(receiver, filter);
複製代碼


3.2 註冊過程

  • 狀況文件註冊(靜態廣播)

  1. 一個app裏:自定義一個類繼承BroadcastReceiver而後要求重寫onReveiver方法
public class MyBroadCastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("MyBroadCastReceiver", "收到信息,內容是 :&emsp;" + intent.getStringExtra("info") + "");
    }
}
複製代碼
  1. 清單文件註冊,並設置Action,就那麼簡單完成接收準備工做
<receiver android:name=".MyBroadCastReceiver">
    <intent-filter>
        <action android:name="myBroadcast.action.call"/>
    </intent-filter>
</receiver>
複製代碼
  • 代碼註冊(動態廣播)

  1. 在和廣播接受者相同的app裏的MainActivity添加一個註冊按鈕,用來註冊廣播接收者
  2. 設置意圖過濾,添加Action
//onCreate建立廣播接收者對象
mReceiver = new MyBroadCastReceiver();              

//註冊按鈕
public void click(View view) {
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("myBroadcast.action.call");
    registerReceiver(mReceiver, intentFilter);
}
複製代碼
  1. 銷燬的時候取消註冊
@Override
protected void onDestroy() {
    unregisterReceiver(mReceiver);
    super.onDestroy();
}
複製代碼
  • 調用鏈

註冊過程

3.3 生命週期

  • BroadCastReceiver的生命週期

BroadCastReceiver的生命週期







4、ContentProvider


  • 內容提供者,它是用在不一樣的應用程序之間共享數據時,能夠把一個應用的數據提供給其餘的應用使用。



4.1 基本使用

  • ContentProvider 管理對結構化數據集的訪問。它們封裝數據,並提供用於定義數據安全性的機制。 內容提供程序是鏈接一個進程中的數據與另外一個進程中運行的代碼的標準界面。

  • ContentProvider 沒法被用戶感知,對於一個 ContentProvider 組件來講,它的內部須要實現增刪該查這四種操做,它的內部維持着一份數據集合,這個數據集合既能夠是數據庫實現,也能夠是其餘任何類型,如 List 和 Map,內部的 insert、delete、update、query 方法須要處理好線程同步,由於這幾個方法是在 Binder 線程池中被調用的。

  • ContentProvider 經過 Binder 向其餘組件乃至其餘應用提供數據。當 ContentProvider 所在的進程啓動時,ContentProvider 會同時啓動併發布到 AMS 中,須要注意的是,這個時候 ContentProvider 的 onCreate 要先於 Application 的 onCreate 而執行。

// Queries the user dictionary and returns results
mCursor = getContentResolver().query(
    UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
    mProjection,                        // The columns to return for each row
    mSelectionClause                    // Selection criteria
    mSelectionArgs,                     // Selection criteria
    mSortOrder);                        // The sort order for the returned rows
複製代碼
public class Installer extends ContentProvider {

    @Override
    public boolean onCreate() {
        return true;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }
}
複製代碼

4.2 ContentProvider 和 sql 區別

ContentProvider 和 sql 區別

總結:

  1. 不少人對於四大組件的理解,一直停留在使用上。本文限於篇幅,不可能展開。平時仍是要多看源碼,纔能有深刻地學習。
  2. Android 的知識點,若是說只是會使用,或者說能跑起來代碼,能夠說不多。可是若是要寫出一個優秀的 app,就至關艱深了,後續我將繼續對Android 的方方面面進行深刻地總結,也歡迎你們關注個人掘金一塊兒學習 _yuanhao 的編程世界




相關文章


按期分享Android開發溼貨,追求文章幽默與深度的完美統一。



爲了方便你們跟進學習,我在 GitHub 創建了一個倉庫


倉庫地址:超級乾貨!精心概括視頻、歸類、總結,各位路過的老鐵支持一下!給個 Star !



請點贊!由於你的鼓勵是我寫做的最大動力!

學Android
相關文章
相關標籤/搜索