摘要:轉(zhuǎn)載請注明出處作者地址本文出自簡書人人都會設(shè)計模式教程簡介閱讀對象本篇教程適合新手閱讀,老手直接略過教程難度初級,本人水平有限,文章內(nèi)容難免會出現(xiàn)問題,如果有問題歡迎指出,謝謝地址正文一什么是觀察者模式生活中的觀察者模式警察抓小偷在現(xiàn)實生活
PS:轉(zhuǎn)載請注明出處
作者: TigerChain
地址: http://www.jianshu.com/p/b972ba509c66
本文出自 TigerChain 簡書 人人都會設(shè)計模式
教程簡介
1、閱讀對象
本篇教程適合新手閱讀,老手直接略過
2、教程難度
初級,本人水平有限,文章內(nèi)容難免會出現(xiàn)問題,如果有問題歡迎指出,謝謝
3、Demo 地址:https://github.com/githubchen001/DesignPattern/tree/master/app/src/main/java/designpattern/jun/com/designpattern/Observer/CustomEventBus
一、什么是觀察者模式 1、生活中的觀察者模式正文
1、警察抓小偷
在現(xiàn)實生活中,警察抓小偷是一個典型的觀察者模式「這以一個慣犯在街道逛街然后被抓為例子」,這里小偷就是被觀察者,各個干警就是觀察者,干警時時觀察著小偷,當小偷正在偷東西「就給干警發(fā)送出一條信號,實際上小偷不可能告訴干警我有偷東西」,干警收到信號,出擊抓小偷。這就是一個觀察者模式
2、裝模作樣寫作業(yè)
小時候家里家活比較多,爸媽讓我去干活的時候,我偶爾會說我要學(xué)習(xí)「其實不想去干活,當然只是偶爾,我還是常常干家務(wù)的」,然后爸媽就去地里了,我一個人在家里,先擺出一張桌子「上面放好想看的書」,然后打開電視機看起電視劇,但是又怕家里人回到家中看到我在看電視,于是把家里的大門鎖住「當爸媽回的時候肯定要開門」,當我聽見開門聲就會立馬關(guān)掉電視,做到作業(yè)桌上「裝模作樣寫作業(yè)」----在這過程中:我充當?shù)木褪怯^察者,爸媽就是被觀察者,他們開門就會觸發(fā)門響「相當于告訴我說他們回來了」,我聽到響聲「關(guān)電視,寫作業(yè)」,有過類似的經(jīng)驗的朋友們下面點個贊
3、遠程視頻會議等
老板和員工遠程開會:老板是被觀察者,員工是觀察者。微信公號:微信公號作者是被觀察者,微信用戶是觀察者「當公號作者發(fā)送一篇文章,關(guān)注了公號的觀察者都可以收到文章」等
2、程序中的觀察者模式觀察者模式的定義
觀察者模式描述的是一種一對多的關(guān)系「一個被觀察者對應(yīng)多個觀察者」,當被觀察者的狀態(tài)發(fā)生改變時,所有觀察者都會得到通知。通俗的理解:觀察者模式就是在特定的時刻「被觀察者發(fā)送通知」干特定的事情「觀察者收到通知處理自己相應(yīng)的事件」
觀察者模式的特點
觀察者模式的三要素:觀察者,被觀察者,事件「訂閱」
觀察者模式的結(jié)構(gòu)
角色 | 類別 | 說明 |
---|---|---|
Subject | 接口或抽象類 | 主題也叫被觀察者 |
RealSubject | 真實的主題類 | 具體的被觀察者,內(nèi)部維護了觀察者的列表 |
IObserver | 觀察者接口或抽象類 | 抽象出觀察者的接口 |
RealObserver | 具體的觀察者 | 被觀察者有更新,觀察者立馬響應(yīng)更新 |
二、觀察者模式舉例觀察者模式簡單的 UML
在舉例之前,我們先看看一個概念--回調(diào),什么是回調(diào):就調(diào)用一圈又回到自已了「通俗的就可以這樣認為」
1、回調(diào)例子一:小明叫爸爸吃飯
舉個例子,小明媽媽做好了飯,讓小明去地里叫他爸回來吃飯,小明說好的我馬上去,過了半個小時小明和他爸一起來了,小明給媽媽的說:“媽,爸回來了”,媽媽說:“好的我知道了,讓你爸洗洗手吃飯吧”,在這一過程中,小明給媽媽的說:“媽,爸回來了”就是一個回調(diào),不好理解?那看代碼吧
小明叫爸爸吃飯簡單的 UML
寫代碼
1、定義一個回調(diào)接口
2、定義媽媽類
3、定義小明類
4、測試
5、運行查看結(jié)果
這就是回調(diào),我們看看的數(shù)據(jù)的走向 Mom-->xiaoming-->Mom 轉(zhuǎn)了一圈回來了,這就是回調(diào)
例子二,模擬Android 中 View 的點擊事件
經(jīng)過例子一,我敢保證多數(shù)朋友對回調(diào)還是稀里糊涂,不要緊,我們再來一個例子感受一下,做過 Android 的朋友一定調(diào)用過 View.setOnclickListener(OnClickListener onClickListener) 點擊函數(shù),沒錯 OnClickListener 就是一個回調(diào)接口,我們來使用 java 代碼模擬一下這個過程
先看一下 UML
根據(jù) UML 寫代碼
1、 定義一個 View 類
public class View { private OnClickListener onClickListener ; // 觸發(fā)點擊事件 protected void click(){ if(onClickListener !=null){ onClickListener.onClick(this); } } // 設(shè)置回調(diào) public void setOnClickListener(OnClickListener onClickListener){ this.onClickListener = onClickListener ; } public interface OnClickListener{ // 定義回調(diào)方法 void onClick(View v) ; } }
2、定義一個 Button 類
/** * Created by TigerChain * 定義一個按鈕 */ public class Button extends View { public void click(){ super.click(); } }
3、測試類 Test
public class Test { public static void main(String args[]){ Button button = new Button() ; //看到了沒,看到這里是不是很親切,是不是發(fā)現(xiàn) 次哦! 這就是回調(diào) button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { System.out.print("自定義 View 的回調(diào)事件"); } }); // 模擬用戶點擊這個運作,Android 系統(tǒng)的 View 是重寫手勢來調(diào)用這個方法的,沒有暴露給用戶 button.click(); } }
4、運行查看結(jié)果
如果你看 Android 源碼,或是三方的源碼會發(fā)現(xiàn)好多這樣的回調(diào)方法,比如網(wǎng)絡(luò)請求成功失敗的回調(diào)等。
使用觀察者模式實現(xiàn)自定義 View
定義 View 「被觀察者」
public class View { //被觀察者的列表 private ArrayListonClickListeners = new ArrayList<>() ; // 觸發(fā)通知 protected void click(){ for(OnClickListener onClickListener:onClickListeners){ if(onClickListener !=null){ onClickListener.onClick(View.this); } } } // 注冊觀察者 public void setOnClickListener(OnClickListener onClickListener){ onClickListeners.add(onClickListener) ; } public interface OnClickListener{ // 定義通知的方法 void onClick(View v) ; } public void unRegister(OnClickListener onClickListener){ if(onClickListeners.contains(onClickListener)){ onClickListeners.remove(onClickListener) ; } } }
注意這里的 OnClickListener 就是抽象的觀察者
2、定義一個 Button
/** * Created by TigerChain */ public class Button extends View { }
3、測試 Test
public class Test { public static void main(String args[]){ //定義一個被觀察者 Button button = new Button() ; //注冊其中一個觀察者 button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { System.out.println("TigerChain"); } }); // 注冊另一個觀察者 button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { System.out.println("never give up"); } }); // 被觀察者觸發(fā)事件 button.click(); } }
4、運行查看結(jié)果
PS:總結(jié)看到了沒,觀察者模式和回調(diào)是如此的相似,如果我們把上面的注冊觀察者改成一個「和 View 回調(diào)一模一樣」,可以說回調(diào)是一種特殊的觀察者模式,回調(diào)和觀察者聯(lián)系和區(qū)別
1、回調(diào)可以說只有一個觀察者,是一對一,是一種特殊的觀察者模式「我是這樣的認為的,個人觀點,如果有誤歡迎指出」
2、觀察者被觀察者持有觀察的列表,是一種一對多的關(guān)系
3、回調(diào)是一種監(jiān)聽方式,觀察者模式是一種解決方案「設(shè)計模式」
有了回調(diào)的基礎(chǔ),下面我們來看看觀察者模式的幾個精典例子
2、觀察者舉例1、微信公號推送文章
最近看了我文章的人都知道我最近在寫關(guān)于設(shè)計模式這一系列,在這里我「TigerChain」就是一個被觀察者,普通的微信用戶就是觀察者,如果微信用戶關(guān)注了 TigerChain ,那么我推送的每一篇的文章,微信用戶就會第一時間收到我的文章「訂閱 TigerChain的用戶」,這就是一個典型的觀察者模式
微信公號推送文章簡單的 UML
根據(jù) UML 擼碼
1、定義抽象的被觀察者 IWxServerSubject.java
/** * Created by TigerChain * 定義主題「被觀察者接口」,所有的公號作者共同屬性「其實這里功能上微信系統(tǒng)的功能,直接抽象成被觀察者」 */ public interface IWxServerSubject { // 添加觀察者 void attchObserver(IObserver iObserver) ; // 移除觀察者 void detachObserver(IObserver iObserver) ; // 通知觀察者 void notifyObserver() ; }
2、定義抽象的觀察者接口 Observer.Java
/** * Created by TigerChain * 定義觀察者接口,即關(guān)注公號的微信用戶共同屬性 */ public interface Observer { // 觀察者收到信息,內(nèi)容為 info void reciveContent(String info) ; }
3、定義具體的被觀察者「公號作者 TigerChain」 TigerChainSubject.java
/** * Created by TigerChain * 定義一個真實的被觀察者 TigerChain「公號的作者」 * 里面存了訂閱 TigerChain 微信公眾賬號的讀者 */ public class TigerChainSubject implements IWxServerSubject { // 訂閱者列表「觀察者列表」,即關(guān)注 TigerChain 公號的讀者 private Listobservers = new ArrayList<>() ; //作者更新公號的內(nèi)容 private String updateContent ; @Override public void attchObserver(IObserver iObserver) { observers.add(iObserver) ; } @Override public void detachObserver(IObserver iObserver) { if(observers.contains(iObserver)) { observers.remove(iObserver); } } @Override public void notifyObserver() { for (IObserver iObserver:observers) { iObserver.reciveContent(updateContent); } } /** * 是否關(guān)注我的公號 * @param iObserver * @return */ public boolean isAttchObserver(IObserver iObserver){ return observers.contains(iObserver) ; } /** * TigerChain 在公號中發(fā)布文章 * @param updateContent */ public void submitContent(String updateContent){ this.updateContent = updateContent ; this.notifyObserver(); } }
4、定義一個具體的觀察者「普通的微信用戶」 ReaderObserver.java
/** * Created by TigerChain * 微信用戶 */ public class ReaderObserver implements Observer { // 微信用戶的姓名 private String name ; public ReaderObserver(String name){ this.uname = name ; } @Override public void reciveContent(String info) { System.out.println(uname+"注意,TigerChain 發(fā)送了文章---"+info); } public String getUname(){ return this.name ; } }
可以看到微信用戶有接收推送文章的能力「前提是要關(guān)注公號作者」
5、來個 Test 類測試一下吧
/** * Created by TigerChain * 測試類 */ public class Test { public static void main(String args[]){ IWxServerSubject iWxServerSubject = new TigerChainSubject() ; // 微信用戶 ReaderObserver zhangsai = new ReaderObserver("張三") ; ReaderObserver lisi = new ReaderObserver("李四") ; ReaderObserver wangwu = new ReaderObserver("王五") ; ReaderObserver zhaoLiu = new ReaderObserver("趙六") ; // 微信用戶張三關(guān)注我的公號「即訂閱」 iWxServerSubject.attchObserver(zhangsai); // 微信用戶李四關(guān)注我的公號「即訂閱」 iWxServerSubject.attchObserver(lisi); // 微信用戶王五關(guān)注我的公號「即訂閱」 iWxServerSubject.attchObserver(wangle); // 我「被觀察者」發(fā)布了一篇文章--觀察者模式 ((TigerChainSubject)iWxServerSubject).submitContent("人人都會設(shè)計模式:觀察者模式") ; boolean isAttch = ((TigerChainSubject)iWxServerSubject).isAttchObserver(zhaoLiu) ; if(!isAttch){ System.out.println(zhaoLiu.getUname()+"你好!你還沒有關(guān)注 TigerChain ,請關(guān)注先,謝謝"); } } }
我們看到和現(xiàn)實情況一樣,普通微信用戶關(guān)注公號作者,然后作者發(fā)送文章,用戶就可以收到文章了
6、運行查看結(jié)果
2、狼王開會
話說冬天來了,狼得找過冬的食物,狼王組織如開了緊急會議,下面的群狼都看著狼王傳遞會議精神和安排任務(wù),此時狼王就是被觀察者,群狼就是觀察者,我們來看看 UML
狼王開會簡單的 UML
根據(jù) UML 擼碼
1、抽象被觀察者功能 Wolf.Java
/** * Created by TigerChain * 抽象的被觀察者 */ public interface Wolf { // 添加觀察者 void attchObserver(NormalWolf observer) ; // 移除觀察者 void detchObserver(NormalWolf observer) ; // 通知觀察者 void notifyObserver(String str) ; }
2、抽象觀察者普通的狼 NormalWolf.java
/** * Created by 抽象的觀察者,普通的狼 */ public abstract class NormalWolf { // 拿到被觀察者的引用 protected IWolf iWolf ; /** * 收到狼王下達的命令 * @param str */ public abstract void reciveCommand(String str) ; }
3、定義具體的被觀察者狼王 LangWang.java
由于一個狼群中只有一個狼王,所以狼王是一個單例「學(xué)以致用,這里把單例模式也使用一把」
/** * Created by TigerChain * 狼王「被觀察者,下面的狼都看狼王的眼色行事」,是一個單例模式 */ public class LangWang implements Wolf{ private static LangWang instance ; private LangWang(){} public static LangWang getInstance(){ if(instance == null){ synchronized (LangWang.class){ if(instance == null){ instance = new LangWang() ; } } } return instance ; } // 除過狼王外的狼「觀察者」 private Listobservers = new ArrayList<>() ; // 狼王下達的命令 private String mingLing ; @Override public void attchObserver(NormalWolf observer) { observers.add(observer); } @Override public void detchObserver(NormalWolf observer) { if(observers.contains(observer)){ observers.remove(observer) ; } } @Override public void notifyObserver(String str) { for(NormalWolf observer:observers){ observer.reciveCommand(str); } } /** * 下達命令 * @param mingLing */ public void xiaDaMingling(String mingLing){ this.mingLing = mingLing ; this.notifyObserver(mingLing); } }
4、定義一個觀察者偵查狼 ZhenChaLang.java
/** * Created by TigerChain * 偵查狼,另一個觀察者 */ public class ZhenChaLang extends NormalWolf { public ZhenChaLang(IWolf iWolf){ this.iWolf = iWolf ; this.iWolf.attchObserver(this); } @Override public void reciveCommand(String string) { System.out.println("偵查狼:狼王開會傳遞的信息是 "+string); } }
在這里我們實例化一個偵查狼的時候就會把它注冊到被觀察者中,也就是狼王開會的時候,群狼肯定狼群中的一員「外來狼可不行」,只有內(nèi)部狼「內(nèi)部會員」才有資格開會「這種關(guān)系就相當于注冊這個過程」
5、定義另一個觀察者捕獵狼 BuLieLang.java
/** * Created by TigerChain * 捕獵狼---觀察者 */ public class BuLieLang extends NormalWolf { public BuLieLang(IWolf iWolf){ this.iWolf = iWolf ; // 添加觀察者,即捕獵狼放在狼王組織中 this.iWolf.attchObserver(this); } @Override public void reciveCommand(String string) { System.out.println("捕獵狼:狼王開會傳遞的信息是 "+string+" "); } }
測試類 Test
/** * Created by TigerChain * 測試類 */ public class Test { public static void main(String args[]){ // 使用單例模式 LangWang langWang = LangWang.getInstance() ; BuLieLang buLieLang = new BuLieLang(langWang) ; ZhenChaLang zhenChaLang = new ZhenChaLang(langWang) ; // 狼王下達命令就是發(fā)送通知 langWang.xiaDaMingling("1、分工合作,捕獵狼根據(jù)偵查狼反饋看機行事 " + "2、偵查狼永遠把危險放在第一位,遇到危險第一時間提醒大家撤退"); } }
6、運行查看結(jié)果
狼王下達命令就是發(fā)送通知,那么現(xiàn)場中的狼都會收到通知,典型的觀察者模式
3、自定義 EventBus
在 Android 中我們常常使用 EventBus,它相當于是一個單例廣播,我們來自定義一個簡單的 EventBus 「不考慮線程切換」,其實它也是一種觀察者模式「俗稱發(fā)布、訂閱模式」
自定義 EventBus 簡單的 UML
代碼這里不貼了,我已經(jīng)上傳到 github 上了,大家可以自行看看:https://github.com/githubchen001/DesignPattern/tree/master/app/src/main/java/designpattern/jun/com/designpattern/Observer/CustomEventBus
三、Android 源碼中的觀察者模式1、RecyclerView 中使用觀察者模式
RecyclerView 中觀察者模式簡單的 UML
源碼就不分析了「貼出代碼估計又得一篇來說」,給出下面流程,大家自行看一下就明白了,動動手印象更深
從 setAdapter 開始看一下觀察者流程
2、ViewTreeObserver
ViewTreeObserver 是用來監(jiān)聽視圖樹的觀察者,如果視圖樹發(fā)生全局改變的時候就會收到通知
其中,被觀察者是 ViewTree ,觀察者是 ViewTreeObserver
抽取 ViewTreeObserver 部分代碼講解
這里說說 view.getViewTreeObserver().addOnGlobalLayoutListener(xxx) 場景,其它的雷同
public final class ViewTreeObserver { ... public interface OnGlobalLayoutListener { public void onGlobalLayout(); } ... // 添加監(jiān)聽器 public void addOnGlobalLayoutListener(OnGlobalLayoutListener listener) { checkIsAlive(); if (mOnGlobalLayoutListeners == null) { mOnGlobalLayoutListeners = new CopyOnWriteArray(); } mOnGlobalLayoutListeners.add(listener); } } ... // 分發(fā)事件相當于發(fā)送通知,即被觀察者調(diào)用--View public final void dispatchOnGlobalLayout() { final CopyOnWriteArray listeners = mOnGlobalLayoutListeners; if (listeners != null && listeners.size() > 0) { CopyOnWriteArray.Access access = listeners.start(); try { int count = access.size(); for (int i = 0; i < count; i++) { access.get(i).onGlobalLayout(); } } finally { listeners.end(); } } }
現(xiàn)在我們有了觀察者 ViewTreeObserver ,觀察者是 ViewTree 我們說了,主要問題的就是 dispatchOnGlobalLayout 誰調(diào)用了,只有觸發(fā)了這個方法那么事件就回調(diào)回來了「這個方法肯定是被觀察者調(diào)用了,系統(tǒng)調(diào)用的」,方法在 ViewRootImpl「關(guān)于 ViewRootImpl 可自行去查看,不在本篇的范圍」 中體現(xiàn)出來了
看看 ViewRootImpl 的部分代碼
public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks { ... private void performTraversals(){ ... // 執(zhí)行測量 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); ... // 執(zhí)行布局 performLayout(lp, mWidth, mHeight); ... if (triggerGlobalLayoutListener) { mAttachInfo.mRecomputeGlobalAttributes = false; // 注意看這里,這里觸發(fā)了 dispatchOnGlobalLayout 方法,系統(tǒng)調(diào)用 mAttachInfo.mTreeObserver.dispatchOnGlobalLayout(); } ... // 執(zhí)行繪制 performDraw(); ... } ... }
看到了沒,mAttachInfo.mTreeObserver.dispatchOnGlobalLayout() 方法是在 ViewRootImpl 中調(diào)用了「即是 View 調(diào)用了,只要 View 樹而已發(fā)生改變,就會調(diào)用」,是由系統(tǒng)調(diào)用的「View 的布局完成這后,就會調(diào)用」,并且還調(diào)用了自定義 View 的測量,布局,繪制方法。
使用場景:比如我們想在 Activity 的 onCreate() 方法中取得某個 View 的寬高,此時是取不到的,由于布局還沒有完成加載之前取到的是 0 ,所以使用 view.getViewTreeObserver().addOnGlobalLayoutListener(xxx) 里面就可以獲取到 view 的寬高了,demo 代碼如下
view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() {// 當layout執(zhí)行結(jié)束后回調(diào) //使用完必須撤銷監(jiān)聽(只測量一次),否則,會一直不停的不定時的測量,這比較耗性能 view.getViewTreeObserver().removeOnGlobalLayoutListener(this);//Added in API level 16 //view.getViewTreeObserver().removeGlobalOnLayoutListener(this);//廢棄了 int width = view.getMeasuredWidth(); int width2 = view.getWidth();//和上面的值是一樣的 } });
3、ListView
ListView 中使用觀察者模式和 RecyclerView 類似,大家可以扒扒這部分源碼,這里就不說了
四、觀察者模式的優(yōu)缺點優(yōu)點
1、解耦,被觀察者只知道觀察者列表「抽象接口」,被觀察者不知道具體的觀察者
2、被觀察者發(fā)送通知,所有注冊的觀察者都會收到信息「可以實現(xiàn)廣播機制」
缺點
1、如果觀察者非常多的話,那么所有的觀察者收到被觀察者發(fā)送的通知會耗時
2、觀察者知道被觀察者發(fā)送通知了,但是觀察者不知道所觀察的對象具體是如何發(fā)生變化的
3、如果被觀察者有循環(huán)依賴的話,那么被觀察者發(fā)送通知會使觀察者循環(huán)調(diào)用,會導(dǎo)致系統(tǒng)崩潰
到此為止,我們的觀察者模式就說完了,一定要扒扒 Android 源碼中相應(yīng)的觀察者模式,你會有一種恍然大悟的感覺,點贊是一美德
以后文章會第一時間發(fā)在公號,請大家添加博文的公號,掃描添加即可關(guān)注
公眾號:TigerChain
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/70735.html
摘要:作者佳杰本文原創(chuàng),轉(zhuǎn)載請注明作者及出處如何實現(xiàn)框架中的數(shù)據(jù)綁定一數(shù)據(jù)綁定概述視圖和數(shù)據(jù)之間的綁定二數(shù)據(jù)綁定目的不用手動調(diào)用方法渲染視圖,提高開發(fā)效率統(tǒng)一處理數(shù)據(jù),便于維護三數(shù)據(jù)綁定中的元素視圖說白了就是中元素的展示數(shù)據(jù)用于保存數(shù)據(jù)的引用類型 作者:佳杰本文原創(chuàng),轉(zhuǎn)載請注明作者及出處 如何實現(xiàn)VM框架中的數(shù)據(jù)綁定 一:數(shù)據(jù)綁定概述 視圖(view)和數(shù)據(jù)(model)之間的綁定 二:...
摘要:三內(nèi)置觀察者模式了解內(nèi)置觀察者模式包內(nèi)包含最基本的類與接口,這和上面的接口與接口很類似。根據(jù)具體的需求,如果內(nèi)置的觀察者模式不能滿設(shè)計,那么我們可以像剛開始那樣自己實現(xiàn)一套觀察者模式。參考資料設(shè)計模式 一、了解觀察者模式 1.1 什么是觀察者模式 觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象狀態(tài)改變時,它的所有依賴者都會收到通知并自動更新。 典型的問題比如報社,只要你是他...
摘要:為了幫助灰太狼擺脫被老婆平底鍋抽的悲劇,發(fā)起了解救灰太狼的行動,必須要知道觀察者模式。持有觀察者對象的集合。設(shè)計模式源碼下載 相信大家都有看過《喜洋洋與灰太狼》,說的是灰太狼和羊族的斗爭,而每次的結(jié)果都是灰太狼一飛沖天,伴隨著一句我還會回來的......。為灰太狼感到悲哀,抓不到羊,在家也被老婆平底鍋虐待。灰太狼為什么會這么背? 很簡單,灰太狼本身就有暴露行蹤的屬性,羊咩咩就能知曉灰太...
閱讀 2033·2023-04-25 23:30
閱讀 1455·2021-11-24 10:18
閱讀 3079·2021-10-09 09:54
閱讀 2021·2021-10-08 10:05
閱讀 3436·2021-09-23 11:21
閱讀 3165·2019-08-30 15:52
閱讀 1563·2019-08-30 13:05
閱讀 1061·2019-08-30 13:02