CC框架实践(1):实现登陆成功再进入目标界面功能

在掘金上看到这篇文章:android 关于先登陆成功后再进入目标界面的思考,做者对实现登陆成功后再跳转到目标界面功能做了比较详细的分析,对比了一些已有的实现方案并指出存在的问题。最终,做者实现了一个可同时添加多个条件判断拦截的方案,思路很新颖。java

这篇文章的阅读量和喜欢数都不少,看来你们对这个需求的关注度很高,这里将咱们在使用 CC框架 过程当中实现这个功能的方案跟你们分享一下。android

快速了解CC

  • 是一套基于组件总线的组件化实施方案
  • 一静一动,开发时运行2个app,业务环境始终是完整的:
    • 静:主App (经过跨App的方式调用单组件App内的组件)
    • 动:正在开发中的单组件App (经过跨App的方式调用主App内的组件)
  • 支持渐进式组件化改造
    • 解耦只是过程,而不是前提

CC框架基因中自带支持组件层面的AOP

在定义组件时,实现IComponent.onCall(cc)方法,并根据cc中的参数来执行组件中的具体逻辑(如:页面跳转等)。git

能够在调用具体逻辑以前对该功能进行AOP实现,例如:登陆、页面数据预加载等github

用CC框架实现必须先登陆再进入目标页面功能

目标页面所在的组件在执行页面跳转前调用登陆组件(用户中心组件)获取用户信息,若未登陆则登陆后返回用户信息。app

这里有2个点:
1. 若用户已登陆,则直接返回用户信息,同步方式实现便可
2. 若用户未登陆,则跳转到登陆页面,须要执行完登陆操做(或取消)后才能得到结果,使用异步方式实现。
复制代码

以打开订单列表页面前须要登陆为例:框架

  1. 先定义用户组件,提供一个强制获取用户登陆信息的功能,若未登陆则打开登陆界面,并在用户登陆后返回登陆结果(取消登陆也是一种结果)
//用户中心组件类
public class UserComponent implements IComponent {
    @Override
    public String getName() {
        return "demo.component.user";
    }

    @Override
    public boolean onCall(CC cc) {
        String actionName = cc.getActionName();
        // ... 
        // 强制获取用户信息,若未登陆则跳转到登陆,并将登陆结果返回
        if ("forceGetLoginUser".equals(actionName)) {
            if (!TextUtils.isEmpty(Global.loginUserName)) {
                //已登陆同步实现,直接调用CC.sendCCResult(...)并返回返回false
                CCResult result = CCResult.success(Global.KEY_USERNAME, Global.loginUserName);
                CC.sendCCResult(cc.getCallId(), result);
                return false;
            }
            //未登陆,打开登陆界面,在登陆完成后再回调结果,异步实现
            Context context = cc.getContext();
            Intent intent = new Intent(context, LoginActivity.class);
            if (!(context instanceof Activity)) {
                //调用方没有设置context或app间组件跳转,context为application
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }
            //将cc的callId传给Activity,登陆完成后经过这个callId来回传结果
            intent.putExtra("callId", cc.getCallId());
            context.startActivity(intent);
            //异步实现,不当即调用CC.sendCCResult,返回true
            return true;
        }
        //...
        return false;
    }

}
复制代码
  1. 模拟的登陆页面:点击按钮模拟登陆,直接返回文本框中的信息做为登陆成功的信息,若未登陆直接返回,则视为取消登陆
/** * 模拟登陆页面 */
public class LoginActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText editText;
    private String callId;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.id.activity_login);
        callId = intent.getStringExtra("callId");
        //init views
    }

    @Override
    public void onClick(View v) {
        //模拟登陆:点击按钮获取文本框内容并做为用户登陆信息返回
        String username = editText.getText().toString().trim();
        if (TextUtils.isEmpty(username)) {
            Toast.makeText(this, R.string.demo_b_username_hint, Toast.LENGTH_SHORT).show();
        } else {
            //登陆成功,返回
            Global.loginUserName = username;
            finish();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //判断是否为CC调用打开本页面
        if (callId != null) {
            CCResult result;
            if (TextUtils.isEmpty(Global.loginUserName)) {
                result = CCResult.error("login canceled");
            } else {
                result = CCResult.success(Global.KEY_USERNAME, Global.loginUserName);
            }
            //为确保无论登陆成功与否都会调用CC.sendCCResult,在onDestroy方法中调用
            CC.sendCCResult(callId, result);
        }
    }
}
复制代码
  1. 在订单组件中进行登陆验证:登陆成功,则跳转到订单列表页;登陆失败,则返回调用失败的结果
//订单组件
public class OrderComponent implements IComponent {
    @Override
    public String getName() {
        return "demo.component.order";
    }

    @Override
    public boolean onCall(CC cc) {
        CCResult result = CC.obtainBuilder("demo.component.user")
                .setActionName("forceGetLoginUser")
                .build()
                .call();
        CCResult ccResult;
        // 根据登陆状态决定是否打开页面
        // 这里也能够添加更多的前置判断逻辑
        if (result.isSuccess()) {
            ccResult = CCResult.success();
            //登陆成功,打开目标页面
            startOrderListActivity(cc);
        } else {
            //登陆失败,返回失败信息
            ccResult = result;
        }
        //调用方不须要得到额外的信息,只须要知道调用状态
        //因此这个组件采用同步实现:同步调用CC.sendCCResult(...) 而且返回false
        CC.sendCCResult(cc.getCallId(), ccResult);
        return false;
    }

    private void startOrderListActivity(CC cc) {
        Context context = cc.getContext();
        Intent intent = new Intent(context, OrderListActivity.class);
        if (!(context instanceof Activity)) {
            // context maybe an application object if caller dose not setContext
            // or call across apps
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }
        context.startActivity(intent);
    }
}
复制代码

至此,打开订单页面必须登陆的功能已所有完成。异步

这样实现的好处

  1. 登陆组件只管登陆自身的逻辑,跟其它逻辑彻底不耦合
  2. 调用订单组件的地方无需添加额外的代码
  3. 能够添加任意的前置条件判断
  4. 登陆条件的判断可用于任意组件而无需修改登陆组件的逻辑
  5. 支持跨app组件调用时的前置条件判断

了解更多关于CC框架的信息

Github源码 ,持续维护更新中, 欢迎watch、fork、star、pr、提issueide

系列文章

CC:可关联生命周期的android组件化开发框架组件化

CC框架实践(1):实现登陆成功再进入目标界面功能post

CC框架实践(2):Fragment和View的组件化

CC框架实践(3): 让jsBridge更优雅

相关文章
相关标签/搜索