CC框架實踐(1):實現登入成功再進入目標介面功能

齊翊發表於2017-12-19

在掘金上看到這篇文章:android 關於先登入成功後再進入目標介面的思考,作者對實現登入成功後再跳轉到目標介面功能作了比較詳細的分析,對比了一些已有的實現方案並指出存在的問題。最終,作者實現了一個可同時新增多個條件判斷攔截的方案,思路很新穎。

這篇文章的閱讀量和喜歡數都很多,看來大家對這個需求的關注度很高,這裡將我們在使用 CC框架 過程中實現這個功能的方案跟大家分享一下。

快速瞭解CC

  • 是一套基於元件匯流排的元件化實施方案
  • 一靜一動,開發時執行2個app,業務環境始終是完整的:
    • 靜:主App (通過跨App的方式呼叫單元件App內的元件)
    • 動:正在開發中的單元件App (通過跨App的方式呼叫主App內的元件)
  • 支援漸進式元件化改造
    • 解耦只是過程,而不是前提

CC框架基因中自帶支援元件層面的AOP

在定義元件時,實現IComponent.onCall(cc)方法,並根據cc中的引數來執行元件中的具體邏輯(如:頁面跳轉等)。

可以在呼叫具體邏輯之前對該功能進行AOP實現,例如:登入、頁面資料預載入等

用CC框架實現必須先登入再進入目標頁面功能

目標頁面所在的元件在執行頁面跳轉前呼叫登入元件(使用者中心元件)獲取使用者資訊,若未登入則登入後返回使用者資訊。

這裡有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、提issue

系列文章

CC:可關聯生命週期的android元件化開發框架

CC框架實踐(1):實現登入成功再進入目標介面功能

CC框架實踐(2):Fragment和View的元件化

CC框架實踐(3): 讓jsBridge更優雅

相關文章