AOP

13.3.1 AOP

1. 簡(jiǎn)介

OOP(Object Oriented Programming)面向?qū)ο缶幊唐┲亍T贠OP的世界中,問(wèn)題或者功能都被劃分到一個(gè)一個(gè)的模塊里邊卿操。每個(gè)模塊專(zhuān)心干自己的事情扁达,模塊之間通過(guò)設(shè)計(jì)好的接口交互。如下圖就是Android Framework中的模塊:

Android Framework中的模塊

OOP的精髓是把++功能或問(wèn)題模塊化碴倾,每個(gè)模塊處理自己的家務(wù)事++。但在現(xiàn)實(shí)世界中掉丽,并不是所有問(wèn)題都能完美得劃分到模塊中跌榔。舉個(gè)最簡(jiǎn)單而又常見(jiàn)的例子:現(xiàn)在想為每個(gè)模塊加上日志功能,要求模塊運(yùn)行時(shí)候能輸出日志捶障。在不知道AOP的情況下僧须,一般的處理都是:先設(shè)計(jì)一個(gè)日志輸出模塊,這個(gè)模塊提供日志輸出API项炼,比如Android中的Log類(lèi)担平。然后,其他模塊需要輸出日志的時(shí)候調(diào)用Log類(lèi)的幾個(gè)方法锭部。這種方式功能是得到了滿足暂论,但是好像沒(méi)有Oriented的感覺(jué)了。是的拌禾,隨意加日志輸出功能取胎,使得其他模塊的代碼和日志模塊耦合非常緊密。而且蹋砚,將來(lái)要是日志模塊修改了API扼菠,則使用它們的地方都得改。

AOP(Aspect Oriented Programming)面向切向編程坝咐。AOP的目標(biāo)==是把這些分散在各個(gè)模塊中的功能集中起來(lái)循榆,放到一個(gè)統(tǒng)一的地方來(lái)控制和管理==。如果說(shuō)墨坚,OOP如果是把問(wèn)題劃分到單個(gè)模塊的話秧饮,那么AOP就是把涉及到眾多模塊的某一類(lèi)問(wèn)題進(jìn)行統(tǒng)一管理映挂。比如我們可以設(shè)計(jì)兩個(gè)Aspects,一個(gè)是管理某個(gè)軟件中所有模塊的日志輸出的功能盗尸,另外一個(gè)是管理該軟件中一些特殊函數(shù)調(diào)用的權(quán)限檢查柑船。

2. AspectJ

AOP是一種思想,就好像OOP中的Java一樣泼各,一些先行者也開(kāi)發(fā)了一套語(yǔ)言來(lái)支持AOP鞍时。目前用得比較火的就是AspectJ了,它是一種幾乎和Java完全一樣的==語(yǔ)言==扣蜻,而且完全兼容Java逆巍。當(dāng)然,除了使用AspectJ特殊的語(yǔ)言外莽使,AspectJ還支持原生的Java锐极,只要加上對(duì)應(yīng)的AspectJ注解就好。所以芳肌,使用AspectJ有兩種方法:

  • 完全使用AspectJ的語(yǔ)言灵再。這語(yǔ)言一點(diǎn)也不難,和Java幾乎一樣亿笤,也能在AspectJ中調(diào)用Java的任何類(lèi)庫(kù)翎迁。AspectJ只是多了一些關(guān)鍵詞罷了。
  • 或者使用純Java語(yǔ)言開(kāi)發(fā)责嚷,然后使用AspectJ注解鸳兽,簡(jiǎn)稱(chēng)@AspectJ。(推薦)

不論哪種方法罕拂,最后都需要AspectJ的編譯工具ajc來(lái)編譯。由于AspectJ實(shí)際上脫胎于Java全陨,所以ajc工具也能編譯java源碼爆班。接下來(lái)介紹下幾個(gè)概念。

(1) Join Points

Join Points(以后簡(jiǎn)稱(chēng)==JPoints==)是AspectJ中最關(guān)鍵的一個(gè)概念辱姨,JPoints就是==程序運(yùn)行時(shí)的一些執(zhí)行點(diǎn)==柿菩。那么,一個(gè)程序中雨涛,哪些執(zhí)行點(diǎn)是JPoints呢枢舶?比如:

  • 一個(gè)函數(shù)的調(diào)用可以是一個(gè)JPoint。比如Log.e()這個(gè)函數(shù)替久。e的執(zhí)行可以是一個(gè)JPoint凉泄,而調(diào)用e的函數(shù)也可以認(rèn)為是一個(gè)JPoint。
  • 設(shè)置一個(gè)變量蚯根,或者讀取一個(gè)變量后众,也可以是一個(gè)JPoint。比如Demo類(lèi)中有一個(gè)disable的boolean變量。設(shè)置它的地方或者讀取它的地方都可以看做是JPoints蒂誉。
  • for循環(huán)可以看做是JPoint教藻。

理論上說(shuō),一個(gè)程序中很多地方都可以被看做是JPoint右锨,但是AspectJ中括堤,只有如表1所示的幾種執(zhí)行點(diǎn)被認(rèn)為是JPoints:

Join Points 說(shuō)明 示例
method call 函數(shù)調(diào)用 比如調(diào)用Log.e(),這是一處JPoint
method execution 函數(shù)執(zhí)行 比如Log.e()的執(zhí)行內(nèi)部绍移,是一處JPoint
constructor call 構(gòu)造函數(shù)調(diào)用 和method call類(lèi)似
constructor execution 構(gòu)造函數(shù)執(zhí)行 和method execution類(lèi)似
field get 獲取變量 比如讀取DemoActivity.debug成員
field set 設(shè)置變量 比如設(shè)置DemoActivity.debug成員
static initialization 類(lèi)初始化 比如類(lèi)的static{}
handler 異常處理 比如try catch(xxx)中悄窃,對(duì)應(yīng)catch內(nèi)的執(zhí)行
(2) Pointcuts

一個(gè)程序會(huì)有很多的JPoints,即使是同一個(gè)函數(shù)(比如testMethod這個(gè)函數(shù))登夫,還分為==call類(lèi)型和execution類(lèi)型==的JPoint广匙。顯然,不是所有的JPoint恼策,也不是所有類(lèi)型的JPoint都是我們關(guān)注的鸦致。比如我們只要求在Activity的幾個(gè)生命周期函數(shù)中打印日志,只有這幾個(gè)生命周期函數(shù)才是我們業(yè)務(wù)需要的JPoint涣楷,而其他的什么JPoint我不需要關(guān)注分唾。

怎么從一堆一堆的JPoints中選擇自己想要的JPoints呢?恩狮斗,這就是Pointcuts的功能绽乔。一句話,==Pointcuts的目標(biāo)是提供一種方法使得開(kāi)發(fā)者能夠選擇自己感興趣的JoinPoints==碳褒。

(i) 一個(gè)Pointcuts例子

@Pointcut("call(public * *.println(..)) && !within(TestAspect)")

  • call(public * *.println(..))是一種選擇條件折砸。call表示我們選擇的Joinpoint類(lèi)型為call類(lèi)型。
  • public * *.println(..):這小行代碼使用了通配符沙峻。由于我們這里選擇的JoinPoint類(lèi)型為call類(lèi)型睦授,它對(duì)應(yīng)的目標(biāo)JPoint一定是某個(gè)函數(shù)。所以我們要找到這個(gè)/些函數(shù)摔寨。public 表示目標(biāo)JPoint的訪問(wèn)類(lèi)型(public/private/protect)去枷。第一個(gè)*表示返回值的類(lèi)型是任意類(lèi)型。第二個(gè)*用來(lái)指明包名是复。此處不限定包名删顶。緊接其后的println是函數(shù)名。這表明我們選擇的函數(shù)是任何包中定義的名字叫println的函數(shù)淑廊。當(dāng)然逗余,唯一確定一個(gè)函數(shù)除了包名外,還有它的參數(shù)蒋纬。在(..)中猎荠,就指明了目標(biāo)函數(shù)的參數(shù)應(yīng)該是什么樣子的坚弱。比如這里使用了通配符..,代表任意個(gè)數(shù)的參數(shù)关摇,任意類(lèi)型的參數(shù)荒叶。
  • call后面的&&:AspectJ可以把幾個(gè)條件組合起來(lái),目前支持 &&输虱,||些楣,以及!這三個(gè)條件宪睹。
  • !within(TestAspectJ):前面的!表示不滿足某個(gè)條件愁茁。within是另外一種類(lèi)型選擇方法,特別注意亭病,這種類(lèi)型和前面講到的joinpoint的那幾種類(lèi)型不同鹅很。within的類(lèi)型是數(shù)據(jù)類(lèi)型,而joinpoint的類(lèi)型更像是動(dòng)態(tài)的罪帖,執(zhí)行時(shí)的類(lèi)型促煮。

上例中的pointcut合起來(lái)就是:

  1. 選擇那些調(diào)用println(而且不考慮println函數(shù)的參數(shù)是什么)的Joinpoint。
  2. 調(diào)用者的類(lèi)型不要是TestAspect的整袁。
(ii) 直接針對(duì)JoinPoint的選擇

pointcuts中最常用的選擇條件和Joinpoint的類(lèi)型密切相關(guān):

不同類(lèi)型的JPoint對(duì)應(yīng)的pointcuts查詢(xún)方法

一個(gè)==Method Signature==的完整表達(dá)式為:@注解 訪問(wèn)權(quán)限 返回值類(lèi)型 包名.函數(shù)名(參數(shù))

  • @注解和訪問(wèn)權(quán)限(public/private/protect菠齿,以及static/final)屬于可選項(xiàng)。如果不設(shè)置它們那么public坐昙,private绳匀,protect及static、final的函數(shù)都會(huì)進(jìn)行搜索炸客。
  • 返回值類(lèi)型就是普通的函數(shù)的返回值類(lèi)型疾棵。如果不限定類(lèi)型的話,就用*通配符表示
  • 包名.函數(shù)名用于查找匹配的函數(shù)痹仙÷穑可以使用通配符,包括*和..以及+號(hào)蝶溶。其中*號(hào)用于匹配除.號(hào)之外的任意字符,而..則表示任意子package宣渗,+號(hào)表示子類(lèi)抖所。

java.*.Date:可以表示java.sql.Date,也可以表示java.util.Date
Test*:可以表示TestBase痕囱,也可以表示TestDervied

java..*:表示java任意子類(lèi)
java..*Model+:表示Java任意package中名字以Model結(jié)尾的子類(lèi)田轧,比如TabelModel,TreeModel等

  • 最后來(lái)看函數(shù)的參數(shù)鞍恢。參數(shù)匹配比較簡(jiǎn)單傻粘,主要是參數(shù)類(lèi)型每窖。

(int, char):表示參數(shù)只有兩個(gè),并且第一個(gè)參數(shù)類(lèi)型是int弦悉,第二個(gè)參數(shù)類(lèi)型是char
(String, ..):表示至少有一個(gè)參數(shù)窒典。并且第一個(gè)參數(shù)類(lèi)型是String,后面參數(shù)類(lèi)型不限稽莉。在參數(shù)匹配中瀑志,
..代表任意參數(shù)個(gè)數(shù)和類(lèi)型
(Object ...):表示不定個(gè)數(shù)的參數(shù),且類(lèi)型都是Object污秆,這里的...不是通配符劈猪,而是Java中代表不定參數(shù)的意思

(iii) 間接針對(duì)JPoint的選擇

除了根據(jù)前面提到的Signature信息來(lái)匹配JPoint外,AspectJ還提供其他一些選擇方法來(lái)選擇JPoint良拼。比如某個(gè)類(lèi)中的所有JPoint战得,每一個(gè)函數(shù)執(zhí)行流程中所包含的JPoint。

下表列出了一些常用的非JPoint選擇方法:

關(guān)鍵詞 說(shuō)明 示例
within(TypePattern) TypePattern標(biāo)示package或者類(lèi)庸推,可以使用通配符 表示某個(gè)Package或者類(lèi)中的所有JPoint常侦。比如within(Test):Test類(lèi)中(包括內(nèi)部類(lèi))所有JPoint。圖2所示的例子就是用這個(gè)方法予弧。
this(Type) JPoint的this對(duì)象是Type類(lèi)型刮吧。(其實(shí)就是判斷Type是不是某種類(lèi)型,即是否滿足instanceof Type的條件) JPoint是代碼段(不論是函數(shù)掖蛤,異常處理杀捻,static block),從語(yǔ)法上說(shuō)蚓庭,它都屬于一個(gè)類(lèi)致讥。如果這個(gè)類(lèi)的類(lèi)型是Type標(biāo)示的類(lèi)型,則和它相關(guān)的JPoint將全部被選中器赞。圖2示例的testMethod是TestDerived類(lèi)垢袱。所以this(TestDerived)將會(huì)選中這個(gè)testMethod JPoint
target(Type) JPoint的target對(duì)象是Type類(lèi)型 和this相對(duì)的是target。不過(guò)target一般用在call的情況港柜。call一個(gè)函數(shù)请契,這個(gè)函數(shù)可能定義在其他類(lèi)。比如testMethod是TestDerived類(lèi)定義的夏醉。那么target(TestDerived)就會(huì)搜索到調(diào)用testMethod的地方爽锥。但是不包括testMethod的execution JPoint
args(TypeSignature) 用來(lái)對(duì)JPoint的參數(shù)進(jìn)行條件搜索的 比如args(int,..),表示第一個(gè)參數(shù)是int畔柔,后面參數(shù)個(gè)數(shù)和類(lèi)型不限的JPoint氯夷。
(3) advice

現(xiàn)在,我們知道如何通過(guò)pointcuts來(lái)選擇合適的JPoint靶擦。那么腮考,下一步工作就是選擇這些JPoint后雇毫,需要干一些事情的。比如前面例子中的輸出都有before踩蔚,after之類(lèi)的棚放。這其實(shí)JPoint在執(zhí)行前,執(zhí)行后寂纪,都執(zhí)行了一些我們?cè)O(shè)置的代碼席吴。在AspectJ中,這段代碼叫advice捞蛋。簡(jiǎn)單點(diǎn)說(shuō)孝冒,advice就是一種Hook。

關(guān)鍵詞 說(shuō)明
before() 表示在JPoint執(zhí)行之前拟杉,需要干的事情
after() 表示JPoint自己執(zhí)行完了后庄涡,需要干的事情
返回值類(lèi)型 around() ==替代了原JPoint==,如果要執(zhí)行原JPoint的話搬设,需要調(diào)用proceed
(4) 例子
(1) 示例一
@Aspect   //必須使用@AspectJ標(biāo)注
public class DemoAspect {  
    static final String TAG = "DemoAspect";  
/* 
@Pointcut:定義一個(gè)pointcut穴店,這個(gè)注解是針對(duì)一個(gè)函數(shù)的,比如此處的logForActivity() 
其實(shí)它代表了這個(gè)pointcut的名字拿穴。如果是帶參數(shù)的pointcut泣洞,則把參數(shù)類(lèi)型和名字放到 
代表pointcut名字的logForActivity中,然后在@Pointcut注解中使用參數(shù)名默色。 
*/  
@Pointcut("execution(* com.androidaop.demo.AopDemoActivity.onCreate(..)) ||"  
        +"execution(* com.androidaop.demo.AopDemoActivity.onStart(..))")  
public void logForActivity(){};  //注意球凰,這個(gè)函數(shù)必須要有實(shí)現(xiàn),否則Java編譯器會(huì)報(bào)錯(cuò)  
   
/* 
@Before:這就是Before的advice腿宰。Before后面跟的是pointcut名字呕诉,然后其代碼塊由一個(gè)函數(shù)來(lái)實(shí)現(xiàn)。比如此處的log吃度。 
*/  
@Before("logForActivity()")  
public void log(JoinPoint joinPoint){  
    //對(duì)于使用Annotation的AspectJ而言甩挫,JoinPoint就不能直接在代碼里得到多了,而需要通過(guò)  
    //參數(shù)傳遞進(jìn)來(lái)椿每。  
    Log.e(TAG, joinPoint.toShortString());  
    }  
}  
(2) 示例二
//第一個(gè)@Target表示這個(gè)注解只能給函數(shù)使用  
//第二個(gè)@Retention表示注解內(nèi)容需要包含的Class字節(jié)碼里伊者,屬于運(yùn)行時(shí)需要的。  
@Target(ElementType.METHOD)  
@Retention(RetentionPolicy.RUNTIME)  
public @interface SecurityCheckAnnotation {//@interface用于定義一個(gè)注解间护。  
    publicString declaredPermission();  //declarePermssion是一個(gè)函數(shù)删壮,其實(shí)代表了注解里的參數(shù)  
}  

怎么使用注解呢?接著看代碼:

//為checkPhoneState使用SecurityCheckAnnotation注解兑牡,并指明調(diào)用該函數(shù)的人需要聲明的權(quán)限  
@SecurityCheckAnnotation(declaredPermission="android.permission.READ_PHONE_STATE")  
private void checkPhoneState(){  
    //如果不使用AOP,就得自己來(lái)檢查權(quán)限  
    if(checkPermission("android.permission.READ_PHONE_STATE") ==false){  
        Log.e(TAG,"have no permission to read phone state");  
        return;  
    }  
    Log.e(TAG,"Read Phone State succeed");  
    return;  
}  

我們來(lái)看看如何在AspectJ中税灌,充分利用這注解信息來(lái)幫助我們檢查權(quán)限均函。

/* 
來(lái)看這個(gè)Pointcut亿虽,首先,它在選擇Jpoint的時(shí)候苞也,把@SecurityCheckAnnotation使用上了洛勉,
這表明所有那些public的,并且攜帶有這個(gè)注解的API都是目標(biāo)JPoint如迟。如果是帶參數(shù)的pointcut收毫,
則把參數(shù)類(lèi)型和名字放到代表pointcut名字的checkPermssion中,然后在@Pointcut注解中使用參數(shù)名殷勘。
接著此再,由于我們希望在函數(shù)中獲取注解的信息,所以這里的poincut函數(shù)有一個(gè)參數(shù)玲销,參數(shù)類(lèi)型是 
SecurityCheckAnnotation输拇,參數(shù)名為ann。
這個(gè)參數(shù)我們需要在后面的advice里用上贤斜,所以pointcut還使用了@annotation(ann)這種方法來(lái)告訴 
AspectJ,這個(gè)ann是一個(gè)注解 
*/ 
@Pointcut("execution(@SecurityCheckAnnotation public * *..*.*(..)) && @annotation(ann)")  
public void checkPermssion(SecurityCheckAnnotation ann){};  
   
/* 
接下來(lái)是advice,advice的真正功能由check函數(shù)來(lái)實(shí)現(xiàn)狈蚤,這個(gè)check函數(shù)第二個(gè)參數(shù)就是我們想要 
的注解贬堵。在實(shí)際運(yùn)行過(guò)程中,AspectJ會(huì)把這個(gè)信息從JPoint中提出出來(lái)并傳遞給check函數(shù)锁荔。 
*/  
@Before("checkPermssion(securityCheckAnnotation)")  
public void check(JoinPoint joinPoint,SecurityCheckAnnotation securityCheckAnnotation){  
    //從注解信息中獲取聲明的權(quán)限蟀给。  
    String neededPermission = securityCheckAnnotation.declaredPermission();  
    Log.e(TAG, joinPoint.toShortString());  
    Log.e(TAG, "\t needed permission is " + neededPermission);  
    return;  
}  
(3) 示例三
@Aspect
public class FragmentAspectj {
    private final static String TAG = FragmentAspectj.class.getCanonicalName();

    @Around("execution(* android.support.v4.app.Fragment.onCreateView(..))")
    public Object fragmentOnCreateViewMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        return trackFragmentView(joinPoint);
    }

    @Around("execution(* android.app.Fragment.onCreateView(..))")
    public Object fragmentOnCreateViewMethod2(ProceedingJoinPoint joinPoint) throws Throwable {
        return trackFragmentView(joinPoint);
    }

    private Object trackFragmentView(final ProceedingJoinPoint joinPoint) throws Throwable {
        // 被注解的方法在這一行代碼被執(zhí)行
        Object result = joinPoint.proceed();
        AopUtil.sendTrackEventToSDK3(joinPoint, "trackFragmentView", result);
        return result;
    }

    @After("execution(* android.support.v4.app.Fragment.onHiddenChanged(boolean))")
    public void onHiddenChangedMethod(JoinPoint joinPoint) throws Throwable {
        AopUtil.sendTrackEventToSDK(joinPoint, "onFragmentHiddenChangedMethod");
    }

    @After("execution(* android.support.v4.app.Fragment.setUserVisibleHint(boolean))")
    public void setUserVisibleHintMethod(JoinPoint joinPoint) throws Throwable {
        AopUtil.sendTrackEventToSDK(joinPoint, "onFragmentSetUserVisibleHintMethod");
    }

    @After("execution(* android.support.v4.app.Fragment.onResume())")
    public void onResumeMethod(JoinPoint joinPoint) throws Throwable {
        AopUtil.sendTrackEventToSDK(joinPoint, "onFragmentOnResumeMethod");
    }
}

參考文獻(xiàn)

深入理解Android之AOP

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市堕战,隨后出現(xiàn)的幾起案子坤溃,更是在濱河造成了極大的恐慌,老刑警劉巖嘱丢,帶你破解...
    沈念sama閱讀 206,013評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件薪介,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡越驻,警方通過(guò)查閱死者的電腦和手機(jī)汁政,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)缀旁,“玉大人记劈,你說(shuō)我怎么就攤上這事〔⑽。” “怎么了目木?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,370評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)懊渡。 經(jīng)常有香客問(wèn)我刽射,道長(zhǎng)军拟,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,168評(píng)論 1 278
  • 正文 為了忘掉前任誓禁,我火速辦了婚禮懈息,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘摹恰。我一直安慰自己辫继,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,153評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布俗慈。 她就那樣靜靜地躺著姑宽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪姜盈。 梳的紋絲不亂的頭發(fā)上低千,一...
    開(kāi)封第一講書(shū)人閱讀 48,954評(píng)論 1 283
  • 那天,我揣著相機(jī)與錄音馏颂,去河邊找鬼示血。 笑死,一個(gè)胖子當(dāng)著我的面吹牛救拉,可吹牛的內(nèi)容都是我干的难审。 我是一名探鬼主播,決...
    沈念sama閱讀 38,271評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼亿絮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼告喊!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起派昧,我...
    開(kāi)封第一講書(shū)人閱讀 36,916評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤黔姜,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后蒂萎,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體秆吵,經(jīng)...
    沈念sama閱讀 43,382評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,877評(píng)論 2 323
  • 正文 我和宋清朗相戀三年五慈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纳寂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,989評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泻拦,死狀恐怖毙芜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情争拐,我是刑警寧澤腋粥,帶...
    沈念sama閱讀 33,624評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響灯抛,放射性物質(zhì)發(fā)生泄漏金赦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,209評(píng)論 3 307
  • 文/蒙蒙 一对嚼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧绳慎,春花似錦纵竖、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,199評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至珊楼,卻和暖如春通殃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背厕宗。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,418評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工画舌, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人已慢。 一個(gè)月前我還...
    沈念sama閱讀 45,401評(píng)論 2 352
  • 正文 我出身青樓曲聂,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親佑惠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子朋腋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,700評(píng)論 2 345

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

  • 里烈:2018-01-30 目標(biāo):女兒能夠?qū)W會(huì)學(xué)習(xí)旭咽,喜歡學(xué)習(xí)。順利通過(guò)國(guó)考 好種子: 1赌厅、懷著感恩穷绵,高興愉悅的心情...
    里喻棋閱讀 84評(píng)論 0 0
  • 從不知道自己不知道~知道自己不知道~不知道自己知道,是一個(gè)認(rèn)知的歷程察蹲。我們大多數(shù)人都在第一或第二階段请垛。 曾經(jīng)很苦惱...
    milk76閱讀 281評(píng)論 2 1
  • 如果有人要我推薦種植一種美麗的花在庭院、在陽(yáng)臺(tái)洽议、在花園小徑邊宗收。這種植物能開(kāi)出美麗的花、一年四季枝葉濃綠亚兄、不用怎么打...
    魚(yú)兒水中游閱讀 1,436評(píng)論 0 0