久久综合给合久久狠狠狠974色|亚洲成熟丰满熟妇高潮xxxxx|国产又黄又黄又大又粗又爽的视频|日韩久久久精品无码一区二区三区|中文字幕无码乱人伦一区二区三区|国产成人无码区免费内射一片色欲|亚洲av无码久久精品一区二区三区

                      Android 代碼優(yōu)化:“這個需求很簡單,怎么實(shí)現(xiàn)我不管”

                      2020-04-04 14:01:20  閱讀:-  來源:


                      Android 代碼優(yōu)化:“這個需求很簡單,怎么實(shí)現(xiàn)我不管”

                      背景

                      before:

                      在我們APP啟動過程,我們可能常常有這樣的需求:在APP第一次進(jìn)入的時候根據(jù)網(wǎng)絡(luò)請求的結(jié)果彈一個對話框式的廣告, ok~ 很簡單,那么代碼大致就是這樣(這邊都是偽代碼,為了方便理解):

                        @Override
                      protected void onCreate(@Nullable Bundle savedInstanceState) {
                      checkFirstDialogIfNeed();
                      }
                      /**
                      * step 1
                      */
                      private void checkFirstDialogIfNeed(){
                      Utils.fakeRequest("http://www.api1.com", new HttpCallBack() {
                      @Override
                      public void onOk() {
                      showADialog();
                      }

                      @Override
                      public void onFailure() {
                      //do nothing
                      }
                      });
                      }

                      ok,做完上線

                      step1

                      過了一陣子,產(chǎn)品同學(xué)又跑過來了說:

                      “老板的需求,我們要在首頁第一次進(jìn)來的時候加一個注冊協(xié)議的彈框”

                      程序員小A: “可是首頁已經(jīng)之前有一個彈框了,直接一起展示嗎?”

                      產(chǎn)品同學(xué): “一起彈出來體驗(yàn)不好,那你放在A彈框之后吧!”

                      ok,那么程序員小A尋思了一下,那應(yīng)該就是這么改了:

                       @Override
                      protected void onCreate(@Nullable Bundle savedInstanceState) {
                      checkFirstDialogIfNeed();
                      }

                      private void checkFirstDialogIfNeed(){
                      Utils.fakeRequest("http://www.api1.com", new HttpCallBack() {
                      @Override
                      public void onOk() {
                      showADialog();
                      }

                      @Override
                      public void onFailure() {
                      //請求失敗直接跳過,請求注冊協(xié)議
                      checkRegisterAgreement();
                      }
                      });
                      }

                      private void showADialog() {
                      new AlertDialog.Builder(this)
                      .setTitle("這是一條有態(tài)度的廣告")
                      .setPositiveButton("我看完了", null)
                      .setOnDismissListener(new DialogInterface.OnDismissListener() {
                      @Override
                      public void onDismiss(DialogInterface dialogInterface) {
                      //彈框結(jié)束后請求注冊協(xié)議
                      checkRegisterAgreement();
                      }
                      }).create().show();
                      }
                      private void checkRegisterAgreement() {
                      Utils.fakeRequest("http://www.api2.com", new HttpCallBack() {
                      @Override
                      public void onOk() {
                      showBDialog();
                      }

                      @Override
                      public void onFailure() {
                      //do nothing
                      }
                      });
                      }

                      這么下來,我們先請求接口APi1,如果成功了彈廣告彈框A,彈框結(jié)束了調(diào)用注冊協(xié)議的方法,如果APi1 請求失敗,直接跳過廣告彈框A,繼而請求注冊協(xié)議,當(dāng)注冊協(xié)議請求成功了彈出我們的注冊協(xié)議彈框,這么下來,我們確實(shí)可以保證注冊協(xié)議彈框在廣告彈框之后~

                      step2

                      過了幾天,產(chǎn)品同學(xué)又來了:“這次我們首頁初次進(jìn)來再加一個H5頁面跳轉(zhuǎn),這次就加在 廣告和注冊協(xié)議之間吧…”

                      于是乎,小A繼續(xù)改了改代碼,整個代碼就成了這樣:

                       @Override
                      protected void onCreate(@Nullable Bundle savedInstanceState) {
                      checkFirstDialogIfNeed();
                      }

                      private void checkFirstDialogIfNeed() {
                      Utils.fakeRequest("http://www.api1.com", new HttpCallBack() {
                      @Override
                      public void onOk() {
                      showADialog();
                      }

                      @Override
                      public void onFailure() {
                      // //請求失敗直接跳過,請求注冊協(xié)議
                      // checkRegisterAgreement();
                      checkNeedShowH5();
                      }
                      });
                      }

                      private void showADialog() {
                      new AlertDialog.Builder(this)
                      .setTitle("這是一條有態(tài)度的廣告")
                      .setPositiveButton("我看完了", null)
                      .setOnDismissListener(new DialogInterface.OnDismissListener() {
                      @Override
                      public void onDismiss(DialogInterface dialogInterface) {
                      // //彈框結(jié)束后請求注冊協(xié)議
                      // checkRegisterAgreement();
                      //現(xiàn)在產(chǎn)品要插入一個H5頁面優(yōu)先請求
                      checkNeedShowH5();

                      }
                      }).create().show();
                      }

                      private void checkRegisterAgreement() {
                      Utils.fakeRequest("http://www.api2.com", new HttpCallBack() {
                      @Override
                      public void onOk() {
                      showBDialog();
                      }

                      @Override
                      public void onFailure() {
                      //do nothing
                      }
                      });
                      }

                      private void showBDialog() {
                      new AlertDialog.Builder(this)
                      .setTitle("這是注冊協(xié)議")
                      .setPositiveButton("我看完了", null)
                      .setOnDismissListener(new DialogInterface.OnDismissListener() {
                      @Override
                      public void onDismiss(DialogInterface dialogInterface) {
                      //do nothing
                      }
                      }).create().show();
                      }

                      private void checkNeedShowH5() {
                      Utils.fakeRequest("http://www.api3.com", new HttpCallBack() {
                      @Override
                      public void onOk() {
                      toH5Page();
                      }

                      @Override
                      public void onFailure() {
                      checkRegisterAgreement();
                      }
                      });
                      }

                      private void toH5Page() {
                      startActivityForResult(new Intent(this, TestH5Activity.class), REQUEST_CODE_H5);
                      }

                      @Override
                      protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
                      super.onActivityResult(requestCode, resultCode, data);
                      switch (requestCode) {
                      case REQUEST_CODE_H5:
                      checkRegisterAgreement();
                      break;
                      default:
                      break;
                      }
                      }

                      首先將原有step1 結(jié)束之后不再調(diào)用注冊協(xié)議,而是調(diào)用請求H5的方法.

                      由于去H5是一個Activity的跳轉(zhuǎn),所以我們在onActivityResult里我們繼續(xù)之前的注冊協(xié)議的調(diào)用.

                      看看大致demo效果:

                      Android 代碼優(yōu)化:“這個需求很簡單,怎么實(shí)現(xiàn)我不管”

                      再后來…幾經(jīng)迭代,首頁首次的彈框和頁面跳轉(zhuǎn)已經(jīng)有7、8個了,當(dāng)每次產(chǎn)品有類似需求的時候,我們又要重復(fù)上面的過程了,每次大概梳理個大半天吧.

                      大家是否有發(fā)現(xiàn)什么問題嗎?

                      1.首頁次序之間強(qiáng)耦合,每次一旦要在二者之前插入一個其他彈框或者頁面,我們每次都要修改在它之前和之后的調(diào)用鏈,至少要修改三處,很容易漏掉,但是實(shí)際上它們彼此之間除了次序之外,沒有其他關(guān)聯(lián).

                      2.每次新需求來的時候都要完整梳理原有邏輯,比較浪費(fèi)時間,影響效率.

                      怎么辦?

                      思考:

                      1.能否在一個鏈中統(tǒng)一管理要處理的事情,每個事情之間互相不關(guān)聯(lián),只要簡單配置就可以輕松替換它們之間的順序.

                      2.后來維護(hù)者,很清晰的就能知道調(diào)用的次序,無需每次重新梳理整個業(yè)務(wù)代碼.

                      設(shè)計:

                      1.我們是否可以把每件要做的事情抽象成一個節(jié)點(diǎn),每個節(jié)點(diǎn)只關(guān)心自己的任務(wù)是否完成,它并不知道它是第幾個,也不知道它前面或者后面的是誰.

                      2.每個節(jié)點(diǎn)統(tǒng)一由一個流來管理,它作為全局統(tǒng)籌者,可以控制從任意節(jié)點(diǎn)開始、控制整個流的開啟與結(jié)束等,每個節(jié)點(diǎn)的順序由流來管理.

                      實(shí)現(xiàn):

                      帶著以上設(shè)計思路,我對代碼做了一下重構(gòu),代碼變成了這樣:

                      public class AfterActivity extends AppCompatActivity {

                      private static final int REQUEST_CODE_H5 = 1;

                      /**
                      * 初次廣告彈框
                      */
                      private static final int NODE_FIRST_AD = 10;

                      /**
                      * 初次進(jìn)入h5頁
                      */
                      private static final int NODE_CHECK_H5 = 20;

                      /**
                      * 初次進(jìn)入的注冊協(xié)議
                      */
                      private static final int NODE_REGISTER_AGREEMENT = 30;

                      private WorkFlow workFlow;

                      @Override
                      protected void onCreate(@Nullable Bundle savedInstanceState) {
                      super.onCreate(savedInstanceState);
                      startWorkFlow();
                      }

                      private void startWorkFlow() {
                      workFlow = new WorkFlow.Builder()
                      .withNode(getFirstAdNode())
                      .withNode(getShowRegisterAgreementNode())
                      .withNode(getShowH5Node())
                      .create();
                      workFlow.start();
                      }

                      private WorkNode getFirstAdNode() {
                      return WorkNode.build(NODE_FIRST_AD, new Worker() {
                      @Override
                      public void doWork(final Node current) {
                      Utils.fakeRequest("http://www.api1.com", new HttpCallBack() {
                      @Override
                      public void onOk() {
                      new AlertDialog.Builder(AfterActivity.this)
                      .setTitle("這是一條有態(tài)度的廣告")
                      .setPositiveButton("我看完了", null)
                      .setOnDismissListener(new DialogInterface.OnDismissListener() {
                      @Override
                      public void onDismiss(DialogInterface dialogInterface) {
                      //僅僅只需關(guān)心自己是否完成,下一個節(jié)點(diǎn)會自動執(zhí)行
                      current.onCompleted();
                      }
                      }).create().show();
                      }

                      @Override
                      public void onFailure() {
                      //僅僅只需關(guān)心自己是否完成,下一個節(jié)點(diǎn)會自動執(zhí)行
                      current.onCompleted();
                      }
                      });
                      }
                      });
                      }

                      private WorkNode getShowRegisterAgreementNode() {
                      return WorkNode.build(NODE_REGISTER_AGREEMENT, new Worker() {
                      @Override
                      public void doWork(final Node current) {
                      Utils.fakeRequest("http://www.api2.com", new HttpCallBack() {
                      @Override
                      public void onOk() {
                      new AlertDialog.Builder(AfterActivity.this)
                      .setTitle("這是注冊協(xié)議")
                      .setPositiveButton("我看完了", null)
                      .setOnDismissListener(new DialogInterface.OnDismissListener() {
                      @Override
                      public void onDismiss(DialogInterface dialogInterface) {
                      current.onCompleted();
                      }
                      }).create().show();
                      }

                      @Override
                      public void onFailure() {
                      current.onCompleted();
                      }
                      });
                      }
                      });
                      }

                      private WorkNode getShowH5Node() {
                      return (WorkNode.build(NODE_CHECK_H5, new Worker() {
                      @Override
                      public void doWork(final Node current) {
                      Utils.fakeRequest("http://www.api3.com", new HttpCallBack() {
                      @Override
                      public void onOk() {
                      startActivityForResult(new Intent(AfterActivity.this, TestH5Activity.class), REQUEST_CODE_H5);
                      }

                      @Override
                      public void onFailure() {
                      current.onCompleted();
                      }
                      });
                      }
                      }));
                      }

                      @Override
                      protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
                      super.onActivityResult(requestCode, resultCode, data);
                      switch (requestCode) {
                      case REQUEST_CODE_H5:
                      workFlow.continueWork();
                      break;
                      default:
                      break;
                      }
                      }
                      }

                      經(jīng)過上述重構(gòu),現(xiàn)在的首頁流程:

                      1. 進(jìn)入首頁要做的幾件事之間相互無關(guān)聯(lián),它們的位置可以任意切換,只需改變id大小便可輕松調(diào)整它們的執(zhí)行順序.

                      2. 想要加入或者插入節(jié)點(diǎn)無需改動原有邏輯.

                      Android 代碼優(yōu)化:“這個需求很簡單,怎么實(shí)現(xiàn)我不管”

                      實(shí)現(xiàn)思路

                      設(shè)計每個工作節(jié)點(diǎn)

                      1.我希望每個任務(wù)間彼此獨(dú)立,只關(guān)心自己的事情是否完成,我把它抽象成一個節(jié)點(diǎn),每個節(jié)點(diǎn)只有自己的節(jié)點(diǎn)id 和 完成的方法:

                      public interface Node {
                      /**
                      * 節(jié)點(diǎn)id
                      *
                      * @return 當(dāng)前節(jié)點(diǎn)id
                      */
                      int getId();
                      /**
                      * 任務(wù)完成時觸發(fā)
                      */
                      void onCompleted();
                      }

                      至于為什么要提供id,后面會講到.

                      我們再來看看它的實(shí)現(xiàn)類WorkNode的核心代碼:

                      public class WorkNode implements Node {
                      /**
                      * 節(jié)點(diǎn)id
                      */
                      private int nodeId;

                      /**
                      * 節(jié)點(diǎn)工作者
                      */
                      private Worker worker;

                      private WorkCallBack callBack;

                      public static WorkNode build(int nodeId, Worker worker) {
                      return new WorkNode(nodeId, worker);
                      }

                      /**
                      * @param worker 調(diào)用者傳入,即真正執(zhí)行要做的事情
                      */
                      public WorkNode(int nodeId, Worker worker) {
                      this.nodeId = nodeId;
                      this.worker = worker;
                      }

                      /**
                      * 由workFlow來決定調(diào)用
                      *
                      * @param callBack 當(dāng)調(diào)用onCompleted 之后回調(diào)給WorkFlow
                      */
                      void doWork(WorkCallBack callBack) {
                      this.callBack = callBack;
                      worker.doWork(this);
                      }

                      @Override
                      public int getId() {
                      return nodeId;
                      }

                      @Override
                      public void onCompleted() {
                      if (null != callBack) {
                      callBack.onWorkCompleted();
                      }
                      }

                      interface WorkCallBack {

                      /**
                      * 當(dāng)前任務(wù)完成
                      */
                      void onWorkCompleted();

                      }
                      }

                      構(gòu)造方法中傳入了節(jié)點(diǎn)id,和Worker, 這個Worker的doWork方法的實(shí)現(xiàn)就是我們這個節(jié)點(diǎn)真正要做的事情:

                      public interface Worker {
                      /**
                      * 執(zhí)行任務(wù)
                      *
                      * @param current 當(dāng)前節(jié)點(diǎn)
                      */
                      void doWork(Node current);

                      }

                      至此我們回看下demo中對WorkNode的構(gòu)建:

                       private WorkNode getFirstAdNode() {
                      return WorkNode.build(NODE_FIRST_AD, new Worker() {
                      @Override
                      public void doWork(final Node current) {
                      Utils.fakeRequest("http://www.api1.com", new HttpCallBack() {
                      @Override
                      public void onOk() {
                      new AlertDialog.Builder(AfterActivity.this)
                      .setTitle("這是一條有態(tài)度的廣告")
                      .setPositiveButton("我看完了", null)
                      .setOnDismissListener(new DialogInterface.OnDismissListener() {
                      @Override
                      public void onDismiss(DialogInterface dialogInterface) {
                      //僅僅只需關(guān)心自己是否完成,下一個節(jié)點(diǎn)會自動執(zhí)行
                      current.onCompleted();
                      }
                      }).create().show();
                      }

                      @Override
                      public void onFailure() {
                      //僅僅只需關(guān)心自己是否完成,下一個節(jié)點(diǎn)會自動執(zhí)行
                      current.onCompleted();
                      }
                      });
                      }
                      });
                      }

                      是不是很清晰?

                      節(jié)點(diǎn)只需要關(guān)心自己是否做完了,調(diào)用完onCompleted之后,一切與我無關(guān)了,后續(xù)做什么就交給WorkFlow去處理了.

                      那節(jié)點(diǎn)的doWork方法是什么時候被調(diào)用的呢? 它內(nèi)部是怎么安排工作的呢?,我們來設(shè)計WorkFlow.

                      設(shè)計工作流:

                      首先作為所有節(jié)點(diǎn)的管理者,當(dāng)然要把它們存下來,用什么數(shù)據(jù)結(jié)構(gòu)來存呢?回顧一下我的需求: 可以靈活控制節(jié)點(diǎn)的執(zhí)行順序, so…經(jīng)過反復(fù)篩選,我最終選擇了SparseArray來存放我們所有的節(jié)點(diǎn),因?yàn)槲覀優(yōu)槊總€節(jié)點(diǎn)提供id作為key:

                      一來可以提高代碼可讀性。

                      二來,SparseArray內(nèi)部是數(shù)組實(shí)現(xiàn)的,而且是按照key的大小升序排列的,基于這個特性,我們只需要改變定義Key值的大小關(guān)系就可以改變它們在數(shù)組中的順序。

                      我們再來看看用SparseArray來實(shí)現(xiàn)的WorkFlow:

                      public class WorkFlow {

                      private SparseArray flowNodes;

                      public WorkFlow(SparseArray flowNodes) {
                      this.flowNodes = flowNodes;
                      }

                      /**
                      * 開始工作,默認(rèn)從第一個節(jié)點(diǎn)
                      */
                      public void start() {
                      startWithNode(flowNodes.keyAt(0));
                      }

                      /**
                      * 基于某個節(jié)點(diǎn)Id 開始工作
                      *
                      * @param startNodeId 節(jié)點(diǎn)id
                      */
                      public void startWithNode(int startNodeId) {
                      final int startIndex = flowNodes.indexOfKey(startNodeId);
                      WorkNode startNode = flowNodes.valueAt(startIndex);
                      startNode.doWork(new WorkNode.WorkCallBack() {
                      @Override
                      public void onWorkCompleted() {
                      findAndExecuteNextNodeIfExist(startIndex);
                      }
                      });
                      }

                      private void findAndExecuteNextNodeIfExist(int startIndex) {
                      final int nextIndex = startIndex + 1;
                      final WorkNode nextNode = flowNodes.valueAt(nextIndex);
                      if (null != nextNode) {
                      nextNode.doWork(new WorkNode.WorkCallBack() {
                      @Override
                      public void onWorkCompleted() {
                      findAndExecuteNextNodeIfExist(nextIndex);
                      }
                      });
                      }
                      }

                      }

                      我們在demo中調(diào)用的start()的方法,其實(shí)內(nèi)部調(diào)用的是 startWithNode 方法,這個方法接收一個參數(shù) startNodeId ,也就是我們構(gòu)建節(jié)點(diǎn)的id,我們在 SparseArray 中找到節(jié)點(diǎn),直接開始執(zhí)行。

                      當(dāng)我們在節(jié)點(diǎn)內(nèi)部調(diào)用完 onCompleted 方法之后, 會直接回調(diào) onWorkCompleted 方法,此時我們再看看是否有下一個節(jié)點(diǎn),當(dāng)有下一個節(jié)點(diǎn)之后,一直遞歸到下一個沒有節(jié)點(diǎn)為止。

                      至此,我們原理基本分析完成,通過內(nèi)部 SparseArray的設(shè)計,我們可以靈活從任意節(jié)點(diǎn)開始執(zhí)行,外部的WorkFlow就是一個全局的管理者,我們可以拓展很多其他功能,比如 continueWork() :當(dāng)我們在節(jié)點(diǎn)以外希望讓流程繼續(xù)執(zhí)行下去的時候(參考Demo的 onActivityResult), revert():回退到上一節(jié)點(diǎn) 等等。

                      總結(jié)

                      • WorkFlow,很好的接耦了各個次序任務(wù)直接的相互依賴,提高了開發(fā)效率。
                      • 它也很好的提高了代碼的可維護(hù)性和可讀性,非常便于后來新次序任務(wù)的拓展。

                      最后對于程序員來說,要學(xué)習(xí)的知識內(nèi)容、技術(shù)有太多太多,要想不被環(huán)境淘汰就只有不斷提升自己,從來都是我們?nèi)ミm應(yīng)環(huán)境,而不是環(huán)境來適應(yīng)我們!

                      這里附上上述的技術(shù)體系圖相關(guān)的幾十套騰訊、頭條、阿里、美團(tuán)等公司19年的面試題,把技術(shù)點(diǎn)整理成了視頻和PDF(實(shí)際上比預(yù)期多花了不少精力),包含知識脈絡(luò) + 諸多細(xì)節(jié),由于篇幅有限,這里以圖片的形式給大家展示一部分。

                      相信它會給大家?guī)砗芏嗍斋@:

                      Android 代碼優(yōu)化:“這個需求很簡單,怎么實(shí)現(xiàn)我不管”

                      上述【高清技術(shù)腦圖】以及【配套的架構(gòu)技術(shù)PDF】可以 私信我【面試】免費(fèi)獲?。?/strong>

                      當(dāng)程序員容易,當(dāng)一個優(yōu)秀的程序員是需要不斷學(xué)習(xí)的,從初級程序員到高級程序員,從初級架構(gòu)師到資深架構(gòu)師,或者走向管理,從技術(shù)經(jīng)理到技術(shù)總監(jiān),每個階段都需要掌握不同的能力。早早確定自己的職業(yè)方向,才能在工作和能力提升中甩開同齡人。

                      诸城市| 庆云县| 广宗县| 开化县| 丰城市| 汽车| 广平县| 双江| 苏尼特左旗| 连江县| 高台县| 广元市| 温州市| 布拖县| 宁远县| 聂拉木县| 永吉县| 龙里县| 永德县| 临沭县| 上犹县| 九龙坡区| 柳河县| 福安市| 青浦区| 靖远县| 利津县| 石渠县| 东光县| 武陟县| 奉化市| 伊金霍洛旗| 绵竹市| 清水河县| 和硕县| 临湘市| 石景山区| 本溪市| 汉沽区| 柏乡县| 龙江县|