在掘金上看到這篇文章: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 {
@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;
}
}
複製程式碼
- 模擬的登入頁面:點選按鈕模擬登入,直接返回文字框中的資訊作為登入成功的資訊,若未登入直接返回,則視為取消登入
/**
* 模擬登入頁面
*/
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);
}
}
}
複製程式碼
- 在訂單元件中進行登入驗證:登入成功,則跳轉到訂單列表頁;登入失敗,則返回撥用失敗的結果
//訂單元件
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);
}
}
複製程式碼
至此,開啟訂單頁面必須登入的功能已全部完成。
這樣實現的好處
- 登入元件只管登入自身的邏輯,跟其它邏輯完全不耦合
- 呼叫訂單元件的地方無需新增額外的程式碼
- 可以新增任意的前置條件判斷
- 登入條件的判斷可用於任意元件而無需修改登入元件的邏輯
- 支援跨app元件呼叫時的前置條件判斷
瞭解更多關於CC框架的資訊
Github原始碼 ,持續維護更新中, 歡迎watch、fork、star、pr、提issue