全面總結(jié)Android面試知識要點:Android組件內(nèi)核面試題

請點贊兰迫,你的點贊對我意義重大,滿足下我的虛榮心炬称。
??常在河邊走汁果,哪有不濕鞋×崆或許面試過程中你遇到的問題就在這呢须鼎?
??關(guān)注我個人簡介鲸伴,面試不迷路~

一、Acitvity的生命周期晋控,如何摧毀一個Activity? (美團)

這道題想考察什么
  1. 是否了解AMS汞窗、Activity的相關(guān)知識?
考察的知識點
  1. AMS的基本知識
  2. Activity的啟動和銷毀流程
考生應(yīng)該如何回答
注意事項

本節(jié)內(nèi)容會涉及到AMS的內(nèi)容赡译,如果不了解建議先學(xué)習相關(guān)章節(jié)

Activity銷毀的相關(guān)流程圖
image.png
ActivityFinish流程以及結(jié)果接收的流程

在執(zhí)行完setResult以及finish之后仲吏,開始啟動Activity銷毀以及結(jié)果返回的流程。

setResult以及finish(用戶進程)

保存resultCode以及data蝌焚,并且通過finishActivity通知AMS開始銷毀當前Activity裹唆,并且攜帶參數(shù)

public final void setResult(int resultCode, Intent data) {
        synchronized (this) {
            mResultCode = resultCode;
            mResultData = data;
        }
    }

private void finish(int finishTask) {
        if (mParent == null) {
            int resultCode;
            Intent resultData;
            synchronized (this) {
                resultCode = mResultCode;
                resultData = mResultData;
            }
            if (false) Log.v(TAG, "Finishing self: token=" + mToken);
            try {
                if (resultData != null) {
                    resultData.prepareToLeaveProcess(this);
                }
                if (ActivityManager.getService()
                        .finishActivity(mToken, resultCode, resultData, finishTask)) {
                    mFinished = true;
                }
            } catch (RemoteException e) {
                // Empty
            }
        } else {
            mParent.finishFromChild(this);
        }
    }
ActivityManagerService.finishActivity(System_Server進程)

ActivityManagerService中,會判斷是否要終止整個Task只洒,如果是的話许帐,則直接把Task從ActivityStackSupervisor中移除,如果不是的話毕谴,則調(diào)用requestFinishActivityLoacked銷毀Activity成畦,并且將resultData傳入。

 final boolean finishWithRootActivity =
                        finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
                if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
                        || (finishWithRootActivity && r == rootR)) {
                    //  判斷是否要結(jié)束整個Task
                    res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, finishWithRootActivity);
                    if (!res) {
                        Slog.i(TAG, "Removing task failed to finish activity");
                    }
                } else {
                    //  如果不清除Task的話涝开,則只清理單獨的Activity
                    res = tr.getStack().requestFinishActivityLocked(token, resultCode,resultData, "app-request", true);
                    if (!res) {
                        Slog.i(TAG, "Failed to finish by app-request");
                    }
                }
ActivityStack.finishActivityLocked(System_Server進程)

ActivityStack.requestFinishActivityLocked最終會調(diào)用到finishActivityLocked函數(shù)中循帐。

  • 修改ActivityRecord中的finishing標識位為true
  • 停止接收Key事件
  • 將Result結(jié)果保存到接收者中
  • 開始暫停當前Activity
...
try {
            //  將ActivityRecord中的finishing置成true,表示正在處于Finishing狀態(tài)
            r.makeFinishingLocked();
            ...
            //  停止接收Key事件的分發(fā)
            r.pauseKeyDispatchingLocked();
            //  重新調(diào)整FocusedActivity棧
            adjustFocusedActivityStackLocked(r, "finishActivity");
            //  將resultData也就是返回的數(shù)據(jù)保存到r.resultTo的ActivityRecord中
            finishActivityResultsLocked(r, resultCode, resultData);
            //  如果當前的Activity是該Task最后一個Activity的話舀武,那么就需要銷毀Task
            final boolean endTask = index <= 0;
            if (mResumedActivity == r) {
                ...
                //  準備Activity切換的窗口動畫
                mWindowManager.prepareAppTransition(transit, false);
                //  告訴WindowManager準備當前的Activity準備移除
                r.setVisibility(false);
                //  如果當前沒有Pausing的Activity的話
                if (mPausingActivity == null) {
                    //  開始Pausing當前的Activity
                    startPausingLocked(false, false, null, pauseImmediately);
                }
                // 如果需要銷毀Task的話拄养,那么就開始銷毀
                if (endTask) {
                    mStackSupervisor.removeLockedTaskLocked(task);
                }
            } else if (r.state != ActivityState.PAUSING) {
                //  如果當前的Activity正處于Pausing 的狀態(tài)的話,那么就會等它Pausing完成之后開始銷毀银舱,否則在這個地方直接銷毀
                if (r.visible) {
                    prepareActivityHideTransitionAnimation(r, transit);
                }
                final int finishMode = (r.visible || r.nowVisible) ? FINISH_AFTER_VISIBLE
                        : FINISH_AFTER_PAUSE;
                //  直接調(diào)用finishCurrentActivityLocked銷毀當前的Activity
                final boolean removedActivity = finishCurrentActivityLocked(r, finishMode, oomAdj)
                        == null;
                ...
            }
...
ActivityStack. startPausingLocked(System_Server進程)
  • 更新mResumedActivity瘪匿、prev.state
  • 執(zhí)行schedulePauseActivity暫停Activity
  • 延遲發(fā)送消息,檢測Pause超時
...
ActivityRecord prev = mResumedActivity;
if (prev == null) {
      if (resuming == null) {
         // 如果沒有要暫停的Activity的話寻馏,就直接resume棧頂?shù)腁ctivity
         mStackSupervisor.resumeFocusedStackTopActivityLocked();
      }
      return false;
}
...
//  更新當前保存的ResumedActivity棋弥,因為即將進入Pausing狀態(tài)
mResumedActivity = null;
//  將正在Pausing的Activity賦值為即將進入Pausing的Activity
mPausingActivity = prev;
mLastPausedActivity = prev;
mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
                || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
//  將當前Activity狀態(tài)置成Pausing
prev.state = ActivityState.PAUSING;
... 
final ActivityRecord next = mStackSupervisor.topRunningActivityLocked();
if (prev.app != null && prev.app.thread != null) {
       ...
       //  回調(diào)Activity的Pause函數(shù),pauseImmediately為false
       prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                        userLeaving, prev.configChangeFlags, pauseImmediately);
           ...
        } else {
            mPausingActivity = null;
            mLastPausedActivity = null;
            mLastNoHistoryActivity = null;
        }
}
...
        if (mPausingActivity != null) {
            //  判斷是否是立即暫停操软,如果是的話嘁锯,則立即調(diào)用completePauseLocked
            if (pauseImmediately) {
                // If the caller said they don't want to wait for the pause, then complete
                // the pause now.
                completePauseLocked(false, resuming);
                return false;
            } else {
                //  如果不是立即暫停的話宪祥,那么就會等待Activity的onPause完畢聂薪,并且檢測超時
                //  如果pause超時的話,也會繼續(xù)執(zhí)行下面的流程
                schedulePauseTimeout(prev);
                return true;
            }
        } else {
            // This activity failed to schedule the
            // pause, so just treat it as being paused now.
            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
            if (resuming == null) {
                mStackSupervisor.resumeFocusedStackTopActivityLocked();
            }
            return false;
        }
ActivityStack.activityPausedLocked(System_Server進程)

當Activity進入Pause的狀態(tài)后蝗羊,會通過Binder回調(diào)到activityPausedLocked該接口藏澳。

        // 根據(jù)mToken找到ActivityRecord
        final ActivityRecord r = isInStackLocked(token);
        if (r != null) {
            //  接收到ActivityPause完成的回調(diào)后,清理PAUSE_TIMEOUT_MSG
            mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
        }
         //  判斷當前Pausing的Activity是否是已經(jīng)回調(diào)過Pause的Activity
         if (mPausingActivity == r) {
             ...
             //  如果是的話耀找,則調(diào)用該函數(shù)
             completePauseLocked(true /* resumeNext */, null /* resumingActivity */)翔悠;
         } else {
            ...
            if (r.state == ActivityState.PAUSING) {
                    r.state = ActivityState.PAUSED;
                    if (r.finishing) {
                        if (DEBUG_PAUSE) Slog.v(TAG,
                                "Executing finish of failed to pause activity: " + r);
                        finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false);
                    }
             }
        }
ActivityStack.completePauseLocked(System_Server進程)

如果Pause完畢之后业崖,則開始處理Stop的流程。

  • 如果prev.finishing為true的話蓄愁,則會執(zhí)行finishCurrentActivityLocked開始銷毀
  • 接著調(diào)用resumeFocusedStackTopActivityLocked開始Resume接下來的Activity
         ActivityRecord prev = mPausingActivity;
        if (prev != null) {
            //  判斷當前是否是STOPPING的狀態(tài)
            final boolean wasStopping = prev.state == STOPPING;
            //  將當前狀態(tài)該成PAUSED
            prev.state = ActivityState.PAUSED;
            //  如果是finishing的話双炕,則調(diào)用finishCurrentActivityLocked開始銷毀
            if (prev.finishing) {
                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
                prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false);
            } 
            ...
            //  將PausingActivity置空
             mPausingActivity = null;
        }
        //  開始resume下一個Activity
        if (resumeNext) {
            final ActivityStack topStack = mStackSupervisor.getFocusedStack();
            //  如果當前沒有關(guān)機或者休眠的話
            if (!mService.isSleepingOrShuttingDownLocked()) {
                //  調(diào)用該函數(shù)開始resumeActivity
                mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
            } else {
                mStackSupervisor.checkReadyForSleepLocked();
                ActivityRecord top = topStack.topRunningActivityLocked();
                if (top == null || (prev != null && top != prev)) {
                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
                }
            }
        }
ActivityStack. resumeTopActivityInnerLocked(System_Server進程)

當Pausing完畢后,會執(zhí)行ActivityStackSupervisor.resumeFocusedStackTopActivityLocked來開始Resume下一個要顯示的Activity撮抓。最終會執(zhí)行到resumeTopActivityInnerLocked該函數(shù)妇斤。

  • 回調(diào)ActivityResult的結(jié)果,即回調(diào)onActivityResult處理返回數(shù)據(jù)
  • 回調(diào)newIntent丹拯,即回調(diào)onNewIntent
  • 最后開始處理resume流程
 // Deliver all pending results.
                ArrayList<ResultInfo> a = next.results;
                if (a != null) {
                    final int N = a.size();
                    if (!next.finishing && N > 0) {
                        if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
                                "Delivering results to " + next + ": " + a);
                        //  如果有ActivityResult需要回調(diào)的話站超,先回調(diào)onActivityResult
                        next.app.thread.scheduleSendResult(next.appToken, a);
                    }
                }

                if (next.newIntents != null) {
                    //  如果有newIntent的話,先回調(diào)onNewIntent
                    next.app.thread.scheduleNewIntent(
                            next.newIntents, next.appToken, false /* andPause */);
                }

               ... 
              //  開始回調(diào)onResume
                next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
                        mService.isNextTransitionForward(), resumeAnimOptions);
ActivityStack.finishCurrentActivityLocked(System_Server進程)

該函數(shù)主要用來完成銷毀當前Activity乖酬。

//  獲取下一個要Resume的Activity
final ActivityRecord next = mStackSupervisor.topRunningActivityLocked()

if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible)
                && next != null && !next.nowVisible) {
            //  如果要銷毀的Activity目前還是可見的死相,而要Resume的Activity是不可見的
            //  那么就先把要銷毀的Activity放到Stopping隊列中,先把要展示的Activity
            //  先進行Resume操作咬像,等Resume完后算撮,回頭再銷毀這個Activity
            if (!mStackSupervisor.mStoppingActivities.contains(r)) {
                addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */);
            }
            ...
            //  將要銷毀的Activity改狀態(tài)為STOPPING
            r.state = STOPPING;
            ...
            return r;
        }
//  如果mode不是FINISH_AFTER_VISIBLE的話,則從各種隊列中先清除
        mStackSupervisor.mStoppingActivities.remove(r);
        mStackSupervisor.mGoingToSleepActivities.remove(r);
        mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(r);
        if (mResumedActivity == r) {
            mResumedActivity = null;
        }
        //  將狀態(tài)從STOPPING變成FINISHING
       final ActivityState prevState = r.state;
        r.state = ActivityState.FINISHING;
if (mode == FINISH_IMMEDIATELY
                || (prevState == ActivityState.PAUSED
                    && (mode == FINISH_AFTER_PAUSE || mStackId == PINNED_STACK_ID))
                || finishingActivityInNonFocusedStack
                || prevState == STOPPING
                || prevState == STOPPED
                || prevState == ActivityState.INITIALIZING) {
          ... 
          //  開始銷毀Activity
            boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm");

            if (finishingActivityInNonFocusedStack) {
                //  如果Activity不在Focus的Stack中的話施掏,則確保Resume的Activity窗口是可見的
                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
            }
            if (activityRemoved) {
                mStackSupervisor.resumeFocusedStackTopActivityLocked();
            }
            return activityRemoved ? null : r;
        }
ActivityThread.handleResumeActivity(用戶進程)

待Activity處于Resume狀態(tài)時钮惠,ActivityThread會調(diào)用該函數(shù)。

  • 回調(diào)Activity的onRestart七芭、onStart素挽、onResume函數(shù)
  • 處理Activity Window相關(guān)的數(shù)據(jù)保存
  • 如果是ActivityManagerService來的請求,則會將Idler加到主線程隊列中狸驳,等待主線程空閑時预明,回調(diào)ActivityManagerService.activityIdle
  • 回調(diào)ActivityManagerService. activityResumed告知已經(jīng)處于Resume狀態(tài)
//  獲取Activity在用戶進程的ActivityClientRecord
ActivityClientRecord r = mActivities.get(token);
...
//  回調(diào)Activity的onResume
 r = performResumeActivity(token, clearHide, reason);

if (r != null) {
            final Activity a = r.activity;
            ...
            //  處理Window相關(guān)的事情
            ...
            //  如果是從ActivityManagerService要求的Resume操作的話
            if (!r.onlyLocalRequest) {
                r.nextIdle = mNewActivities;
                mNewActivities = r;
                if (localLOGV) Slog.v(
                    TAG, "Scheduling idle handler for " + r);
                //  將Idler添加到Looper的隊列中,而在Idler中
                //  當主線程隊列空閑的時候會回調(diào)am.activityIdle
                Looper.myQueue().addIdleHandler(new Idler());
            }
            r.onlyLocalRequest = false;

            // Tell the activity manager we have resumed.
            if (reallyResume) {
                try { 
                    //  告知ActivityManagerService當前的Activity處于Resume狀態(tài)了
                    ActivityManager.getService().activityResumed(token);
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            }
ActivityStackSupervisor.activityIdleInternal(System_Server進程)

當用戶進程處于空閑狀態(tài)時耙箍,就會回調(diào)ActivityManagerService.activityIdle撰糠。而在該接口中,就會調(diào)用activityIdleInternalLocked函數(shù)辩昆。

//  根據(jù)token找到已經(jīng)Resume的ActivityRecord
 ActivityRecord r = ActivityRecord.forTokenLocked(token);
 if (r != null) {
        //   先移除IDLE_TIMEOUT_MSG的消息
        mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
        ...
}
//  從mStoppingActivities列表中將要stop的Activity選出來
final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(r,true /* remove */, processPausingActivities);
//  獲取StopActivity的數(shù)量
 NS = stops != null ? stops.size() : 0;
//  判斷當前是否有Finishing的Activity
 if ((NF = mFinishingActivities.size()) > 0) {
        finishes = new ArrayList<>(mFinishingActivities);
         mFinishingActivities.clear();
 }
...
//  遍歷所有的StopActivity
 for (int i = 0; i < NS; i++) {
            r = stops.get(i);
            final ActivityStack stack = r.getStack();
            if (stack != null) {
            //  判斷是否要finish阅酪,之前有標記過該位
                if (r.finishing) {
                    // 如果finishing的話,則調(diào)用該函數(shù)立即finish
                    stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false);
                } else {
                    //  否則只是調(diào)用onStop
                    stack.stopActivityLocked(r);
                }
            }
        }
        //  銷毀finishing隊列中的Activity
        for (int i = 0; i < NF; i++) {
            r = finishes.get(i);
            final ActivityStack stack = r.getStack();
            if (stack != null) {
                activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
            }
        }
ActivityStack.destroyActivityLocked(System_Server進程)
//  清理ActivityRecord相關(guān)的隊列與窗口
  cleanUpActivityLocked(r, false, false);
//  判斷Activity進程是否存在
  final boolean hadApp = r.app != null;
if (hadApp) {
      //  如果是finishing的話汁针,則是從Activities中移除
     if (removeFromApp) {
        //  移除該ActivityRecord
         r.app.activities.remove(r);
     }
     ...
      boolean skipDestroy = false;
      try {
                //  回調(diào)用戶進程的scheduleDestroyActivity术辐,開始銷毀Activity
                r.app.thread.scheduleDestroyActivity(r.appToken, r.finishing,
                        r.configChangeFlags);
      } catch (Exception e) {
                if (r.finishing) {
                    removeActivityFromHistoryLocked(r, reason + " exceptionInScheduleDestroy");
                    removedFromHistory = true;
                    //  如果銷毀失敗的話,置標識位
                    skipDestroy = true;
                }
       }
      ...
      if (r.finishing && !skipDestroy) {
               //  如果執(zhí)行了Activity用戶進程的onDestroy的話施无,
                r.state = ActivityState.DESTROYING;
               //  則需要定時檢測用戶進程將銷毀成功的消息發(fā)送回來
                Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG, r);
                mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
            } else {
                //  如果出現(xiàn)意外了辉词,則要清理app了
                r.state = ActivityState.DESTROYED;
                r.app = null;
            }

二、Activity的4大啟動模式猾骡,與開發(fā)中需要注意的問題瑞躺,如onNewIntent() 的調(diào)用

這道題想考察什么敷搪?
  1. 是否了解Activity的啟動模式?
考察的知識點
  1. Activity的啟動模式
  2. 不同啟動模式運用場景
  3. 開發(fā)中的注意事項
考生應(yīng)該如何回答
一. Android啟動模式詳解
image.png
1. Standard 標準模式

說明: Android創(chuàng)建Activity時的默認模式幢哨,如果沒有為Activity設(shè)置啟動模式的話赡勘,默認為標準模式。每次啟動一個Activity都會重新創(chuàng)建一個新的實例入棧捞镰,不管這個實例是否存在狮含。

生命周期: 如上所示,每次被創(chuàng)建的實例Activity 的生命周期符合典型情況曼振,它的onCreate几迄、onStart、onResume都會被調(diào)用冰评。

舉例: 此時Activity 棧中以此有A映胁、B、C三個Activity甲雅,此時C處于棧頂解孙,啟動模式為Standard 模式。若在C Activity中添加點擊事件抛人,需要跳轉(zhuǎn)到另一個同類型的C Activity弛姜。結(jié)果是另一個C Activity進入棧中,成為棧頂妖枚。

image.png
2. SingleTop 棧頂復(fù)用模式

說明:分兩種處理情況:需要創(chuàng)建的Activity已經(jīng)處于棧頂時廷臼,此時會直接復(fù)用棧頂?shù)腁ctivity,不會再創(chuàng)建新的Activity绝页;若需要創(chuàng)建的Activity不處于棧頂荠商,此時會重新創(chuàng)建一個新的Activity入棧,同Standard模式一樣续誉。

生命周期:若情況一中棧頂?shù)腁ctivity被直接復(fù)用時莱没,它的onCreate、onStart不會被系統(tǒng)調(diào)用酷鸦,因為它并沒有發(fā)生改變饰躲,但是一個新的方法 onNewIntent會被回調(diào)(Activity被正常創(chuàng)建時不會回調(diào)此方法)。

舉例:此時Activity 棧中以此有A臼隔、B嘹裂、C三個Activity,此時C處于棧頂躬翁,啟動模式為SingleTop 模式焦蘑。情況一:在C Activity中添加點擊事件盯拱,需要跳轉(zhuǎn)到另一個同類型的C Activity盒发。結(jié)果是直接復(fù)用棧頂?shù)腃 Activity例嘱。情況二:在C Activity中添加點擊事件,需要跳轉(zhuǎn)到另一個A Activity宁舰。結(jié)果是創(chuàng)建一個新的Activity入棧拼卵,成為棧頂。

[圖片上傳失敗...(image-6e3769-1685949643113)]

3. SingleTask 棧內(nèi)復(fù)用模式

說明:若需要創(chuàng)建的Activity已經(jīng)處于棧中時蛮艰,此時不會創(chuàng)建新的Activity腋腮,而是將存在棧中的Activity上面的其它Activity全部銷毀,使它成為棧頂壤蚜。

生命周期:同SingleTop 模式中的情況一相同即寡,只會重新回調(diào)Activity中的 onNewIntent方法

舉例:此時Activity 棧中以此有A、B袜刷、C三個Activity聪富,此時C處于棧頂,啟動模式為SingleTask 模式著蟹。情況一:在C Activity中添加點擊事件墩蔓,需要跳轉(zhuǎn)到另一個同類型的C Activity。結(jié)果是直接用棧頂?shù)腃 Activity萧豆。情況二:在C Activity中添加點擊事件奸披,需要跳轉(zhuǎn)到另一個A Activity。結(jié)果是將A Activity上面的B涮雷、C全部銷毀阵面,使A Activity成為棧頂。

[圖片上傳失敗...(image-bf892f-1686034647957)]

4. SingleInstance 單實例模式

說明: SingleInstance比較特殊洪鸭,是全局單例模式膜钓,是一種加強的SingleTask模式,它除了具有它所有特性外卿嘲,還加強了一點:具有此模式的Activity只能單獨位于一個任務(wù)棧中颂斜。這個常用于系統(tǒng)中的應(yīng)用,例如Launch拾枣、鎖屏鍵的應(yīng)用等等沃疮,整個系統(tǒng)中只有一個!所以在我們的應(yīng)用中一般不會用到梅肤,了解即可司蔬。

舉例: 比如 A Activity是該模式,啟動A后姨蝴,系統(tǒng)會為它創(chuàng)建一個單獨的任務(wù)棧俊啼,由于棧內(nèi)復(fù)用的特性,后續(xù)的請求均不會創(chuàng)建新的Activity左医,除非這個獨特的任務(wù)棧被系統(tǒng)銷毀授帕。

二.啟動模式的使用方法
1. 在 Manifest.xml中指定Activity啟動模式

一種靜態(tài)注冊同木,在Manifest.xml文件中聲明Activity的同時指定它的啟動模式,這樣在代碼中跳轉(zhuǎn)時會按照指定的模式來創(chuàng)建Activity跛十。例子如下:

<activity android:name="..activity.MultiportActivity" android:launchMode="singleTask"/>
2. 啟動Activity時彤路,在Intent中指定啟動模式去創(chuàng)建Activity

一種動態(tài)的啟動模式,在new 一個Intent后芥映,通過Intent的addFlags方法去動態(tài)指定一個啟動模式洲尊。例子如下:

Intent intent = new Intent();
intent.setClass(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);

注意: 以上兩種方式都可以為Activity指定啟動模式,但是二者還是有不一樣的奈偏。

(1)優(yōu)先級:動態(tài)指定方式即第二種比第一種優(yōu)先級要坞嘀,若兩者同時存在,是以第二種方式為準惊来。 (2)限定范圍:第一種方式無法為Activity直接指定 FLAG_ACTIVITY_CLEAR_TOP 標識姆吭,第二種方式無法為Activity指定 singleInstance 模式。

三. Activity 的 Flags

標記位既可以設(shè)定Activity的啟動模式唁盏,如同上面介紹的内狸,在動態(tài)指定啟動模式,比如 FLAG_ACTIVITY_NEW_TASKFLAG_ACTIVITY_SINGLE_TOP 等厘擂。它還可以影響Activity 的運行狀態(tài) 昆淡,比如 FLAG_ACTIVITY_CLEAN_TOPFLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 等。下面介紹幾個主要的標記位刽严,不要死記昂灵,理解幾個即可,需要時再查官方文檔舞萄。

1. FLAG_ACTIVITY_NEW_TASK

作用是為Activity指定 “SingleTask”啟動模式眨补,跟在AndroidMainfest.xml指定效果相同。

2. FLAG_ACTIVITY_SINGLE_TOP

作用是為Activity指定 “SingleTop”啟動模式倒脓,跟在AndroidMainfest.xml指定效果相同撑螺。

3. FLAG_ACTIVITY_CLEAN_TOP

具有此標記位的Activity,啟動時會將與該Activity在同一任務(wù)棧的其它Activity出棧崎弃,一般與SingleTask啟動模式一起出現(xiàn)甘晤。它會完成SingleTask的作用,但其實SingleTask啟動模式默認具有此標記位的作用

4.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

具有此標記位的Activity不會出現(xiàn)在歷史Activity的列表中饲做,使用場景:當某些情況下我們不希望用戶通過歷史列表回到Activity時线婚,此標記位便體現(xiàn)了它的效果。它等同于在xml中指定Activity的屬性:

android:excludeFromRecents="trure"
四. 啟動模式的實際應(yīng)用場景

這四種模式中的Standard模式是最常見的一種盆均,沒有什么特別注意塞弊,而SingleInstance模式是整個系統(tǒng)的單例模式,在我們的應(yīng)用中一般不會應(yīng)用到,所以游沿,這里就具體講解 SingleTop 和 SingleTask模式的運用場景:

  1. SingleTask模式的運用場景 最常見的應(yīng)用場景就是保持我們應(yīng)用開啟后只有一個Activity的實例饰抒,最典型的例子就是應(yīng)用中展示的主頁(Home頁)。假設(shè)用戶在主頁跳轉(zhuǎn)到其它頁面奏候,執(zhí)行多次操作后想返回到主頁,如果不使用SingleTask模式唇敞,在點擊返回的過程中會多次看到主頁蔗草,這明顯就是設(shè)計不合理了。
  1. SingleTop模式的運用場景 如果你在當前的Activity中又要啟動同類型的Activity疆柔,此時建議將此類型Activity的啟動模式指定為SingleTop咒精,可以減少Activity的創(chuàng)建,節(jié)省內(nèi)存旷档!
  1. 注意:復(fù)用Activity時的生命周期回調(diào) 這里還需要考慮一個Activity跳轉(zhuǎn)時攜帶頁面參數(shù)的問題模叙。

因為當一個Activity設(shè)置了SingleTop或者SingleTask模式后,跳轉(zhuǎn)此Activity出現(xiàn)復(fù)用原有Activity的情況時鞋屈,此Activity的onCreate方法將不會再次執(zhí)行范咨!onCreate方法只會在第一次創(chuàng)建Activity時被執(zhí)行。

而一般onCreate方法中會進行該頁面的數(shù)據(jù)初始化厂庇、UI初始化渠啊,如果頁面的展示數(shù)據(jù)無關(guān)頁面跳轉(zhuǎn)傳遞的參數(shù),則不必擔心此問題权旷,若頁面展示的數(shù)據(jù)就是通過getInten() 方法來得到替蛉,那么問題就會出現(xiàn):getInten()獲取的一直都是舊數(shù)據(jù),根本無法接收跳轉(zhuǎn)時傳送的新數(shù)據(jù)拄氯!下面躲查,通過一個例子來詳解:

Manifest.xml
        <activity
            android:name=".activity.CourseDetailActivity"
            android:launchMode="singleTop"
            android:screenOrientation="portrait" />
public class CourseDetailActivity extends BaseActivity{
  ......
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_course_detail_layout);
        initData();
        initView();
    }

   //初始化數(shù)據(jù)
    private void initData() {
        Intent intent = getIntent();
        mCourseID = intent.getStringExtra(COURSE_ID);
    }

    //初始化UI
    private void initView() {
    ......
    }
    ......
}

以上代碼中的CourseDetailActivity在配置文件中配置了啟動模式是SingleTop模式,根據(jù)上面啟動模式的介紹可得知译柏,當CourseDetailActivity處于棧頂時镣煮,再次跳轉(zhuǎn)頁面到CourseDetailActivity時會直接復(fù)用原有的Activity,而且此頁面需要展示的數(shù)據(jù)是從getIntent()方法得來鄙麦,可是initData()方法不會再次被調(diào)用怎静,此時頁面就無法顯示新的數(shù)據(jù)。

當然這種情況系統(tǒng)早就為我們想過了黔衡,這時我們需要另外一個回調(diào) onNewIntent(Intent intent)方法蚓聘,此方法會傳入最新的intent,這樣我們就可以解決上述問題盟劫。這里建議的方法是重新去setIntent夜牡,然后重新去初始化數(shù)據(jù)和UI,代碼如下所示:

/*
* 復(fù)用Activity時的生命周期回調(diào)
*/
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        initData();
        initView();
    }

這樣,在一個頁面中可以重復(fù)跳轉(zhuǎn)并顯示不同的內(nèi)容塘装。


三急迂、Intent顯示跳轉(zhuǎn)與隱式跳轉(zhuǎn),如何使用蹦肴?(美團)

這道題想考察什么僚碎?
  1. 是否了解Intent跳轉(zhuǎn)的真實場景使用,是否熟悉Intent使用場景阴幌?
考察的知識點
  1. Intent跳轉(zhuǎn)處理在項目中使用與基本知識
考生應(yīng)該如何回答
1.顯示意圖與隱式意圖的區(qū)別勺阐,說說你的簡單理解?

答:

在Activity的跳轉(zhuǎn)中

Activity的跳轉(zhuǎn)需要創(chuàng)建Intent對象矛双,通過設(shè)置intent對象的參數(shù)指定要跳轉(zhuǎn)Activity 通過設(shè)置Activity的包名和類名實現(xiàn)跳轉(zhuǎn)渊抽,稱為顯式意圖 通過指定動作實現(xiàn)跳轉(zhuǎn),稱為隱式意圖

顯式意圖
  • 跳轉(zhuǎn)到同一項目下的另一個Activity议忽,直接指定該Activity的字節(jié)碼即可 Intent intent = new Intent(); intent.setClass(this, SecondActivity.class); startActivity(intent);
  • 跳轉(zhuǎn)至其他App中的Activity懒闷,需要指定該應(yīng)用的包名和該Activity的類名 Intent intent = new Intent(); //啟動系統(tǒng)自帶的撥號器應(yīng)用 intent.setClassName("com.android.dialer", "com.android.dialer.DialtactsActivity"); startActivity(intent);
隱式意圖
  • 隱式意圖跳轉(zhuǎn)至指定Activity Intent intent = new Intent(); //啟動系統(tǒng)自帶的撥號器應(yīng)用 intent.setAction(Intent.ACTION_DIAL); startActivity(intent);
  • 要讓一個Activity可以被隱式啟動,需要在清單文件的activity節(jié)點中設(shè)置intent-filter子節(jié)點 <intent-filter > <action android:name="com.itheima.second"/> <data android:scheme="asd" android:mimeType="aa/bb"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter>
  • action 指定動作(可以自定義栈幸,可以使用系統(tǒng)自帶的)
  • data 指定數(shù)據(jù)(操作什么內(nèi)容)
  • category 類別 (默認類別愤估,機頂盒,車載電腦)
  • 隱式意圖啟動Activity速址,需要為intent設(shè)置以上三個屬性灵疮,且值必須與該Activity在清單文件中對三個屬性的定義匹配
  • intent-filter節(jié)點及其子節(jié)點都可以同時定義多個,隱式啟動時只需與任意一個匹配即可
獲取通過setData傳遞的數(shù)據(jù)

/ /獲取啟動此Activity的intent對象 Intent intent = getIntent(); Uri uri = intent.getData();

顯式意圖和隱式意圖的應(yīng)用場景
  • 顯式意圖用于啟動同一應(yīng)用中的Activity
  • 隱式意圖用于啟動不同應(yīng)用中的Activity
  • 如果系統(tǒng)中存在多個Activity的intent-filter同時與你的intent匹配壳繁,那么系統(tǒng)會顯示一個對話框震捣,列出所有匹配的Activity,由用戶選擇啟動哪一個

2.你在工作中闹炉,留意過在Android中Intent顯示跳轉(zhuǎn)和隱式跳轉(zhuǎn)蒿赢,如何使用?

答:

顯式 Intent 調(diào)用:
    // 創(chuàng)建一個顯式的 Intent 對象(方法一:在構(gòu)造函數(shù)中指定)
     Intent intent = new Intent(Intent_Demo1.this, Intent_Demo1_Result1.class);

     Bundle bundle = new Bundle();
     bundle.putString("id", strID);
     intent.putExtras(bundle);

     intent.putExtra("name", "derry");
     intent.putExtra("userInfo", new UserInfo(1, "name"));
     startActivity(intent);

     // 創(chuàng)建一個顯式的 Intent 對象(方法二:用 setClass 方法)
     Intent intent = new Intent();
     Bundle bundle = new Bundle();
     bundle.putString("id", strID);
     intent.setClass(Intent_Demo1.this, Intent_Demo1_Result1.class);
     intent.putExtras(bundle);
     startActivity(intent);

     // 創(chuàng)建一個顯式的 Intent 對象(方法三:用 setClass 方法)
     Intent intent = new Intent();
     Bundle bundle = new Bundle();
     bundle.putString("id", strID);
     intent.setClassName(Intent_Demo1.this, "com.great.activity_intent.Intent_Demo1_Result1");
     intent.putExtras(bundle);
     startActivity(intent);

     //創(chuàng)建一個顯式的 Intent 對象(方法四:用 setComponent 方法)
     Intent intent = new Intent();
     Bundle bundle = new Bundle();
     bundle.putString("id", strID);
     //setComponent方法的參數(shù):ComponentName
     intent.setComponent(new ComponentName(Intent_Demo1.this,
                                           Intent_Demo1_Result1.class));
     intent.putExtras(bundle);
     startActivity(intent);
Intent隱式跳轉(zhuǎn) Action:
    // 創(chuàng)建一個隱式的 Intent 對象:Action 動作
    /**
     * 這里指定的是 AndroidManifest.xml 文件中配置的
     * <intent-filter>標簽中的<action android:name="com.great.activity_intent
     .Intent_Demo1_Result3" />
     * 所在的 Activity渣触,注意這里都要設(shè)置 <category  android:name="android.intent.
     category.DEFAULT" />
     */

    Intent intent = new Intent();
    // 設(shè)置 Intent 的動作
    intent.setAction("com.great.activity_intent.Intent_Demo1_Result3");
    Bundle bundle = new Bundle();
    bundle.putString("id", strID);
    intent.putExtras(bundle);
    startActivity(intent);
AndroidManifest.xml:
        <activity android:name="Intent_Demo1_Result3"
                  android:label="Intent_Demo1_Result3">
            <intent-filter>
                <action android:name="com.great.activity_intent.Intent_Demo1_Result3" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
Category 類別:
// 創(chuàng)建一個隱式的 Intent 對象:Category 類別
Intent intent = new Intent();
intent.setAction("com.great.activity_intent.Intent_Demo1_Result33");
/**
 * 不指定 Category 或 只指定 AndroidManifest.xml 文件中 <intent-filter> 
 * 標簽中配置的任意一個 Category
 * <category android:name="android.intent.category.DEFAULT" /> 除外羡棵,就可以訪問該 Activity,
 */
intent.addCategory(Intent.CATEGORY_INFO);
intent.addCategory(Intent.CATEGORY_DEFAULT);
Bundle bundle = new Bundle();
bundle.putString("id", strID);
intent.putExtras(bundle);
startActivity(intent);
AndroidManifest.xml:
        <activity android:name="Intent_Demo1_Result2"
                  android:label="Intent_Demo1_Result2">
            <intent-filter>
                
                <category android:name="android.intent.category.INFO" />
                <category android:name="android.intent.category.BROWSABLE" />
                <category android:name="android.intent.category.DEFAULT" />
                
            </intent-filter>
        </activity>
Date 數(shù)據(jù) 跳轉(zhuǎn):
// 創(chuàng)建一個隱式的 Intent 對象嗅钻,方法四:Date 數(shù)據(jù)
Intent intent = new Intent();
Uri uri = Uri.parse("http://www.great.org:8080/folder/subfolder/etc/abc.pdf");

// 注:setData皂冰、setDataAndType、setType 這三種方法只能單獨使用养篓,不可共用
// 要么單獨以 setData 方法設(shè)置 URI
// intent.setData(uri);
// 要么單獨以 setDataAndType 方法設(shè)置 URI 及 mime type
intent.setDataAndType(uri, "text/plain");
// 要么單獨以 setDataAndType 方法設(shè)置 Type
//intent.setType("text/plain");

/**
 * 不指定 Category 或 只指定 AndroidManifest.xml 文件中 <intent-filter> 標簽中配置的任意一個 Category
 * <category android:name="android.intent.category.DEFAULT" /> 除外秃流,就可以訪問該 Activity
 */
Bundle bundle = new Bundle();
bundle.putString("id", strID);
intent.putExtras(bundle);
startActivity(intent);
AndroidManifest.xml:
        <activity android:name="Intent_Demo1_Result2"
                  android:label="Intent_Demo1_Result2">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT" />
                <data
                    android:scheme="http"
                    android:host="www.great.org"
                    android:port="8080"
                    android:pathPattern=".*pdf"
                    android:mimeType="text/plain"/>
            </intent-filter>
        </activity>

四、Activity A跳轉(zhuǎn)B柳弄,B跳轉(zhuǎn)C舶胀,A不能直接跳轉(zhuǎn)到C,A如何傳遞消息給C?(美團)

這道題想考察什么嚣伐?
  1. 是否了解Activity間信息傳遞糖赔?
考察的知識點
  1. 消息傳遞的機制
  2. 事件總線
  3. 本地數(shù)據(jù)存儲
考生應(yīng)該如何回答
消息通信機制

Android 開發(fā)之中我們常常需要應(yīng)用到消息傳遞,消息的傳遞有多種方式轩端。消息傳遞的作用不必多說放典,主要是在不同的組件之間傳播消息或者共享某些狀態(tài)等,以下是幾種常用的消息傳遞機制:

  • 靜態(tài)變量
  • 全局變量 及Application
  • Android系統(tǒng)剪切板
  • 本地化存儲方式
  • Android組件
  • EventBus
  • LiveDataBus
靜態(tài)變量 和 全局變量基茵、Application && Android 系統(tǒng)剪切板

這三種方式其實非常類似奋构,靜態(tài)變量和全局變量都可以采用static的方式來聲明,如果采用這種方式還是推薦用一個專門的類結(jié)合單體模式進行管理耿导,盡量減少對內(nèi)存的消耗声怔。 而使用系統(tǒng)剪切板的方式一般也很少用态贤,比較多限制舱呻,容易丟失數(shù)據(jù),幾乎沒有看到有這樣用的悠汽。

Application: 可以通過在Application 中的全局靜態(tài)變量來實現(xiàn)

這里還有利用Application進行共享Handler來消息傳遞箱吕,方法很簡單,就是在Application中定義一個全局的Handler柿冲,雖然這種方法可以實現(xiàn)茬高,但是卻保留了在整個App中保留了全局的Handler,如果在Handler的設(shè)置中引用了某個Activity假抄,就容易造成內(nèi)存泄露了怎栽。

本地化存儲方式

本地存儲方式有如下三種:

SharedPreference SQLite File

這三種方式的好處就是他們是持久存儲的,只要不卸載APP或者不刪除文件就可以一直保存下去宿饱,而且也幾乎沒有大小的限制熏瞄,可以做一些統(tǒng)計。不過缺點也比較明顯谬以,這三種方式最好是采用多線程來進行讀寫强饮,尤其是數(shù)據(jù)量大的時候,我們知道为黎,IO的操作是非常耗費時間的邮丰,所以盡量不要在UI線程中使用這三種方式讀寫。

示例代碼:

SharedPreference:

// 發(fā)送消息
SharedPreferences.Editor editor = MainActivity.this.getSharedPreferences("SEND", Context.MODE_PRIVATE).edit();
editor.putString("SEND","SharedPreferences的消息");
editor.apply();
startActivity(new Intent(MainActivity.this,ReceiveActivity.class));
// 接收消息
SharedPreferences sharedPreferences = getSharedPreferences("SEND", MODE_PRIVATE);
textView.setText(sharedPreferences.getString("SEND", ""));
附支持的數(shù)據(jù)類型如下: 

SQLite

SQLite需要先創(chuàng)建數(shù)據(jù)庫铭乾,后面向數(shù)據(jù)庫中插入和讀取數(shù)據(jù)實現(xiàn)信息共享剪廉。 首先要繼承SQLiteOpenHelper并在onCreate方法中創(chuàng)建數(shù)據(jù)庫:

sqLiteDatabase.execSQL("CREATE TABLE Teacher(teacherId INTEGER PRIMARY KEY" +
           " AUTOINCREMENT,userId VARCHAR(20) UNIQUE,name VARCHAR(20),password VARCHAR(20))");

向數(shù)據(jù)庫中插入:

String type = "Teacher";
sqLiteDatabase.execSQL("INSERT INTO " + type + "(userId,name,password) values (?,?,?)",
                    new String[]{userId, userName, userPassword});

從數(shù)據(jù)庫中讀取信息:

String type = "Teacher";
Cursor cursor = sqLiteDatabase.rawQuery("SELECT * FROM " + type + " WHERE userId = ?",
         new String[]{userId.getText().toString()});
if(cursor.moveToFirst()){
         String userId = cursor.getString(cursor.getColumnIndex("userId"));
         String name = cursor.getString(cursor.getColumnIndex("name"));
}
cursor.close();

File

文件方式要記得申請權(quán)限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

寫入數(shù)據(jù):

try {
    File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/data/temp.txt");
    FileOutputStream out = new FileOutputStream(file);
    out.write("message".getBytes(Charset.forName("UTF-8")));
    out.flush();
    out.close();
} catch (IOException e) {
    e.printStackTrace();
}

讀取數(shù)據(jù):

try {
    FileInputStream in = new FileInputStream(Environment.getExternalStorageDirectory().getAbsolutePath() + "/temp.txt");
    byte[] reader = new byte[256];
    int read = in.read(reader);
    String content = "";
    if (read > 0)
        content = new String(reader, 0, read, Charset.forName("UTF-8"));
    Toast.makeText(this, content, Toast.LENGTH_SHORT).show();
} catch (IOException e) {
    e.printStackTrace();
}
Broadcast方式

使用組件也就是說利用Broadcast進行消息傳遞。 優(yōu)選LocalBroadcast:

LocalBroadcastManager.getInstance(context).registerReceiver(@NonNull BroadcastReceiver receiver, @NonNull IntentFilter filter) LocalBroadcastManager.getInstance(context).sendBroadcast(intent); 如果說使用Android 進行消息傳遞的話炕檩,Broadcast是最好的了妈经,顧名思義的我們清楚,廣播就是有一個發(fā)送消息和接受消息的過程,所以可以用于消息傳遞吹泡。 示例:

注冊廣播和接受消息
        // 定義廣播
        final Button broadButton = findViewById(R.id.broadcast);
        final BroadcastReceiver receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                broadButton.setText("" + intent.getStringExtra("data"));
            }
        };
        broadButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                registerReceiver(receiver,new IntentFilter("broadsend.action"));
                startActivity(new Intent(MainActivity.this,ReceiveActivity.class));
            }
        });
發(fā)送廣播:
    Intent intent = new Intent("broadsend.action");
    intent.putExtra("data","send");
    sendBroadcast(intent);

這種方式有一些限制骤星,因為接收事件要比發(fā)送事件先定義好,所以只能在當前Activity中注冊廣播爆哑,在跳轉(zhuǎn)的Activity發(fā)送洞难,所以嚴格說這不能算是消息傳遞,因為是單向傳遞的揭朝。

Service方式

Service可以結(jié)合Broadcast進行消息傳遞队贱,不過這樣子就不能算是Service了。 使用Service進行消息傳遞潭袱,我們可以定義接口柱嫌,并利用接口進行消息傳遞。 定義消息接收的接口:

    public static MessageCallback messageCallback = new MessageCallback() {
        @Override
        public void onMessage(String message) {
            Log.d("tag","" + message);
        }
    };
    public interface MessageCallback{
        public void onMessage(String message);
    }

進行消息發(fā)送:

MainActivity.messageCallback.onMessage("message"); 有的人講這種方式不久和共享變量一樣了嗎屯换,不不不编丘,這是完全不一樣的,如果是共享變量的話彤悔,當變量被改變了是不是還得程序員或者用戶去響應(yīng)這種改變呢嘉抓,這就很不好了,而采用這種靜態(tài)接口的方法晕窑,只要函數(shù)被調(diào)用抑片,就立刻可以進行響應(yīng)并處理,不是很方便嗎杨赤。當然也可以想辦法將接口的對象進行傳遞敞斋,例如利用Broadcast來進行傳遞。

EventBus

EventBus 是一款針對Android的發(fā)布以及訂閱事件總線疾牲,使用它可以很方便的進行信息傳遞植捎,而且使用起來很方便。 首先是定義一個消息:

public class Event {
    private String message;
    public Event(){
        message = "EventBus message";
    }

    public void setMessage(String message){
        this.message = message;
    }
     
    public String getMessage(){
        return message;
    }
}
發(fā)送消息:

這里使用了postSticky说敏,這是發(fā)送的粘性廣播鸥跟,使用這個發(fā)送就可以先發(fā)送信息再進行注冊,后注冊的也能接收到前面發(fā)送的廣播盔沫。當然還有其他的使用方式医咨。

EventBus.getDefault().postSticky(new Event());
startActivity(new Intent(MainActivity.this,ReceiveActivity.class));
注冊事件的訂閱者:

EventBus.getDefault().register(this); 接受粘性廣播:

@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventThread(Event event) {
    textView.setText(event.getMessage());
}
LiveDataBus

LiveDataBus是通過包裝LiveData實現(xiàn)的消息總線組件,如果同學(xué)對LiveData不熟悉的同學(xué)架诞,可以找到相對應(yīng)的章節(jié)先學(xué)習一下拟淮。

LiveDataBus 的封裝
  • 通過 map 維護一個消息事件和 MutableLiveData 的映射關(guān)系,MutableLiveData 的類型默認為 Object谴忧,接收任意類型很泊,實現(xiàn)總線通信將 LiveDataBus 封裝為一個單例類角虫。
  • 消息注冊時,如果當前 map 中不存在委造,則先將消息和對應(yīng)的 MutableLiveData 對象放入維護的 map 中戳鹅,添加映射關(guān)系,返回當前 map 中緩存的 MutableLiveData 對象
LiveDataBus 的組成
  • 消息 消息可以是任何的Object昏兆,可以定義不同類型的消息枫虏,如 Boolean、String爬虱。也可以定義自定義類型的消息隶债。
  • 消息總線 消息總線通過單例實現(xiàn),不同的消息通道存放在一個 HashMap中跑筝。
  • 訂閱 訂閱者通過 with 方式獲取消息通道死讹,然后調(diào)用 observe 訂閱這個通道的消息。
  • 發(fā)布 發(fā)布者通過 with 獲取消息通道曲梗,然后調(diào)用 setValue 或者 postValue 發(fā)布消息赞警。
LiveDataBus 的實現(xiàn)
public final class LiveDataBus {

    private final Map<String, MutableLiveData<Object>> bus;

    private LiveDataBus() {
        bus = new HashMap<>();
    }

    private static class SingletonHolder {
        private static final LiveDataBus DATA_BUS = new LiveDataBus();
    }

    public static LiveDataBus get() {
        return SingletonHolder.DATA_BUS;
    }

    public <T> MutableLiveData<T> with(String target, Class<T> type) {
        if (!bus.containsKey(target)) {
            bus.put(target, new MutableLiveData<>());
        }
        return (MutableLiveData<T>) bus.get(target);
    }

    public MutableLiveData<Object> with(String target) {
        return with(target, Object.class);
    }
}
注冊訂閱
LiveDataBus.get().with("key_test", Boolean.class)
    .observe(this, new Observer<Boolean>() {
        @Override
        public void onChanged(@Nullable Boolean aBoolean) {
        }
 });
發(fā)送消息
LiveDataBus.get().with("key_test").setValue(true);

今天的面試分享到此結(jié)束拉~下期在見

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市稀并,隨后出現(xiàn)的幾起案子仅颇,更是在濱河造成了極大的恐慌单默,老刑警劉巖碘举,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異搁廓,居然都是意外死亡引颈,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門境蜕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蝙场,“玉大人,你說我怎么就攤上這事粱年∈勐耍” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵台诗,是天一觀的道長完箩。 經(jīng)常有香客問我,道長拉队,這世上最難降的妖魔是什么弊知? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮粱快,結(jié)果婚禮上秩彤,老公的妹妹穿的比我還像新娘叔扼。我一直安慰自己,他們只是感情好漫雷,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布瓜富。 她就那樣靜靜地躺著,像睡著了一般降盹。 火紅的嫁衣襯著肌膚如雪食呻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天澎现,我揣著相機與錄音仅胞,去河邊找鬼。 笑死剑辫,一個胖子當著我的面吹牛干旧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播妹蔽,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼椎眯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了胳岂?” 一聲冷哼從身側(cè)響起编整,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎乳丰,沒想到半個月后掌测,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡产园,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年汞斧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片什燕。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡粘勒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出屎即,到底是詐尸還是另有隱情庙睡,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布技俐,位于F島的核電站乘陪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏虽另。R本人自食惡果不足惜暂刘,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望捂刺。 院中可真熱鬧谣拣,春花似錦募寨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至贵涵,卻和暖如春列肢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留擅腰,地道東北人思劳。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓寒砖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容