Android code optimization: "The demand is very simple, I do not care how to achieve"

Android code optimization: "The demand is very simple, I do not care how to achieve"

background

before:

In our APP to start the process, we may often have such a demand: the APP for the first time into the bomb when a dialog-based advertising network based on the result of the request, ok ~ very simple, the code is roughly like this (here are pseudocode, for ease of understanding):

  @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, done on-line

step1

After a while, the students ran up the product, he said:

"The boss needs, we want to play the box when coming home for the first time plus a registration agreement."

Programmers small A: "But home has a bomb box before, and directly show with me?"

Product classmates: "pop up with a bad experience, you put A bomb box after it!"

ok, then the programmer A small pondered a moment, it should be changed so that:

 @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() {
                //请求失败直接跳过,请求注册协议
                checkRegisterAgreement();
            }
        });
    }

    private void showADialog() {
        new AlertDialog.Builder(this)
                .setTitle("这是一条有态度的广告")
                .setPositiveButton("我看完了", null)
                .setOnDismissListener(new DialogInterface.OnDismissListener() {
                    @Override
                    public void onDismiss(DialogInterface dialogInterface) {
                        //弹框结束后请求注册协议
                        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
            }
        });
    }

So down, we first request interface APi1, if successful bomb shells advertising box A, box bomb ended the method call registration agreement, if APi1 request fails, skip advertising pop box A, then request registration agreement, when the registration request protocol successful pop our registration agreement bomb box, so down, we really can guarantee registration agreement bomb box after box advertising pop -

step2

A few days later, the product classmates again: "This time we came home for the first time plus a H5 page jump, this time on the increase in the registration agreement between advertising and it ..."

Ever since, the small A continues to change the code changed, the entire code became this:

 @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() {
//                //请求失败直接跳过,请求注册协议
//                checkRegisterAgreement();
                checkNeedShowH5();
            }
        });
    }

    private void showADialog() {
        new AlertDialog.Builder(this)
                .setTitle("这是一条有态度的广告")
                .setPositiveButton("我看完了", null)
                .setOnDismissListener(new DialogInterface.OnDismissListener() {
                    @Override
                    public void onDismiss(DialogInterface dialogInterface) {
                        //    //弹框结束后请求注册协议
//                        checkRegisterAgreement();
                        //现在产品要插入一个H5页面优先请求
                        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("这是注册协议")
                .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;
        }
    }

First, the original agreement step1 no longer registered after the call, but the call request method of H5.

H5 is due to a jump Activity, so we call our registration agreement before continuing in onActivityResult in.

Roughly demo to see the effect:

Android code optimization: "The demand is very simple, I do not care how to achieve"

Still later ... after several iterations, the home page for the first time the bomb box and jumping have been 7-8 up, when every time there is a similar product demand, we have to repeat the process, and every time it probably sort out most of the day .

Do you have to find out what the problem?

1. Home strong coupling between the order, once every other bomb box to insert a page or two before, every time we have to modify it before and after the call chain, at least to modify the three, it is easy to miss but in fact, in addition to the order between each other, no other association.

2. Each time a new demand must complete the carding original logic, more waste of time and affect efficiency.

How to do?

Thinking:

1. Can things in a unified chain management to deal with, is not associated with each other between each thing, as long as the simple configuration can easily replace the order between them.

2.后来维护者,很清晰的就能知道调用的次序,无需每次重新梳理整个业务代码.

设计:

1.我们是否可以把每件要做的事情抽象成一个节点,每个节点只关心自己的任务是否完成,它并不知道它是第几个,也不知道它前面或者后面的是谁.

2.每个节点统一由一个流来管理,它作为全局统筹者,可以控制从任意节点开始、控制整个流的开启与结束等,每个节点的顺序由流来管理.

实现:

带着以上设计思路,我对代码做了一下重构,代码变成了这样:

public class AfterActivity extends AppCompatActivity {

    private static final int REQUEST_CODE_H5 = 1;

    /**
     * 初次广告弹框
     */
    private static final int NODE_FIRST_AD = 10;

    /**
     * 初次进入h5页
     */
    private static final int NODE_CHECK_H5 = 20;

    /**
     * 初次进入的注册协议
     */
    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("这是一条有态度的广告")
                                .setPositiveButton("我看完了", null)
                                .setOnDismissListener(new DialogInterface.OnDismissListener() {
                                    @Override
                                    public void onDismiss(DialogInterface dialogInterface) {
                                        //仅仅只需关心自己是否完成,下一个节点会自动执行
                                        current.onCompleted();
                                    }
                                }).create().show();
                    }

                    @Override
                    public void onFailure() {
                        //仅仅只需关心自己是否完成,下一个节点会自动执行
                        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("这是注册协议")
                                .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;
        }
    }
}

经过上述重构,现在的首页流程:

1. 进入首页要做的几件事之间相互无关联,它们的位置可以任意切换,只需改变id大小便可轻松调整它们的执行顺序.

2. 想要加入或者插入节点无需改动原有逻辑.

项目源码传送门

Android code optimization: "The demand is very simple, I do not care how to achieve"

实现思路

设计每个工作节点

1.我希望每个任务间彼此独立,只关心自己的事情是否完成,我把它抽象成一个节点,每个节点只有自己的节点id 和 完成的方法:

public interface Node {
    /**
     * 节点id
     *
     * @return 当前节点id
     */
    int getId();
    /**
     * 任务完成时触发
     */
    void onCompleted();
}

至于为什么要提供id,后面会讲到.

我们再来看看它的实现类WorkNode的核心代码:

public class WorkNode implements Node {
    /**
     * 节点id
     */
    private int nodeId;

    /**
     * 节点工作者
     */
    private Worker worker;

    private WorkCallBack callBack;

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

    /**
     * @param worker 调用者传入,即真正执行要做的事情
     */
    public WorkNode(int nodeId, Worker worker) {
        this.nodeId = nodeId;
        this.worker = worker;
    }

    /**
     * 由workFlow来决定调用
     *
     * @param callBack 当调用onCompleted 之后回调给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 {

        /**
         * 当前任务完成
         */
        void onWorkCompleted();

    }
}

构造方法中传入了节点id,和Worker, 这个Worker的doWork方法的实现就是我们这个节点真正要做的事情:

public interface Worker {
    /**
     * 执行任务
     *
     * @param current 当前节点
     */
    void doWork(Node current);

}

至此我们回看下demo中对WorkNode的构建:

 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("这是一条有态度的广告")
                                .setPositiveButton("我看完了", null)
                                .setOnDismissListener(new DialogInterface.OnDismissListener() {
                                    @Override
                                    public void onDismiss(DialogInterface dialogInterface) {
                                        //仅仅只需关心自己是否完成,下一个节点会自动执行
                                        current.onCompleted();
                                    }
                                }).create().show();
                    }

                    @Override
                    public void onFailure() {
                        //仅仅只需关心自己是否完成,下一个节点会自动执行
                        current.onCompleted();
                    }
                });
            }
        });
    }

是不是很清晰?

节点只需要关心自己是否做完了,调用完onCompleted之后,一切与我无关了,后续做什么就交给WorkFlow去处理了.

那节点的doWork方法是什么时候被调用的呢? 它内部是怎么安排工作的呢?,我们来设计WorkFlow.

设计工作流:

首先作为所有节点的管理者,当然要把它们存下来,用什么数据结构来存呢?回顾一下我的需求: 可以灵活控制节点的执行顺序, so…经过反复筛选,我最终选择了SparseArray来存放我们所有的节点,因为我们为每个节点提供id作为key:

一来可以提高代码可读性。

二来,SparseArray内部是数组实现的,而且是按照key的大小升序排列的,基于这个特性,我们只需要改变定义Key值的大小关系就可以改变它们在数组中的顺序。

我们再来看看用SparseArray来实现的WorkFlow:

public class WorkFlow {

    private SparseArray<WorkNode> flowNodes;

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

    /**
     * 开始工作,默认从第一个节点
     */
    public void start() {
        startWithNode(flowNodes.keyAt(0));
    }

    /**
     * 基于某个节点Id 开始工作
     *
     * @param startNodeId 节点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中调用的start()的方法,其实内部调用的是 startWithNode 方法,这个方法接收一个参数 startNodeId ,也就是我们构建节点的id,我们在 SparseArray 中找到节点,直接开始执行。

当我们在节点内部调用完 onCompleted 方法之后, 会直接回调 onWorkCompleted 方法,此时我们再看看是否有下一个节点,当有下一个节点之后,一直递归到下一个没有节点为止。

至此,我们原理基本分析完成,通过内部 SparseArray的设计,我们可以灵活从任意节点开始执行,外部的WorkFlow就是一个全局的管理者,我们可以拓展很多其他功能,比如 continueWork() :当我们在节点以外希望让流程继续执行下去的时候(参考Demo的 onActivityResult), revert():回退到上一节点 等等。

总结

  • WorkFlow,很好的接耦了各个次序任务直接的相互依赖,提高了开发效率。
  • 它也很好的提高了代码的可维护性和可读性,非常便于后来新次序任务的拓展。

    • *

最后对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司19年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

Android code optimization: "The demand is very simple, I do not care how to achieve"

上述【高清技术脑图】以及【配套的架构技术PDF】可以 加我wx:X1524478394 免费获取

When programmers easily, when a good programmer is a need to learn from junior programmer to senior programmer, architect from primary to senior architect, or to management, technical director from technical manager to each stage We need to have different capabilities. Early to determine their career direction, in order to throw off their peers at work and in capacity building.

Guess you like

Origin blog.51cto.com/14332859/2459389