관찰자 모드 와 리 셋 메커니즘 이 안 드 로 이 드 소스 코드 를 만 났 을 때

지난 블 로 그 는 안 드 로 이 드 소스 코드 의 장식 자 모델 을 공 유 했 습 니 다. 아직 끝나 지 않 았 습 니 다. 오늘 은 안 드 로 이 드 의 관찰자 모델 을 공유 하 겠 습 니 다. 참고 로 관찰자 모델 과 리 셋 체제 의 관 계 를 말씀 드 리 겠 습 니 다. 벽돌 을 찍 는 것 을 환영 합 니 다.
관찰자 모드
정의.
관찰자 모델 은 한 쌍 의 다 중 의존 관 계 를 정의 하여 여러 관찰자 대상 이 특정한 주제 대상 을 동시에 감청 하도록 한다.이 테마 대상 은 상태 가 변 할 때 모든 관찰자 대상 에 게 자동 으로 자신 을 업데이트 할 수 있 도록 알려 줍 니 다.
관찰자 모드 의 구조
관찰자 모드 와 관련 된 역할 은 다음 과 같다.
  • 추상 적 인 주제 (Subject) 역할: 추상 적 인 주제 역할 은 모든 관찰자 대상 에 대한 인용 을 하나의 집합 (예 를 들 어 Array List 대상) 에 저장 하고 모든 주 제 는 모든 수량의 관찰자 가 있 을 수 있다.추상 적 인 주 제 는 하나의 인 터 페 이 스 를 제공 하여 관찰자 의 대상 을 증가 하고 삭제 할 수 있 으 며 추상 적 인 주제 역할 은 추상 적 인 피 관찰자 (Observable) 역할 이 라 고도 부른다.
  • 구체 적 인 주제 (Concrete Subject) 역할: 관련 상 태 를 구체 적 인 관찰자 대상 에 저장 합 니 다.구체 적 인 주제 의 내부 상태 가 바 뀌 었 을 때 등 록 된 모든 관찰자 에 게 통 지 를 한다.구체 적 인 주제 역할 은 구체 적 인 피 관찰자 (Concrete Observable) 역할 이 라 고도 부른다.  
  • 추상 적 인 관찰자 (Observer) 역할: 모든 구체 적 인 관찰자 에 게 인 터 페 이 스 를 정의 하고 주제 의 통 지 를 받 을 때 자신 을 업데이트 합 니 다. 이 인 터 페 이 스 는 업데이트 인터페이스 라 고 합 니 다.  
  • 구체 적 인 관찰자 (Concrete Observer) 역할: 저장 과 주제 의 상태 가 적당 한 상태 입 니 다.구체 적 인 관찰자 역할 은 추상 적 인 관찰자 역할 이 요구 하 는 업데이트 인 터 페 이 스 를 실현 하여 자신의 상태 와 주제 의 상 태 를 조 화 롭 게 한다.필요 하 다 면 구체 적 인 관찰자 역할 은 구체 적 인 주제 대상 을 가리 키 는 인용 을 유지 할 수 있다.

  • 이루어지다
    추상 테마 캐릭터 클래스
    public abstract class Subject {
        /**
         *             
         */
        private    List list = new ArrayList();
        /**
         *        
         * @param observer         
         */
        public void attach(Observer observer){
    
            list.add(observer);
            System.out.println("Attached an observer");
        }
        /**
         *        
         * @param observer         
         */
        public void detach(Observer observer){
    
            list.remove(observer);
        }
        /**
         *             
         */
        public void nodifyObservers(String newState){
    
            for(Observer observer : list){
                observer.update(newState);
            }
        }
    }
    

    구체 적 주제 캐릭터 클래스
    public class ConcreteSubject extends Subject{
    
        private String state;
    
        public String getState() {
            return state;
        }
    
        public void change(String newState){
            state = newState;
            System.out.println("     :" + state);
            //      ,       
            this.nodifyObservers(state);
        }
    }
    

    추상 관찰자 역할 류
    public interface Observer {
        /**
         *     
         * @param state         
         */
        public void update(String state);
    }
    

    구체 적 관찰자 역할 류
    public class ConcreteObserver implements Observer {
        //      
        private String observerState;
    
        @Override
        public void update(String state) {
            /**
             *         ,            
             */
            observerState = state;
            System.out.println("   :"+observerState);
        }
    
    }
    

    테스트 클래스
    public class Test {
    
        public static void main(String[] args) {
            //      
            ConcreteSubject subject = new ConcreteSubject();
            //       
            Observer observer = new ConcreteObserver();
            //              
            subject.attach(observer);
            //         
            subject.change("new state");
        }
    
    }
    

    관찰자 의 두 가지 실현 방식
  • Push 테마 대상 이 관찰자 에 게 주제 의 상세 한 정 보 를 전달 하 는데 관찰자 가 필요 하 든 안 하 든 추 송 된 정 보 는 보통 주제 대상 의 전부 또는 일부 데이터 이다.
  • Pull 테마 대상 은 관찰자 에 게 알 릴 때 소량의 정보 만 전달한다.만약 에 관찰자 가 더욱 구체 적 인 정 보 를 필요 로 한다 면 관찰자 가 주동 적 으로 주제 대상 에서 얻 는 것 은 관찰자 가 주제 대상 에서 데 이 터 를 끌 어 당 기 는 것 과 같다.일반적으로 이러한 모델 의 실현 에 있어 서 주제 대상 자 체 를 update () 방법 으로 관찰자 에 게 전달 합 니 다. 그러면 관찰자 가 데 이 터 를 얻 어야 할 때 이 인용 을 통 해 얻 을 수 있 습 니 다.

  • 두 가지 방식 의 비교
  • Push 모델 은 주제 대상 이 관찰자 가 필요 로 하 는 데 이 터 를 알 고 있다 고 가정 하 는 것 이다.한편, Pull 모델 은 주제 대상 이 관찰자 가 구체 적 으로 어떤 데 이 터 를 필요 로 하 는 지 모 르 고 어 쩔 수 없 는 상황 에서 아예 자신 을 관찰자 에 게 전달 하여 관찰자 가 스스로 필요 에 따라 값 을 추출 하도록 한다.
  • Push 모델 은 관찰자 대상 을 재 활용 하기 어 려 울 수 있 습 니 다. 관찰자 의 update () 방법 은 필요 에 따라 정 의 된 매개 변수 이기 때문에 고려 하지 않 은 사용 상황 을 병행 할 수 없습니다.이 는 새로운 상황 이 발생 했 을 때 새로운 update () 방법 을 제공 하거나 관찰 자 를 아예 다시 실현 할 수 있다 는 뜻 이다.한편, Pull 모델 은 이런 상황 을 초래 하지 않 습 니 다. Pull 모델 에서 update () 방법의 매개 변 수 는 주제 대상 자체 이기 때문에 대체적으로 주제 대상 이 전달 할 수 있 는 최대 데이터 집합 으로 각종 상황 의 수요 에 적응 할 수 있 습 니 다.

  • 리 셋 메커니즘 과 관찰자 모델
    Android 에는 Activity 의 라 이 프 사이클, 버튼 클릭 이벤트, 스 레 드 의 run () 방법 등 리 셋 메커니즘 을 사용 하 는 곳 이 매우 많 습 니 다.
    다음은 리 셋 의 기본 모델 입 니 다.
    public interface CallBack {  
        public void oncall();  
    }  
    
    public class A {  
        private CallBack callback;  
        //        
        public void register(CallBack callback){  
            this.callback = callback;  
        }  
        //            
        public void call(){  
            callback.oncall();  
        }  
    }  
    
    public static void main(String[] args) {  
        A a = new A();  
        a.register(new CallBack() {  
            @Override  
            public void oncall() {  
                System.out.println("       ");  
            }  
        });  
        a.call();  
    

    }
    이렇게 보면 리 셋 체제 와 관찰자 모델 은 일치 하 다. 차이 점 은 관찰자 모델 에서 목표 류 는 모든 관찰자 의 인용 을 유지 하고 리 셋 안 은 하나의 인용 만 유지 하 는 것 이다.
    Android 의 관찰자 모드
    Android 에 서 는 관찰자 모드 를 대량으로 사 용 했 고 Framework 계층 의 이벤트 구동 은 모두 관찰자 모드 를 바탕 으로 이 루어 졌 다.또한 Framework 계층 에서 의 각종 서 비 스 는 데이터 가 변 경 될 때 관찰자 모델 을 통 해 상부 데이터 업 데 이 트 를 실현 한다.뷰 의 Listener 감청, GPS 위치 정보 감청, BroadcastReceiver 등 은 모두 관찰자 모드 를 바탕 으로 이 루어 졌 다.다음은 ListView 의 관찰자 모델 이 어떻게 실현 되 었 는 지, RecyclerView 는 대동소이 하고 관심 있 는 것 은 스스로 연구 할 수 있다.
    Listview 의 notifyDataSetChanged ()
    먼저 listview 부분 관찰자 모드 의 구 조 를 살 펴 보 겠 습 니 다.
    그 중에서 관 계 를 연구 하 는 데 편리 하도록 우 리 는 Adapter 부분의 일부 관 계 를 생략 했다.다음은 구체 적 인 호출 관 계 를 살 펴 보 자.
    우선 데이터 가 바 뀌 었 을 때 adapter 의 notifyDataSetChanged () 방법 을 호출 합 니 다.
    /**
     * Common base class of common implementation for an {@link Adapter} that can be
     * used in both {@link ListView} (by implementing the specialized
     * {@link ListAdapter} interface) and {@link Spinner} (by implementing the
     * specialized {@link SpinnerAdapter} interface).
     */
    public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
        private final DataSetObservable mDataSetObservable = new DataSetObservable();
    
        public boolean hasStableIds() {
            return false;
        }
    
        public void registerDataSetObserver(DataSetObserver observer) {
            mDataSetObservable.registerObserver(observer);
        }
    
        public void unregisterDataSetObserver(DataSetObserver observer) {
            mDataSetObservable.unregisterObserver(observer);
        }
    
        /**
         * Notifies the attached observers that the underlying data has been changed
         * and any View reflecting the data set should refresh itself.
         */
        public void notifyDataSetChanged() {
            mDataSetObservable.notifyChanged();
        }
    
        /**
         * Notifies the attached observers that the underlying data is no longer valid
         * or available. Once invoked this adapter is no longer valid and should
         * not report further data set changes.
         */
        public void notifyDataSetInvalidated() {
            mDataSetObservable.notifyInvalidated();
        }
    }
    

    상기 코드 에 따라 mDataSetObservable. notifyChanged () 방법 을 찾 을 수 있 습 니 다.
    /**
     * A specialization of {@link Observable} for {@link DataSetObserver}
     * that provides methods for sending notifications to a list of
     * {@link DataSetObserver} objects.
     */
    public class DataSetObservable extends Observable {
        /**
         * Invokes {@link DataSetObserver#onChanged} on each observer.
         * Called when the contents of the data set have changed.  The recipient
         * will obtain the new contents the next time it queries the data set.
         */
        public void notifyChanged() {
            synchronized(mObservers) {
                // since onChanged() is implemented by the app, it could do anything, including
                // removing itself from {@link mObservers} - and that could cause problems if
                // an iterator is used on the ArrayList {@link mObservers}.
                // to avoid such problems, just march thru the list in the reverse order.
                for (int i = mObservers.size() - 1; i >= 0; i--) {
                    mObservers.get(i).onChanged();
                }
            }
        }
    
        /**
         * Invokes {@link DataSetObserver#onInvalidated} on each observer.
         * Called when the data set is no longer valid and cannot be queried again,
         * such as when the data set has been closed.
         */
        public void notifyInvalidated() {
            synchronized (mObservers) {
                for (int i = mObservers.size() - 1; i >= 0; i--) {
                    mObservers.get(i).onInvalidated();
                }
            }
        }
    }
    

    notifyChanged () 방법 을 호출 하면 mObserver 를 옮 겨 다 니 며 모든 관찰자 의 onchange () 방법 을 호출 합 니 다.
    그렇다면 문제 가 생 겼 다. 우리 관찰자 대상 은 언제 추 가 됐 을 까?ListView 가 처음으로 BaseAdapter 와 연 결 된 곳, 즉 setAdapter (ListAdapter adapter) 방법 을 살 펴 보 자.
    @Override
    public void setAdapter(ListAdapter adapter) {
        //        Adapter,            。
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }
    
        //      
    
        if (mAdapter != null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            mOldItemCount = mItemCount;
            mItemCount = mAdapter.getCount();
            checkFocus();
    
            //            
            mDataSetObserver = new AdapterDataSetObserver();
            //    DataSetObservable     
            mAdapter.registerDataSetObserver(mDataSetObserver);
    
            //      
        } else {
            mAreAllItemsSelectable = true;
            checkFocus();
            // Nothing selected
            checkSelectionChanged();
        }
    
        requestLayout();
    }
    

    이렇게 해서 우리 의 네 가지 역할 이 다 되 었 다. Observable - > Subject;DataSetObservable—>Concrete Subject;DataSetObserver—>Observer;AdapterDataSetObserver—>Concrete Observer。그리고 우리 가 등록 한 곳 도 찾았어 요.
    마지막 으로 우리 의 데이터 가 어떻게 이 문 제 를 갱신 하 는 지 남 았 다.AdapterDataSetObserver 는 ListView 의 부모 클래스 AbsListView 에 정의 되 어 있 으 며, AbsListView 의 부모 클래스 AdapterView 의 AdapterDataSetObserver 를 계승 합 니 다.
    class AdapterDataSetObserver extends DataSetObserver {
    
        private Parcelable mInstanceState = null;
        //     Adapter notifyDataSetChanged            onChanged  ,        
        @Override
        public void onChanged() {
            mDataChanged = true;
            mOldItemCount = mItemCount;
            //   Adapter      
            mItemCount = getAdapter().getCount();
    
            // Detect the case where a cursor that was previously invalidated has
            // been repopulated with new data.
            if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
                    && mOldItemCount == 0 && mItemCount > 0) {
                AdapterView.this.onRestoreInstanceState(mInstanceState);
                mInstanceState = null;
            } else {
                rememberSyncState();
            }
            checkFocus();
            //     ListView、GridView AdapterView  
            requestLayout();
        }
    
        //     
    
        public void clearSavedState() {
            mInstanceState = null;
        }
    } 
    

    requestLayout () 방법 은 View 에서 실현 되 고 하위 View 는 필요 에 따라 다시 씁 니 다.주석 좀 봅 시다.
    /*Call this when something has changed which has invalidated the layout of this view. This will schedule a layout pass of the view tree./
    자, 여기까지 의 모든 호출 관 계 를 우 리 는 기본적으로 알 게 되 었 다.ListView 의 데이터 가 변 할 때 Adapter 의 notifyDataSetChanged 함 수 를 호출 합 니 다. 이 함 수 는 DataSetObservable 의 notifyChanged 함 수 를 호출 합 니 다. 이 함 수 는 모든 관찰자 (AdapterDataSetObserver) 의 onChanged 방법 을 호출 합 니 다.onChanged 함수 에서 Adapter 의 데이터 세트 의 새로운 수량 을 가 져 온 다음 ListView 의 requestLayout () 방법 으로 레이아웃 을 다시 하고 사용자 인터페이스 를 업데이트 합 니 다.
    되는대로 정리 하 다
    ListView 는 주로 Adapter 와 관찰자 모델 을 활용 하여 확장 성, 유연성 이 매우 강하 지만 결합 도 는 낮 습 니 다. 이것 은 디자인 모델 이 Android 소스 코드 에서 우수한 활용 사례 라 고 생각 합 니 다.그러면 우 리 는 ListView 구성 요 소 를 실현 하기 위해 더 아름 다운 방법 이 있 는 지 생각 하기 시작 해 야 한다. 우 리 는 이 실현 방향 을 어디 에 응용 할 수 있 습 니까?
    사람 은 생각 할 줄 아 는 갈대 다. 생각 하면 서 우 리 는 남 의 눈 에 큰 신 이 된다.
    참조 링크:
    http://www.itdadao.com/articles/c15a265623p0.html
    http://blog.csdn.net/bboyfeiyu/article/details/44040533
    http://www.cnblogs.com/mythou/p/3370340.html

    좋은 웹페이지 즐겨찾기