1.狀態(tài)模式(state)
1.1定義
當一個對象的內在狀態(tài)改變時容許改變其行為。這個對象看起來像是改變了其類
1.2說明
- Context:控制類,定義客戶感興趣的接口犁苏,維護一個State子類的實例
- State:抽象狀態(tài)類或者狀態(tài)接口场绿,定義一個或者以組接口,表示該狀態(tài)下的行為建车。
- ConcreteStateA,ConcreteStateB:具體狀態(tài)類扩借,每一個具體的類實現抽象State中定義的接口。
PS:和策略模式的區(qū)別
狀態(tài)模式和策略模式的結構幾乎一模一樣,但他們的目的, 本質卻完全不一樣. 狀態(tài)模式的行為是平行的不可替換的. 策略模式的行為是彼此獨立, 可相互替換的. 總結一句話表述: 狀態(tài)模式是把對象的行為包裝在不同的狀態(tài)對象里, 每一個狀態(tài)對象都有一個共同的抽象狀態(tài)基類, 狀態(tài)模式的意圖是讓一個對象在其內部狀態(tài)改變的時候, 其行為也隨之改變
1.3使用場景
- 一個代碼的行為取決于它的裝填, 并且必須在運行時根據其狀態(tài)改變它的行為
- 代碼中包含大量與對象狀態(tài)有關的條件語句, 同樣可以去除分支語句的效果
1.4代碼實現
如果登陸成功則可以調用其他的頁面缤至,沒有登陸則需要跳轉到登陸界面的例子
1.狀態(tài)接口
public interface UserState {
/**
* 轉發(fā)
*/
public void forward(Context context);
/**
* 評論
*/
public void comment(Context context);
}
2.具體的實現類
登陸過的
public class LoginState implements UserState {
@Override
public void forward(Context context) {
Toast.makeText(context, "跳轉", Toast.LENGTH_LONG).show();
}
@Override
public void comment(Context context) {
Toast.makeText(context, "評論", Toast.LENGTH_LONG).show();
}
}
未登陸的
public class LogoutState implements UserState {
@Override
public void forward(Context context) {
gotoLoginActivity(context);
}
@Override
public void comment(Context context) {
gotoLoginActivity(context);
}
private void gotoLoginActivity(Context context) {
Intent intent = new Intent(context, LoginActivity.class);
context.startActivity(intent);
}
}
3.控制類
public class LoginContext {
//用戶狀態(tài)
UserState userState = new LogoutState();
private LoginContext(){
}
//單例
private static class SingleTonHolder
{
private static LoginContext sLoginContext = new LoginContext();
}
public static LoginContext getLoginContext() {
return SingleTonHolder.sLoginContext;
}
public void setUserState(UserState userState) {
this.userState = userState;
}
public void forward(Context context) {
userState.forward(context);
}
public void comment(Context context) {
userState.forward(context);
}
}
4.調用方法
1.4優(yōu)缺點
優(yōu)點:狀態(tài)模式將所有與一個狀態(tài)相關的行為放到一個狀態(tài)對象中潮罪,它提供了一個更好的方法來組織與特定狀態(tài)相關的代碼,將繁瑣的判斷換成結構清晰的狀態(tài)類族领斥,在避免代碼膨脹的同時保證了可擴展和可維護性嫉到。
缺點:狀態(tài)模式使用必然增加系統(tǒng)類和對象的個數
1.5 Android源碼對相應實現
WIFI管理就是使用了狀態(tài)模式
在WiFi復雜的調用中, 存在一個State的狀態(tài)類, 它代表了WiFi的某個狀態(tài), 定義如下:
com\android\internal\State.java
public class State implements IState {
// 進入當前狀態(tài)之后調用該函數
@Override
public void enter() {
}
// 退出該狀態(tài)后改用該函數
@Override
public void exit() {
}
// 處理消息
@Override
public boolean processMessage(Message msg) {
return false;
}
}
狀態(tài)之間并不是可以隨意切換的, 他們有一種層級關系, 這些層級關系StateMachine的構造函數中被定義的, 代碼如下:
com\android\server\WifiStaaMachine.java
public WifiStateMachine(Context context, String wlanInterface,
WifiTrafficPoller trafficPoller) {
addState(mDefaultState);
addState(mInitialState, mDefaultState);
addState(mSupplicantStartingState, mDefaultState);
addState(mSupplicantStartedState, mDefaultState);
addState(mDriverStartingState, mSupplicantStartedState);
addState(mDriverStartedState, mSupplicantStartedState);
addState(mScanModeState, mDriverStartedState);
addState(mConnectModeState, mDriverStartedState);
addState(mL2ConnectedState, mConnectModeState);
addState(mObtainingIpState, mL2ConnectedState);
addState(mVerifyingLinkState, mL2ConnectedState);
addState(mConnectedState, mL2ConnectedState);
addState(mRoamingState, mL2ConnectedState);
addState(mDisconnectingState, mConnectModeState);
addState(mDisconnectedState, mConnectModeState);
addState(mWpsRunningState, mConnectModeState);
addState(mWaitForP2pDisableState, mSupplicantStartedState);
addState(mDriverStoppingState, mSupplicantStartedState);
addState(mDriverStoppedState, mSupplicantStartedState);
addState(mSupplicantStoppingState, mDefaultState);
addState(mSoftApStartingState, mDefaultState);
addState(mSoftApStartedState, mDefaultState);
addState(mTetheringState, mSoftApStartedState);
addState(mTetheredState, mSoftApStartedState);
addState(mUntetheringState, mSoftApStartedState);
// 初始化模式為mInitialState
setInitialState(mInitialState);
}
com\android\internal\StateMachine.java
protected final void addState(State state, State parent) {
mSmHandler.addState(state, parent);
}
在構造函數中調用了addState()函數, 這些函數最終會調用mSmHandler#addState()函數. 這個函數就是在狀態(tài)之間建立一個層級關系, 這是一個樹形的層級關系. 狀態(tài)之間并不是跨越式的轉換, 當前狀態(tài)只能轉換到上一個狀態(tài)或者下一個狀態(tài).
State的類有enter,exit,processMessage三個函數, 進入狀態(tài)之后會調用enter(), 退出時調用exit(), 處理具體消息時調用processMessage(). 而狀態(tài)模式的核心就是當一個對象的內在狀態(tài)改變時允許改變其行為, 所以我們關注processMessage()不同的狀態(tài)下就是依賴這個函數實現不同行為的.
例如: 在請求掃描Wifi時, 如果在初始化狀態(tài)(InitialState)下, 說明Wifi驅動還沒有進行加載和啟動, 掃描的請求會被會被忽略. 而在驅動加載狀態(tài)下, 請求會被添加到延遲處理的消息隊列中, 等待驅動加載完畢進行掃描請求.
2.責任鏈模式(Chain of Responsibility)
2.1 定義
使多個對象都有機會處理請求, 從而避免了請求的發(fā)送者和接收者之間的耦合關系. 將這些對象連成一條鏈, 并沿著這條鏈傳遞該請求, 直到對象處理它為止
2.2使用場景
- 多個對象可以處理一個請求,但是具體由那個處理則在運行中動態(tài)決定
- 在請求處理者不明確的情況下向多個對象的一個進行提交請求月洛。
2.3 代碼實現
1.處理抽象類
public abstract class Handler {
//下一個節(jié)點處理者
protected Handler successor;
/**
* 請求處理
* @param condition 請求條件
*/
public abstract void handleRequest(String condition);
}
2.具體的實現
public class ConcreteHandler1 extends Handler {
@Override
public void handleRequest(String condition) {
if(condition.equals("ConcreteHandler1")){
System.out.println("ConcreteHandler1 handler");
}else{
successor.handleRequest(condition);
}
}
}
public class ConcreteHandler2 extends Handler {
@Override
public void handleRequest(String condition) {
if(condition.equals("ConcreteHandler2")){
System.out.println("ConcreteHandler2 handler");
}else{
successor.handleRequest(condition);
}
}
}
3.具體調用
public class Client {
public static void main(String[] args) {
ConcreteHandler1 concreteHandler1 = new ConcreteHandler1();
ConcreteHandler2 concreteHandler2 = new ConcreteHandler2();
concreteHandler1.successor = concreteHandler2;
concreteHandler2.successor = concreteHandler1;
concreteHandler2.handleRequest("ConcreteHandler1");
}
}
2.4 Android源碼對應實現
View的事件分發(fā)
2.5.1 Activity分發(fā)
A.activity事件分發(fā)是先調用dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
從代碼可知道:
1.是否執(zhí)行onTouchEvent(ev)取決于getWindow().superDispatchTouchEvent(ev).
2.如果getWindow().superDispatchTouchEvent(ev)返回的是true則onTouchEvent不會執(zhí)行何恶。
3.getWindow()實際上是phonwindow,并在其中調用了mDecor.superDispatchTouchEvent(). DecorView繼承于viewgroup嚼黔,所以實際調用了父類的dispatchTouchEvent().于是進入了ViewGroup的事件分發(fā)细层。
B.ViewGroup事件分發(fā)
ViewGroup的dispatchTouchEvent()方法
1.先設置mFirstTouchTarget = null;
如果ViewGroup的有子元素成功處理惜辑,mFirstTouchTarget就會指向該元素
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
/**
* Clears all touch targets.
*/
private void clearTouchTargets() {
TouchTarget target = mFirstTouchTarget;
if (target != null) {
do {
TouchTarget next = target.next;
target.recycle();
target = next;
} while (target != null);
mFirstTouchTarget = null;
}
}
2.默認情況下所有的ViewGroup都是默認不攔截的
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
&& ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
&& isOnScrollbarThumb(ev.getX(), ev.getY())) {
return true;
}
return false;
}
3.然后會查找子控件是否有事件消耗。如果找到了就會設置FirstTouchTarget不為空且設置 alreadyDispatchedToNewTouchTarget = true
for (int i = childrenCount - 1; i >= 0; i--) {
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
4.dispatchTransformedTouchEvent區(qū)分是本view還是child
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
// Canceling motions is a special case. We don't need to perform any transformations
// or filtering. The important part is the action, not the contents.
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
}
- 當是本view調用父類的dispatchTouchEvent也就是View的dispatchTouchEvent疫赎。
- 如果是子view這根據情況調用viewgroup或者view的dispatchTouchEvent盛撑。
C.View事件分發(fā)
dispatchTouchEvent
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
- 如果onTouchListener的onTouch方法返回了true,那么view里面的onTouchEvent就不會被調用了捧搞。
- 如果view為disenable,則:onTouchListener里面不會執(zhí)行抵卫,但是會執(zhí)行onTouchEvent(event)方法