Android主流IOC框架淺析

TextView mTextView皆看;
mTextView=(TextView) findViewById(R.id.mTextView);
mTextView.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
// TODO Auto-generated method stub

   }
});

作為一名Android程序員,對(duì)于上面這種機(jī)械化的代碼你一定寫到想吐了背零,或許多數(shù)時(shí)候你只是copy ,paste,然后再改一改腰吟,完了你可能又會(huì)覺得這種代碼毫無(wú)營(yíng)養(yǎng),寫得實(shí)在沒勁徙瓶。俗話說:“不會(huì)偷懶的程序員不是好程序員”毛雇,今天我們就來(lái)探討下如何偷懶嫉称。

到這里可能你已經(jīng)知道我要說的是什么了,是的灵疮,我要說的就是Android中的IOC框架织阅,這類框架中比較早的有:Afinal,Xutils震捣,目前開發(fā)者中呼聲比較高的有Android Annotations蒲稳,ButterKnife,Dagger伍派,RoboGuice等江耀。我在這里就簡(jiǎn)單介紹下比較有代表性的Android Annotations和ButterKnife。

什么是IOC诉植?

Inversion of Control祥国,英文縮寫為IOC,字面翻譯:控制反轉(zhuǎn)晾腔。什么意思呢舌稀?就是一個(gè)類里面需要用到很多個(gè)成員變量,傳統(tǒng)的寫法灼擂,你要用這些成員變量壁查,那么你就new 出來(lái)用唄!IOC的原則是:NO剔应,我們不要new睡腿,這樣耦合度太高,你配置個(gè)xml文件峻贮,里面標(biāo)明哪個(gè)類席怪,里面用了哪些成員變量,等待加載這個(gè)類的時(shí)候纤控,我?guī)湍阕⑷耄╪ew)進(jìn)去挂捻;當(dāng)然了,你又會(huì)覺得船万,寫個(gè)配置文件刻撒,臥槽,這多麻煩耿导。于是乎声怔,又出現(xiàn)了另一種方案,得你嫌配置文件麻煩碎节,那用注解唄在你需要注入的成員變量上面加個(gè)注解捧搞,例如:@Inject,這樣就行了,你總不能說這么個(gè)單詞麻煩吧胎撇。當(dāng)然了介粘,有了配置文件和注解,那么怎么注入呢晚树?其實(shí)就是把字符串類路徑變成類么姻采,這個(gè)時(shí)候需要用到反射。

什么是反射爵憎?

首先JAVA語(yǔ)言并不是一種動(dòng)態(tài)編程語(yǔ)言慨亲,而為了使語(yǔ)言更靈活,JAVA引入了反射機(jī)制宝鼓。 JAVA反射機(jī)制是在運(yùn)行狀態(tài)中刑棵,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法愚铡;對(duì)于任意一個(gè)對(duì)象蛉签,都能夠調(diào)用它的任意一個(gè)方法;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為java語(yǔ)言的反射機(jī)制沥寥。Java反射機(jī)制主要提供了以下功能: 在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類碍舍;在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象;在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法邑雅;在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法片橡;生成動(dòng)態(tài)代理。

什么是注解淮野?

JAVA1.5之后引入的注解和反射捧书,注解的實(shí)現(xiàn)依賴于反射。JAVA中的注解是一種繼承自接口java.lang.annotation.Annotation的特殊接口录煤。那么接口怎么能夠設(shè)置屬性呢鳄厌?簡(jiǎn)單來(lái)說就是JAVA通過動(dòng)態(tài)代理的方式為你生成了一個(gè)實(shí)現(xiàn)了接口Annotation的實(shí)例,然后對(duì)該代理實(shí)例的屬性賦值妈踊,這樣就可以在程序運(yùn)行時(shí)(如果將注解設(shè)置為運(yùn)行時(shí)可見的話)通過反射獲取到注解的配置信息。說的通俗一點(diǎn)泪漂,注解相當(dāng)于一種標(biāo)記廊营,在程序中加了注解就等于為程序打上了某種標(biāo)記。程序可以利用JAVA的反射機(jī)制來(lái)了解你的類及各種元素上有無(wú)何種標(biāo)記萝勤,針對(duì)不同的標(biāo)記露筒,就去做相應(yīng)的事件。標(biāo)記可以加在包敌卓,類慎式,方法,方法的參數(shù)以及成員變量上。

以上算是背景介紹吧瘪吏,也正是基于以上需求和實(shí)現(xiàn)原理癣防,一大波Android IOC框架應(yīng)運(yùn)而生。我們先來(lái)看第一個(gè):Android Annotations掌眠。我們先比較下常規(guī)寫法和Annotations寫法的代碼蕾盯。

常規(guī)寫法

先隨便來(lái)一段吧~~

public class MainActivity extends Activity {
 TextView textView;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  textView = (TextView) findViewById(R.id.text);
  textView.setText("test");

  textView.setOnClickListener(new OnClickListener() {
   
   @Override
   public void onClick(View v) {
    // TODO Auto-generated method stub
    Intent intent = new Intent(this, ChildActivity.class);
    startActivity(intent);
     }
    });
  }
}

Android Annotations寫法

@EActivity(R.layout.activity_main)
public class MainActivity extends Activity {
 @ViewById(R.id.text)
 TextView textView;
 
 @AfterViews
 public void init() {
  textView.setText("annotations test");
 }

 @Click(R.id.text)
 void buttonClick() {
  Intent intent = new Intent(this,ChildActivity_.class);
  startActivity(intent);
}
}

就是這么簡(jiǎn)單

Android Annotations就這么寫?可能你會(huì)問蓝丙,oncreat()去哪了级遭,這可是執(zhí)行入口,你又會(huì)擔(dān)心setContentView()呢渺尘,布局怎么加載挫鸽?看上面的代碼第一行@EActivity(R.layout.activity_main),就這一句全搞定鸥跟,同時(shí)也不需要搞一大堆findViewById掠兄,不需要搞一大推setOnClickListener。Android Annotations能干的事可遠(yuǎn)不止這些锌雀,Http請(qǐng)求蚂夕,開啟線程,事件綁定等等都可以通過一個(gè)注解標(biāo)記搞定腋逆。

其他語(yǔ)法舉例

色值 @ColorRes
  @ColorRes(R.color.backgroundColor)
  int someColor;

  @ColorRes
  int backgroundColor;
動(dòng)畫 @AnimationRes
  @AnimationRes(R.anim.fadein)
  XmlResourceParser xmlResAnim;

  @AnimationRes
  Animation fadein;
自定義View @EViewGroup
@EViewGroup(R.layout.title_with_subtitle)
public class TitleWithSubtitle extends RelativeLayout {

    @ViewById
    protected TextView title, subtitle;

    public TitleWithSubtitle(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setTexts(String titleText, String subTitleText) {
        title.setText(titleText);
        subtitle.setText(subTitleText);
    }

}
HttpClient @HttpsClient
@HttpsClient
HttpClient httpsClient;
UiThread @UiThread
void myMethod() {
    doInUiThread("hello", 100);
}

@UiThread
void doInUiThread(String aParam, long anotherParam) {
    [...]
}

怎么實(shí)現(xiàn)的婿牍?

Android Annotations的實(shí)現(xiàn)原理其實(shí)很簡(jiǎn)單。它會(huì)使用標(biāo)準(zhǔn)的Java注解處理工具自動(dòng)添加一個(gè)額外的編譯步驟生成的源代碼惩歉。其實(shí)就是生成一個(gè)原有類的子類等脂,這個(gè)子類才是真正運(yùn)行用的類。例如上面代碼使用@EActivity注解的MainActivity撑蚌,將生成這個(gè)MainActivity的一個(gè)子類上遥,它的名字是“MainActivity_”。該子類重載一些方法(例如onCreate())争涌,通過委托方式調(diào)用了activity的相關(guān)方法粉楚。所以這里有個(gè)大坑,所有Activity的相關(guān)操作都要操作其子類亮垫,例如 AndroidManifest.xml中類名要寫成android:name=".MainActivity_"模软,開啟MainActivity要寫成 startActivity(new Intent(this, MainActivity_.class));下面是上文中AndroidAnnotations方式寫法的MainActivity的子類MainActivity_,我貼出來(lái)大家隨便感受下饮潦。

public final class MainActivity_
    extends MainActivity
    implements HasViews, OnViewChangedListener
{

    private final OnViewChangedNotifier onViewChangedNotifier_ = new OnViewChangedNotifier();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        OnViewChangedNotifier previousNotifier = OnViewChangedNotifier.replaceNotifier(onViewChangedNotifier_);
        init_(savedInstanceState);
        super.onCreate(savedInstanceState);
        OnViewChangedNotifier.replaceNotifier(previousNotifier);
        setContentView(layout.activity_main);
    }

    private void init_(Bundle savedInstanceState) {
        OnViewChangedNotifier.registerOnViewChangedListener(this);
    }

    @Override
    public void setContentView(int layoutResID) {
        super.setContentView(layoutResID);
        onViewChangedNotifier_.notifyViewChanged(this);
    }

    @Override
    public void setContentView(View view, LayoutParams params) {
        super.setContentView(view, params);
        onViewChangedNotifier_.notifyViewChanged(this);
    }

    @Override
    public void setContentView(View view) {
        super.setContentView(view);
        onViewChangedNotifier_.notifyViewChanged(this);
    }

    public static MainActivity_.IntentBuilder_ intent(Context context) {
        return new MainActivity_.IntentBuilder_(context);
    }

    public static MainActivity_.IntentBuilder_ intent(Fragment supportFragment) {
        return new MainActivity_.IntentBuilder_(supportFragment);
    }

    @Override
    public void onViewChanged(HasViews hasViews) {
        textView = ((TextView) hasViews.findViewById(id.text));
        if (textView!= null) {
            textView.setOnClickListener(new OnClickListener() {


                @Override
                public void onClick(View view) {
                    MainActivity_.this.buttonClick();
                }

            }
            );
        }
        init();
    }

    public static class IntentBuilder_
        extends ActivityIntentBuilder<MainActivity_.IntentBuilder_>
    {

        private Fragment fragmentSupport_;

        public IntentBuilder_(Context context) {
            super(context, MainActivity_.class);
        }

        public IntentBuilder_(Fragment fragment) {
            super(fragment.getActivity(), MainActivity_.class);
            fragmentSupport_ = fragment;
        }

        @Override
        public void startForResult(int requestCode) {
            if (fragmentSupport_!= null) {
                fragmentSupport_.startActivityForResult(intent, requestCode);
            } else {
                if (context instanceof Activity) {
                    Activity activity = ((Activity) context);
                    ActivityCompat.startActivityForResult(activity, intent, requestCode, lastOptions);
                } else {
                    context.startActivity(intent);
                }
            }
        }

    }

}

ButterKnife

再說說ButterKnife吧燃异,其實(shí)原理和Android Annotations差不多,只是一些寫法和細(xì)節(jié)上有略微差別继蜡,最主要一點(diǎn)是沒有Android Annotations的坑回俐,Android Studio上有個(gè)ButterKnife的插件逛腿,這個(gè)插件提供的炫酷技能幾乎是一鍵搞定findViewById和setOnClickListener,基于此ButterKnife被很多開發(fā)者所推崇仅颇,我們還是簡(jiǎn)單對(duì)比下代碼吧单默。

常規(guī)寫法

public class MainActivity extends Activity {
    TextView textView;
    ListView listView;
    ListViewAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.text);
        listView = (ListView) findViewById(R.id.ListView);
        textView.setText("test");
        adapter = new ListViewAdapter(MainActivity.this);
        listView.setAdapter(adapter);
        textView.setOnClickListener(new OnClickListener() {
               
                @Override
                public void onClick(View v) {
                 text.setText("你點(diǎn)擊了按鈕");
          }
    });
}
       
    class ListViewAdapter extends BaseAdapter {

        private Context mContext;

        public ListViewAdapter(Context context) {
            mContext = context;
        }

        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return 1000;
        }

        @Override
        public Object getItem(int position) {
            // TODO Auto-generated method stub
            return position;
        }

        @Override
        public long getItemId(int position) {
            // TODO Auto-generated method stub
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            if (convertView == null) {
                holder = new ViewHolder();
                LayoutInflater layoutInflater = ((Activity) mContext)
                        .getLayoutInflater();
                convertView = layoutInflater.inflate(R.layout.list_item,
                        parent, false);
                holder.imageview = (ImageView) convertView
                        .findViewById(R.id.headshow);
                holder.textview0 = (TextView) convertView
                        .findViewById(R.id.name);
                holder.textview1 = (TextView) convertView
                        .findViewById(R.id.text);
                convertView.setTag(convertView);
            } else {
                convertView = (View) convertView.getTag();
            }
            holder.textview0.setText("star");
            holder.textview1.setText("test");
            return convertView;
        }

    }

    static class ViewHolder {
        ImageView imageview;
        TextView textview0;
        TextView textview1;
    }
}

ButterKnife寫法

public class MainActivity extends Activity {

    @InjectView(R.id.text)
    TextView text;

    @InjectView(R.id.ListView)
    ListView listView;

    ListViewAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.inject(this);
        adapter = new ListViewAdapter(MainActivity.this);
        listView.setAdapter(adapter);
        text.setText("ButterKnife test");
    }

    @OnClick(R.id.text)
    void onClick() {
        text.setText("你點(diǎn)擊了按鈕");
    }

    class ListViewAdapter extends BaseAdapter {
        private Context mContext;

        public ListViewAdapter(Context context) {
            mContext = context;
        }

        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return 1000;
        }

        @Override
        public Object getItem(int position) {
            // TODO Auto-generated method stub
            return position;
        }

        @Override
        public long getItemId(int position) {
            // TODO Auto-generated method stub
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            if (convertView == null) {
                LayoutInflater layoutInflater = ((Activity) mContext)
                        .getLayoutInflater();
                convertView = layoutInflater.inflate(
                        R.layout.list_item, parent, false);
                holder = new ViewHolder(convertView);
                convertView.setTag(convertView);
            } else {
                convertView = (View) convertView.getTag();
            }
            holder.textview0.setText("butterknife");
            holder.textview1.setText("test");
            return convertView;
        }

    }

    static class ViewHolder {
        @InjectView(R.id.headshow)
        ImageView imageview;
        @InjectView(R.id.name)
        TextView textview0;
        @InjectView(R.id.text)
        TextView textview1;
        public ViewHolder(View view) {
            ButterKnife.inject(this, view);
        }
    }
}

整體寫法與Android Annotations類似所灸,實(shí)現(xiàn)原理上ButterKnife的實(shí)現(xiàn)也是在編譯的過程中生成了另外一個(gè)類來(lái)幫我們完成一些基本操作宇姚,以上面的代碼為例擂红,生成了一個(gè)名為MainActivity$$ViewInjector的類朽缎,這里我就不再貼代碼了敛摘。但與Android Annotations所不同的是牢硅,我們?cè)诖a中操作的還是MainActivity瓢宦,而并不是MainActivity$$ViewInjector造烁。

ButterKnife其他語(yǔ)法列舉
資源注入
class ExampleActivity extends Activity {
  @BindString(R.string.title) String title;
  @BindDrawable(R.drawable.graphic) Drawable graphic;
  @BindColor(R.color.red) int red; 
  @BindDimen(R.dimen.spacer) Float spacer; 
  // ...
}
Fragment注入
public class FancyFragment extends Fragment {
  @InjectView(R.id.button1) Button button1;
  @InjectView(R.id.button2) Button button2;
 
  @Override View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fancy_fragment, container, false);
    ButterKnife.inject(this, view);
    // TODO Use "injected" views...
    return view;
  }
}
回調(diào)函數(shù)注入
// 帶有 Button 參數(shù)
@OnClick(R.id.submit)
public void sayHi(Button button) {
  button.setText("Hello!");
}
 
// 不帶參數(shù)
@OnClick(R.id.submit)
public void submit() {
  // TODO submit data to server...
}
 
// 同時(shí)注入多個(gè) View 事件
@OnClick({ R.id.door1, R.id.door2, R.id.door3 })
public void pickDoor(DoorView door) {
  if (door.hasPrizeBehind()) {
    Toast.makeText(this, "You win!", LENGTH_SHORT).show();
  } else {
    Toast.makeText(this, "Try again", LENGTH_SHORT).show();
  }
}

性能如何

關(guān)于IOC框架的基本寫法和實(shí)現(xiàn)原理明场,通過上面兩個(gè)例子汽摹,相信大家都已經(jīng)有所了解。但是前面已經(jīng)說了IOC的注解機(jī)制是依賴JAVA的反射苦锨,可能很多開發(fā)者都會(huì)嗤之以鼻:反射會(huì)影響性能逼泣。在早期的JAVA語(yǔ)言中反射是會(huì)帶來(lái)不小的性能消耗,而隨著語(yǔ)言自身的進(jìn)步和完善舟舒,到了現(xiàn)在情況有所好轉(zhuǎn)拉庶。但我們移動(dòng)設(shè)備的性能,不比后臺(tái)服務(wù)器擁有充足的內(nèi)存和運(yùn)算能力秃励。當(dāng)大量的使用注解的時(shí)候氏仗,會(huì)不會(huì)對(duì)APP造成什么不良的影響,會(huì)不會(huì)影響到APP的執(zhí)行性能夺鲜?在這里先明確的聲明皆尔,上述框架不會(huì)給APP帶來(lái)任何副作用,相反它強(qiáng)大易用的api能為你帶來(lái)前所未有的編程體驗(yàn)币励。

上述框架的實(shí)現(xiàn)慷蠕,都是通過使用jdk 1.6引入的Java Annotation Processing Tool,在編譯器中加了一層額外的自動(dòng)編譯步驟食呻,用來(lái)生成基于你源碼的代碼流炕。運(yùn)行期運(yùn)行的其實(shí)就是這個(gè)二次編譯的代碼,實(shí)際上反射注解這一過程是在編譯期完成的搁进,而并不會(huì)影響Runtime浪感。所以上述IOC框架并不會(huì)影響到APP得性能。簡(jiǎn)單的做了幾組測(cè)試饼问,分別用Android Annotations,ButterKnife揭斧,以及常規(guī)寫法寫了同樣實(shí)現(xiàn)的代碼莱革,然后打印代碼執(zhí)行時(shí)間峻堰。每組大概跑200次,然后計(jì)算代碼執(zhí)行的平均耗時(shí)盅视,發(fā)現(xiàn)三組數(shù)據(jù)基本是在同一個(gè)水平上捐名,并不能判斷孰優(yōu)孰劣。

IDE集成方法

不管是Eclipse還是Android Studio都可以很方便的集成上述框架闹击,而且資源包很小镶蹋,具體方法大家網(wǎng)上找一找,我就不再列出具體步驟了赏半。從網(wǎng)上偷了張ButterKnife的插件技能圖贺归,大家隨便感受下。

20140122235713140.gif

既然此類框架如此炫酷高效断箫,那么我們是否應(yīng)該大肆推崇拂酣,廣泛采用呢。這個(gè)具體還是根據(jù)自己的項(xiàng)目實(shí)際情況來(lái)定吧仲义,據(jù)我小范圍了解婶熬,目前部分大廠出品的部分應(yīng)用并未采用此類框架,最后我們還是來(lái)看看此類框架的優(yōu)缺點(diǎn)吧埃撵。

優(yōu)點(diǎn)

1:提高開發(fā)效率
2:減少代碼量

缺點(diǎn)

1:代碼可讀性差
2:增加新人學(xué)習(xí)成本
3:加速觸及65535方法數(shù)問

你用或者不用赵颅,框架就在那里,不悲不喜暂刘!
(如有刊物饺谬,歡迎指正)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鸳惯,隨后出現(xiàn)的幾起案子商蕴,更是在濱河造成了極大的恐慌,老刑警劉巖芝发,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绪商,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡辅鲸,警方通過查閱死者的電腦和手機(jī)格郁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)独悴,“玉大人例书,你說我怎么就攤上這事】坛矗” “怎么了决采?”我有些...
    開封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)坟奥。 經(jīng)常有香客問我树瞭,道長(zhǎng)拇厢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任晒喷,我火速辦了婚禮孝偎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘凉敲。我一直安慰自己衣盾,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開白布爷抓。 她就那樣靜靜地躺著势决,像睡著了一般。 火紅的嫁衣襯著肌膚如雪废赞。 梳的紋絲不亂的頭發(fā)上徽龟,一...
    開封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音唉地,去河邊找鬼据悔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛耘沼,可吹牛的內(nèi)容都是我干的极颓。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼群嗤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼菠隆!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起狂秘,我...
    開封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤骇径,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后者春,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體破衔,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年钱烟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了晰筛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拴袭,死狀恐怖读第,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拥刻,我是刑警寧澤怜瞒,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站般哼,受9級(jí)特大地震影響盼砍,放射性物質(zhì)發(fā)生泄漏尘吗。R本人自食惡果不足惜逝她,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一浇坐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧黔宛,春花似錦近刘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至徽惋,卻和暖如春案淋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背险绘。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工踢京, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宦棺。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓瓣距,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親代咸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蹈丸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容