在掘金上看到這篇文章:android 關于先登錄成功后再進入目標界面的思考,作者對實現登錄成功后再跳轉到目標界面功能作了比較詳細的分析,對比了一些已有的實現方案并指出存在的問題。最終,作者實現了一個可同時添加多個條件判斷攔截的方案,思路很新穎。
這篇文章的閱讀量和喜歡數都很多,看來大家對這個需求的關注度很高,這里將我們在使用 CC框架 過程中實現這個功能的方案跟大家分享一下。
快速了解CC
- 是一套基于組件總線的組件化實施方案
-
一靜一動,開發時運行2個app,業務環境始終是完整的:
- 靜:主App (通過跨App的方式調用單組件App內的組件)
- 動:正在開發中的單組件App (通過跨App的方式調用主App內的組件)
-
支持漸進式組件化改造
- 解耦只是過程,而不是前提
CC框架基因中自帶支持組件層面的AOP
在定義組件時,實現IComponent.onCall(cc)方法,并根據cc中的參數來執行組件中的具體邏輯(如:頁面跳轉等)。
可以在調用具體邏輯之前對該功能進行AOP實現,例如:登錄、頁面數據預加載等
用CC框架實現必須先登錄再進入目標頁面功能
目標頁面所在的組件在執行頁面跳轉前調用登錄組件(用戶中心組件)獲取用戶信息,若未登錄則登錄后返回用戶信息。
這里有2個點:
1. 若用戶已登錄,則直接返回用戶信息,同步方式實現即可
2. 若用戶未登錄,則跳轉到登錄頁面,需要執行完登錄操作(或取消)后才能獲得結果,使用異步方式實現。
以打開訂單列表頁面前需要登錄為例:
- 先定義用戶組件,提供一個強制獲取用戶登錄信息的功能,若未登錄則打開登錄界面,并在用戶登錄后返回登錄結果(取消登錄也是一種結果)
//用戶中心組件類
public class UserComponent implements IComponent {@Overridepublic String getName() {return "demo.component.user";}@Overridepublic boolean onCall(CC cc) {String actionName = cc.getActionName();// ... // 強制獲取用戶信息,若未登錄則跳轉到登錄,并將登錄結果返回if ("forceGetLoginUser".equals(actionName)) {if (!TextUtils.isEmpty(Global.loginUserName)) {//已登錄同步實現,直接調用CC.sendCCResult(...)并返回返回falseCCResult 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為applicationintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);}//將cc的callId傳給Activity,登錄完成后通過這個callId來回傳結果intent.putExtra("callId", cc.getCallId());context.startActivity(intent);//異步實現,不立即調用CC.sendCCResult,返回truereturn true;}//...return false;}}
- 模擬的登錄頁面:點擊按鈕模擬登錄,直接返回文本框中的信息作為登錄成功的信息,若未登錄直接返回,則視為取消登錄
/*** 模擬登錄頁面*/
public class LoginActivity extends AppCompatActivity implements View.OnClickListener {private EditText editText;private String callId;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.id.activity_login);callId = intent.getStringExtra("callId");//init views}@Overridepublic 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();}}@Overrideprotected 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);}}
}
- 在訂單組件中進行登錄驗證:登錄成功,則跳轉到訂單列表頁;登錄失敗,則返回調用失敗的結果
//訂單組件
public class OrderComponent implements IComponent {@Overridepublic String getName() {return "demo.component.order";}@Overridepublic 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(...) 并且返回falseCC.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 appsintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);}context.startActivity(intent);}
}
至此,打開訂單頁面必須登錄的功能已全部完成。
這樣實現的好處
- 登錄組件只管登錄自身的邏輯,跟其它邏輯完全不耦合
- 調用訂單組件的地方無需添加額外的代碼
- 可以添加任意的前置條件判斷
- 登錄條件的判斷可用于任意組件而無需修改登錄組件的邏輯
- 支持跨app組件調用時的前置條件判斷
了解更多關于CC框架的信息
Github源碼 ,持續維護更新中, 歡迎watch、fork、star、pr、提issue
系列文章
CC:基于組件總線的Android組件化開源框架
CC框架實踐(1):實現登錄成功再進入目標界面功能
CC框架實踐(2):Fragment和View的組件化
CC框架實踐(3): 讓jsBridge更優雅