2. Android 3分鐘手寫B(tài)utterKnife 徹底搞懂 注解處理器 APT 和IOC

今天開(kāi)始架構(gòu)師之路寄月!分為6節(jié)課焚辅,以手寫retofit ,Butterknife,Arount,Dagger2,hilit,ASM ,AOP為主

****APT :處理提取和處理 Annotation 的代碼統(tǒng)稱為(Annotation Processing Tool)段只。

作用

使用APT的優(yōu)點(diǎn)就是方便、簡(jiǎn)單,可以少些很多重復(fù)的代碼。

用過(guò)ButterKnife侦香、DaggerEventBus等注解框架的同學(xué)就能感受到纽疟,利用這些框架可以少些很多代碼罐韩,只要寫一些注解就可以了。

編寫注解處理器

   和運(yùn)行時(shí)注解的解析不一樣污朽,編譯時(shí)注解的解析需要我們自己去實(shí)現(xiàn)一個(gè)注解處理器散吵。

注解處理器(Annotation Processor)是javac的一個(gè)工具,它用來(lái)在編譯時(shí)掃描和處理注解(Annotation)。一個(gè)注解的注解處理器矾睦,以Java代碼(或者編譯過(guò)的字節(jié)碼)作為輸入晦款,生成文件(通常是.java文件)作為輸出。而且這些生成的Java文件同咱們手動(dòng)編寫的Java源代碼一樣可以調(diào)用枚冗。(注意:不能修改已經(jīng)存在的java文件代碼)缓溅。

   注解處理器所做的工作,就是在代碼編譯的過(guò)程中赁温,找到我們指定的注解坛怪。然后讓我們更加自己特定的邏輯做出相應(yīng)的處理(通常是生成JAVA文件)。

   注解處理器的寫法有固定套路的股囊,兩步:

注冊(cè)注解處理器(這個(gè)注解器就是我們第二步自定義的類)袜匿。

自定義注解處理器類繼承AbstractProcessor。

如何看:java里面的jdk稚疹。會(huì)在resources的里面有META-INF居灯。里面會(huì)有注解處理器的文件!javax.annotation.

所以apt用的是java的lib内狗,不是Android 怪嫌!

注解處理器會(huì)在路徑Build/classes/java/main/com/META-INF/Services/javax_anontation_process.

APT本質(zhì):生成java類

APT(Annotation Processing Tool)即注解處理器,是一種處理注解的工具其屏,確切的說(shuō)它是javac的一個(gè)工具喇勋,它用來(lái)在編譯時(shí)掃描和處理注解。注解處理器以Java代碼(或者編譯過(guò)的字節(jié)碼)作為輸入偎行,生成.java文件作為輸出川背。

簡(jiǎn)單來(lái)說(shuō)就是在編譯期,通過(guò)注解生成.java文件蛤袒。

詳細(xì)解釋: 它對(duì)源代碼文件進(jìn)行檢測(cè)找出其中的Annotation熄云,使用Annotation進(jìn)行額外的處理。 Annotation處理器在處理Annotation時(shí)可以根據(jù)源文件中的Annotation生成額外的源文件和其它的文件(文件具體內(nèi)容由Annotation處理器的編寫者決定)妙真,

APT還會(huì)編譯生成的源文件和原來(lái)的源文件缴允,將它們一起生成class文件。

在這里我們將在每一個(gè)業(yè)務(wù)組件的 build.gradle 都引入ActivityRouter 的 Annotation處理器珍德,我們將會(huì)在聲明組件和Url的時(shí)候使用练般,annotationProcessor是Android官方提供的Annotation處理器插件,代碼如下:

dependencies {

compile fileTree(dir: 'libs', include: ['*.jar'])

annotationProcessor "com.github.mzule.activityrouter:compiler:$rootProject.annotationProcessor"

}

[圖片上傳失敗...(image-c4ef24-1640265031541)]

JavaPoet

更好的方案:通過(guò)javapoet可以更加簡(jiǎn)單得生成這樣的Java代碼锈候。(后面會(huì)說(shuō)到)

JavaPoet是square推出的開(kāi)源java代碼生成框架薄料,提供Java Api生成.java源文件

需要添加JavaPoet的依賴

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: Consolas; font-size: 0.817rem;">implementation'com.squareup:javapoet:1.9.0'</pre>

javapoet的詳細(xì)內(nèi)容:

1.方法名

2.返回值

3.打印

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: Consolas; font-size: 0.817rem;">MethodSpec main = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addParameter(String[].class, "args")
.addStatement("T.out.println(S)", System.class, "Hello, JavaPoet!")
.build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build(); JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
.build(); javaFile.writeTo(System.out);</pre>

生成的代碼

上面的功能一直在完成一件事情,那就是生成Java代碼泵琳,那么生成的代碼在哪摄职?
app/build/generated/source/apt中可以找到生成的Java文件

比如:MainActivity$$ARounter

autoService:主要作用:注冊(cè)誊役,****相當(dāng)于安卓的四大組件需要注冊(cè)一樣!

google auto 中的autoservice 可以幫助我們生成對(duì)應(yīng)的配置:使用AutoService注解谷市,可以自動(dòng)生成meta信息

spi 是一種服務(wù)發(fā)現(xiàn)的標(biāo)準(zhǔn)蛔垢,對(duì)于開(kāi)發(fā)中我們通常需要編寫 META-INF/services 文件夾中定義的類。

自定義注解處理器注冊(cè)才能被Java虛擬機(jī)調(diào)用迫悠,在上面的博客第四小節(jié)中用的方法是手動(dòng)注冊(cè)鹏漆,這比較違反程序員懶的特點(diǎn),在里面也提到了自動(dòng)注冊(cè)的方法及皂,就是AutoService

介紹下依賴庫(kù)auto-service
在使用注解處理器需要先聲明甫男,步驟:
1、需要在 processors 庫(kù)的 main 目錄下新建 resources 資源文件夾验烧;
2、在 resources文件夾下建立 META-INF/services 目錄文件夾又跛;
3碍拆、在 META-INF/services 目錄文件夾下創(chuàng)建 javax.annotation.processing.Processor 文件;
4慨蓝、在 javax.annotation.processing.Processor 文件寫入注解處理器的全稱感混,包括包路徑;)
這樣聲明下來(lái)也太麻煩了礼烈?這就是用引入auto-service的原因弧满。
通過(guò)auto-service中的@AutoService可以自動(dòng)生成AutoService注解處理器是Google開(kāi)發(fā)的,用來(lái)生成 META-INF/services/javax.annotation.processing.Processor 文件的

怎么讓jvm在編譯期間調(diào)用我們自己寫的這個(gè)注解處理器呢此熬?

有一個(gè)快捷辦法就是使用谷歌的開(kāi)源庫(kù)auto庭呜,然后使用它提供的AutoService注解來(lái)實(shí)現(xiàn),

另外一種辦法就是自己手動(dòng)去創(chuàng)建指定的文件夾犀忱,然后配置我們的注解處理器的路徑募谎。

注解處理器的debug調(diào)試

注解處理器的debug 跟普通的代碼debug 有點(diǎn)不同:

在當(dāng)前工程路徑下輸入命令

gradlew --no-daemon

-Dorg.gradle.debug=true :app:clean :app:compileDebugJavaWithJavac

并在Edit Configurations 中新添加一個(gè)遠(yuǎn)程配置(remote),名字隨意阴汇,端口為

5005数冬。然后點(diǎn)擊debug 按鈕,就可以連接上遠(yuǎn)程調(diào)試器進(jìn)行Annotation 的調(diào)試了搀庶。

demo地址:

https://github.com/jaminchanks/AnnotationProcess-Demo

參考博客:

http://www.reibang.com/p/7af58e8e3e18

http://www.reibang.com/p/6955a56844d7

點(diǎn)擊世界是否會(huì)用到動(dòng)態(tài)代理拐纱??哥倔?---另外一種方案秸架!

https://blog.csdn.net/u010008118/article/details/100896148

ButterKnife:黃油刀,被廢棄了∥窗撸現(xiàn)在已經(jīng)被viewBind和databing取代咕宿,依賴注入框架币绩。但是原理很重要!

問(wèn)題:他和Arounter結(jié)合的時(shí)候會(huì)又什么問(wèn)題府阀?

R2缆镣?為什么會(huì)產(chǎn)生這樣的問(wèn)題?

編****譯時(shí)注解實(shí)戰(zhàn): 手寫 ButterKnife

4個(gè)模塊

1.APP :使用

2.annoations:用于定義接口

3.annotation_complier:注解處理器试浙,用于自動(dòng)生成文件董瞻,注解處理器要放在java library里面

annotation_complier繼承:abstractProcessor

4. lib :通過(guò)反射調(diào)用

重寫幾個(gè)方法

然后我們還需要重寫AbstractProcessor

getSupportedAnnotationTypes() 方法

getSupportedSourceVersion() 方法

getSupportedAnnotationTypes() 方法用來(lái)指定該注解處理器是用來(lái)處理哪個(gè)注解的

getSupportedSourceVersion()方法用來(lái)指定java版本,一般給值為SourceVersion.latestSupported()田巴。

注意:如果必要的方法沒(méi)有寫钠糊,會(huì)導(dǎo)致不執(zhí)行主要的處理方法

里面重寫的方法可以用注解代替, evenbus源碼就是這么寫的

那我們開(kāi)始:創(chuàng)建項(xiàng)目,4步搞定

創(chuàng)建Android Module命名為app
創(chuàng)建Java library Module命名為 apt-annotation
創(chuàng)建Java library Module命名為 apt-processor 依賴 apt-annotation
創(chuàng)建Android library Module 命名為apt-library依賴 apt-annotation壹哺、auto-service

分析:

1.看下使用流程:1.有一個(gè)注解 2.有自動(dòng)生成的類可以自動(dòng)注入抄伍!

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;"> @BindView(R.id.tv_bangding)
TextView btn; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this); //自動(dòng)生成的類有一個(gè)bind方法,進(jìn)行動(dòng)態(tài)注入
}
}</pre>

手寫步驟:

1.新建java module管宵。把注解方進(jìn)來(lái)截珍。

為什么要把注解單獨(dú)放一個(gè)module?

因?yàn)橹鱩odule和編譯module都需要用!

那為什么不把注解放在自動(dòng)生成注解的module里面箩朴?

field注解+編譯時(shí)+java module

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int value(); }
</pre>

apt-annotation(自定義注解)

創(chuàng)建注解類BindView

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
    int value();
}

@Retention(RetentionPolicy.CLASS):表示編譯時(shí)注解
@Target(ElementType.FIELD):表示注解范圍為類成員(構(gòu)造方法岗喉、方法、成員變量)

@Retention: 定義被保留的時(shí)間長(zhǎng)短
RetentionPoicy.SOURCE炸庞、RetentionPoicy.CLASS钱床、RetentionPoicy.RUNTIME
@Target: 定義所修飾的對(duì)象范圍
TYPE、FIELD埠居、METHOD查牌、PARAMETER、CONSTRUCTOR拐格、LOCAL_VARIABLE等

這里定義了運(yùn)行時(shí)注解BindView僧免,其中value()用于獲取對(duì)應(yīng)Viewid

2.新建java module,把注解處理器需要的寫進(jìn)來(lái)捏浊,依賴javapoat和autoservice

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">implementation 'com.google.auto.service:auto-service:1.0-rc2' implementation 'com.squareup:javapoet:1.10.0' implementation project(':apt-annotation')</pre>

需要先模擬好生成的類懂衩,在主程序的build/generated/source/apt

[圖片上傳失敗...(image-fd1f62-1640265031545)]

在自動(dòng)生成模塊里面會(huì)生成注冊(cè)器

[圖片上傳失敗...(image-15d481-1640265031545)]

一共5個(gè)方法:

其他3個(gè)方法可以用注解的形式下。

然后init方法和process方法注解處理器解析:

1.得到所以有標(biāo)記的注解元素或者節(jié)點(diǎn)

2.得到包名

3.得到類名

4.拼接類

5.生成java文件

主要的一些對(duì)象:

1.元素

2.文件創(chuàng)造器

3.日志信息打印輸出messager

<pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">@AutoService(Processor.class)
public class BindViewProcessor extends AbstractProcessor {

private Messager mMessager; // 打印

private Elements mElementUtils;
private Map<String, ClassCreatorProxy> mProxyMap = new HashMap<>(); /***

  • 初始化一些類:比如日志金踪,讀寫流 * @param processingEnv
    */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
    super.init(processingEnv);
    mMessager = processingEnv.getMessager();
    mElementUtils = processingEnv.getElementUtils();
    }

    /***

  • 需要處理注解的類型 * @return
    */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
    HashSet<String> supportTypes = new LinkedHashSet<>();
    supportTypes.add(BindView.class.getCanonicalName());
    return supportTypes;
    }

    /***

  • java的版本 * @return
    */
    @Override
    public SourceVersion getSupportedSourceVersion() {
    return SourceVersion.latestSupported();
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
    mMessager.printMessage(Diagnostic.Kind.NOTE, "processing...");
    mProxyMap.clear();
    //得到所有的帶指定的注解BindView
    Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
    for (Element element : elements) {
    VariableElement variableElement = (VariableElement) element;
    TypeElement classElement = (TypeElement) variableElement.getEnclosingElement();
    String fullClassName = classElement.getQualifiedName().toString();
    //elements的信息保存到mProxyMap中浊洞,key:類全名
    ClassCreatorProxy proxy = mProxyMap.get(fullClassName);
    if (proxy == null) {
    proxy = new ClassCreatorProxy(mElementUtils, classElement);
    mProxyMap.put(fullClassName, proxy);
    }
    BindView bindAnnotation = variableElement.getAnnotation(BindView.class);//通過(guò)元素得到注解類
    int id = bindAnnotation.value();//得到注解類里面的值
    proxy.putElement(id, variableElement);
    }
    //拿到開(kāi)始map存放的信息
    //通過(guò)javapoet生成java類 for (String key : mProxyMap.keySet()) {
    ClassCreatorProxy proxyInfo = mProxyMap.get(key);
    JavaFile javaFile = JavaFile.builder(proxyInfo.getPackageName(), proxyInfo.generateJavaCode2()).build();
    try {
    // 生成文件
    javaFile.writeTo(processingEnv.getFiler());
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    mMessager.printMessage(Diagnostic.Kind.NOTE, "process finish ...");
    return true; }

}</pre>

<pre style="margin: 8px 0px;">

<pre style="margin: 8px 0px; font-family: "JetBrains Mono", monospace; font-size: 0.817rem; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198);">/**

  • 加入Method * javapoet */ private MethodSpec generateMethods2() {
    ClassName host = ClassName.bestGuess(mTypeElement.getQualifiedName().toString());
    MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("bind")
    .addModifiers(Modifier.PUBLIC)
    .returns(void.class)
    .addParameter(host, "host"); for (int id : mVariableElementMap.keySet()) {
    VariableElement element = mVariableElementMap.get(id); //VariableElement就是相應(yīng)的元素
    String name = element.getSimpleName().toString(); //控件名字
    String type = element.asType().toString();//控件的類型,是btn還是text
    methodBuilder.addCode("host." + name + " = " + "(" + type + ")(((android.app.Activity)host).findViewById( " + id + "));");
    }
    return methodBuilder.build(); }</pre>

<pre style="margin: 8px 0px; font-family: "JetBrains Mono", monospace; font-size: 0.817rem; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198);">最重要的是要得到VariableElement</pre>

<pre style="margin: 8px 0px; font-family: "JetBrains Mono", monospace; font-size: 0.817rem; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198);">主要就是從Elements胡岔、TypeElement得到想要的一些信息法希,如package name、Activity名靶瘸、變量類型苫亦、id等毛肋,</pre>

<pre style="margin: 8px 0px; font-family: "JetBrains Mono", monospace; font-size: 0.817rem; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198);">

<pre style="margin: 8px 0px; font-family: "JetBrains Mono", monospace;">使用的時(shí)候:
public class MainActivity extends AppCompatActivity {

@BindView(R.id.tv)
TextView mTextView;

@BindView(R.id.btn)
Button mButton;</pre>

</pre>

<pre style="margin: 8px 0px; font-family: "JetBrains Mono", monospace; font-size: 0.817rem; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198);">獲取和解析控件:</pre>

<pre style="margin: 8px 0px;">

<pre style="margin: 8px 0px; font-family: "JetBrains Mono", monospace; font-size: 0.817rem; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198);">VariableElement element = mVariableElementMap.get(id); //VariableElement就是相應(yīng)的元素 String name = element.getSimpleName().toString(); //控件名字 String type = element.asType().toString();//控件的類型,是btn還是text</pre>

<pre style="margin: 8px 0px; font-family: "JetBrains Mono", monospace; font-size: 0.817rem; background-color: rgb(43, 43, 43);">常用的幾個(gè)類(打印屋剑,元素润匙,javapoat文件讀寫操作JavaFile)</pre>

<pre style="margin: 8px 0px; font-family: "JetBrains Mono", monospace; font-size: 0.817rem; background-color: rgb(43, 43, 43);"> <pre style="margin: 8px 0px; color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace;">public class BindViewProcessor extends AbstractProcessor {

private Messager mMessager; // 打印

private Elements mElementUtils; // 對(duì)應(yīng)的元素
private Map<String, ClassCreatorProxy> mProxyMap = new HashMap<>(); //把帶注解的元素封裝在map中</pre>

3、封裝一個(gè)調(diào)用module唉匾。 </pre>

<pre style="margin: 8px 0px;"> #### apt-library 工具類在BindViewProcessor中創(chuàng)建了對(duì)應(yīng)的xxxActivity_ViewBinding.java孕讳,我們改怎么調(diào)用?當(dāng)然是反射啦N”臁3Р啤!apt-library 工具類

為什么用通過(guò)反射調(diào)用峡懈?

因?yàn)檫@些類都是動(dòng)態(tài)生成的璃饱,根據(jù)不同的activity動(dòng)態(tài)生成的!

</pre>

<pre style="margin: 8px 0px; color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace;">public class BindViewTools {

public static void bind(Activity activity) {

    Class clazz = activity.getClass();

try {
Class bindViewClass = Class.forName(clazz.getName() + "_ViewBinding");
Method method = bindViewClass.getMethod("bind", activity.getClass());
method.invoke(bindViewClass.newInstance(), activity);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
</pre>

<pre style="margin: 8px 0px; color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace;">4.使用編譯時(shí)注解</pre>

<pre style="margin: 8px 0px; color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace;">

<pre style="margin: 8px 0px; font-family: "JetBrains Mono", monospace;">public class MainActivity extends AppCompatActivity {

@BindView(R.id.tv)
TextView mTextView;

@BindView(R.id.btn)
Button mButton; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BindViewTools.bind(this);
mTextView.setText("bind TextView success");
mButton.setText("bind Button success");
}
}</pre>

</pre>

</pre>

</pre>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末逮诲,一起剝皮案震驚了整個(gè)濱河市帜平,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌梅鹦,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冗锁,死亡現(xiàn)場(chǎng)離奇詭異齐唆,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)冻河,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門箍邮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人叨叙,你說(shuō)我怎么就攤上這事锭弊。” “怎么了擂错?”我有些...
    開(kāi)封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵味滞,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我钮呀,道長(zhǎng)剑鞍,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任爽醋,我火速辦了婚禮蚁署,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蚂四。我一直安慰自己光戈,他們只是感情好哪痰,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著久妆,像睡著了一般晌杰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上镇饺,一...
    開(kāi)封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天乎莉,我揣著相機(jī)與錄音,去河邊找鬼奸笤。 笑死惋啃,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的监右。 我是一名探鬼主播边灭,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼健盒!你這毒婦竟也來(lái)了绒瘦?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤扣癣,失蹤者是張志新(化名)和其女友劉穎惰帽,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體父虑,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡该酗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了士嚎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呜魄。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖莱衩,靈堂內(nèi)的尸體忽然破棺而出爵嗅,到底是詐尸還是另有隱情,我是刑警寧澤笨蚁,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布睹晒,位于F島的核電站,受9級(jí)特大地震影響赚窃,放射性物質(zhì)發(fā)生泄漏册招。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一勒极、第九天 我趴在偏房一處隱蔽的房頂上張望是掰。 院中可真熱鬧,春花似錦辱匿、人聲如沸键痛。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)絮短。三九已至江兢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間丁频,已是汗流浹背杉允。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留席里,地道東北人叔磷。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像奖磁,于是被迫代替她去往敵國(guó)和親改基。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

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