Android APT(注解處理器之編譯時(shí)注解)

什么是注解

注解,通俗的來(lái)說(shuō)祸穷,就是像注釋一樣,是由程序員在代碼中加入的一種“標(biāo)注”谓罗,不影響所編寫(xiě)的原有代碼的執(zhí)行粱哼。而這種標(biāo)注(注解)可以被編碼用的IDE、編譯器檩咱、類(lèi)加載器的代理程序揭措、其他第三方工具以及原有代碼運(yùn)行期間讀取和處理,生成一些新的輔助代碼或是提示刻蚯,從而節(jié)省時(shí)間绊含,提升效率。這些工具讀取注解的時(shí)機(jī)是根據(jù)注解的生命周期來(lái)定的,注解的生命周期就是其“存在壽命”炊汹,分為三種:

1躬充,源注解

@Retention(RetentionPolicy.SOURCE)
注解將被編譯器丟棄。如:@Override

2讨便,類(lèi)注解(ButterKnife)

@Retention(RetentionPolicy.CLASS)
注解由編譯器記錄在類(lèi)文件中充甚,但不需要由VM在運(yùn)行時(shí)保留。

3霸褒,運(yùn)行時(shí)注解(EventBus)

@Retention(RetentionPolicy.RUNTIME)
注解由編譯器記錄在類(lèi)文件中伴找,并在運(yùn)行時(shí)由VM保存,因此可以反射性地讀取它們废菱。 如:@Deprecated

APT(Annotation Processing Tool)注解處理器技矮, 是一個(gè)Gradle插件,協(xié)助Android Studio 處理annotation processors,

是一種處理注解的工具殊轴,確切的說(shuō)它是javac的一個(gè)工具衰倦,可以在代碼編譯期解析注解。注解處理器以Java代碼(或者編譯過(guò)的字節(jié)碼)作為輸入旁理,生成.java文件作為輸出樊零。

Android Gradle插件2.2版本發(fā)布后,Android 官方提供了annotationProcessor插件來(lái)代替android-apt孽文,annotationProcessor同時(shí)支持 javac 和 jack 編譯方式驻襟,而android-apt只支持 javac 方式十性。
同時(shí)android-apt作者宣布不在維護(hù),當(dāng)然目前android-apt仍然可以正常運(yùn)行

總體流程:自定義注解->自定義注解處理器(會(huì)用到j(luò)avapoet)->注冊(cè)注解處理器(會(huì)用到auto-service)->編譯生成java代碼

這面我只是簡(jiǎn)單的做了findViewId和onCliclk事件塑悼!

那我們開(kāi)始說(shuō)起:

image.png

如圖:
apt_annotation ,一個(gè)Java Library
主要是用來(lái)自定義注解

apt_library,一個(gè)Android Library
主要是用來(lái)寫(xiě)調(diào)用的編譯時(shí)期生成的java代碼的工具類(lèi)

apt_processor ,一個(gè)Java Library
主要是用來(lái)處理編譯時(shí)的注解操作

為什么要建立java Library呢 ?

原因AbstractProcessor不在Android SDK里面楷掉!要是不建立 Java Library 是調(diào)用不到的厢蒜!在java jre中。

首先:在apt_annotation module 建立注解
//編譯時(shí)期注解烹植,作用目標(biāo) 域生明(類(lèi)斑鸦,接口,成員變量草雕,類(lèi)靜態(tài)變量)
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
    int value();
}
@Retention(RetentionPolicy.CLASS)
@Target(METHOD)
public @interface OnClick {
    int[] value();
}

那注解寫(xiě)好了:

再來(lái):apt_processor module 建立編譯時(shí)注解處理的邏輯

在moudle中添加依賴(lài)

 implementation project(':apt_annotation')
 implementation 'com.google.auto.service:auto-service:1.0-rc2'
 implementation 'com.squareup:javapoet:1.11.1'
@AutoService(Processor.class)
public class BindViewProcessorByPoet extends AbstractProcessor {

    //寫(xiě)入代碼會(huì)用到
    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        filer = processingEnv.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        // 拿到每個(gè)類(lèi)巷屿,要生成的代碼集合;
        Map<TypeElement, List<CodeBlock.Builder>> builderMap = findAndBuilderByTargets(roundEnvironment);
        for (TypeElement typeElement : builderMap.keySet()) {
            List<CodeBlock.Builder> codeList = builderMap.get(typeElement);
            // 去生成對(duì)應(yīng)的 類(lèi)文件墩虹;
            BindViewCreatorByPoetHelper.writeBindView(typeElement, codeList, filer);
        }
        return true;
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>();
        types.add(BindView.class.getCanonicalName());
        types.add(OnClick.class.getCanonicalName());
        return types;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    private Map<TypeElement, List<CodeBlock.Builder>> findAndBuilderByTargets(RoundEnvironment env) {
        Map<TypeElement, List<CodeBlock.Builder>> builderMap = new HashMap<>();

        // 遍歷帶對(duì)應(yīng)注解的元素嘱巾,就是某個(gè)View對(duì)象
        for (Element element : env.getElementsAnnotatedWith(BindView.class)) {

            //感覺(jué)這里面應(yīng)該是VariableElement
            BindViewCreatorByPoetHelper.parseBindView(element, builderMap);
        }

        // 遍歷帶對(duì)應(yīng)注解的元素,就是某個(gè)方法
        for (Element element : env.getElementsAnnotatedWith(OnClick.class)) {
            BindViewCreatorByPoetHelper.parseListenerView(element, builderMap);
        }
        return builderMap;
    }

}

這面會(huì)實(shí)現(xiàn)4個(gè)方法:
init:初始化诫钓⊙眩可以得到ProcessingEnviroment,ProcessingEnviroment提供很多有用的工具類(lèi)Elements, Types 和 Filer
getSupportedAnnotationTypes:指定這個(gè)注解處理器是注冊(cè)給哪個(gè)注解的菌湃,這里說(shuō)明是注解BindView和OnClick
getSupportedSourceVersion:指定使用的Java版本问拘,通常這里返回SourceVersion.latestSupported()
process:可以在這里寫(xiě)掃描、評(píng)估和處理注解的代碼惧所,生成Java文件
所以說(shuō)主要的還是

 @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        // 拿到每個(gè)類(lèi)骤坐,要生成的代碼集合;
        Map<TypeElement, List<CodeBlock.Builder>> builderMap = findAndBuilderByTargets(roundEnvironment);
        for (TypeElement typeElement : builderMap.keySet()) {
            List<CodeBlock.Builder> codeList = builderMap.get(typeElement);
            // 去生成對(duì)應(yīng)的 類(lèi)文件下愈;
            BindViewCreatorByPoetHelper.writeBindView(typeElement, codeList, filer);
        }
        return true;
    }

這一方法:
我們?cè)敿?xì)看下
因?yàn)榇蠖紨?shù)代碼里面都是有注釋的:

private Map<TypeElement, List<CodeBlock.Builder>> findAndBuilderByTargets(RoundEnvironment env) {
        Map<TypeElement, List<CodeBlock.Builder>> builderMap = new HashMap<>();

        // 遍歷帶對(duì)應(yīng)注解的元素纽绍,就是某個(gè)View對(duì)象
        for (Element element : env.getElementsAnnotatedWith(BindView.class)) {

            //感覺(jué)這里面應(yīng)該是VariableElement
            BindViewCreatorByPoetHelper.parseBindView(element, builderMap);
        }

        // 遍歷帶對(duì)應(yīng)注解的元素,就是某個(gè)方法
        for (Element element : env.getElementsAnnotatedWith(OnClick.class)) {
            BindViewCreatorByPoetHelper.parseListenerView(element, builderMap);
        }
        return builderMap;
    }

Map<TypeElement, List<CodeBlock.Builder>> builderMap = new HashMap<>();
這一個(gè)集合進(jìn)行存儲(chǔ)驰唬,key則是其實(shí)也就是關(guān)聯(lián)Actvity對(duì)象的Element顶岸,value則是寫(xiě)入的代碼集合(一個(gè)類(lèi)維護(hù)一個(gè)生成的代碼塊的集合)
然后分別對(duì)兩個(gè)注解添加代碼集合:

public class BindViewCreatorByPoetHelper {

    public static void parseBindView(Element element, Map<TypeElement, List<CodeBlock.Builder>> codeBuilderMap) {
        //獲取最外層的類(lèi)名,具體實(shí)際就是關(guān)聯(lián)某個(gè)Activity對(duì)象Element
        //因?yàn)榇藭r(shí)的element是VriableElement,所以拿到的Enclosing 就應(yīng)該是Activity對(duì)象
        TypeElement classElement = (TypeElement) element.getEnclosingElement();
        // 這個(gè)view是哪個(gè)類(lèi) Class(android.widget.TextView)
        String viewType = element.asType().toString();
        // 注解的值叫编,具體實(shí)際可能就是 R.id.xxx
        int value = element.getAnnotation(BindView.class).value();
        // 這個(gè)view對(duì)象名稱(chēng)(比如TextView)
        String name = element.getSimpleName().toString();

        //創(chuàng)建代碼塊
        //$L是占位符辖佣,會(huì)把后面的 name 參數(shù)拼接到 $L 所在的地方
        CodeBlock.Builder builder = CodeBlock.builder()
                .add("target.$L = ", name);
        builder.add("($L)target.findViewById($L)", viewType, value);

        List<CodeBlock.Builder> codeList = codeBuilderMap.get(classElement);
        if (codeList == null) {
            codeList = new ArrayList<>();
            codeBuilderMap.put(classElement, codeList);
        }
        codeList.add(builder);
    }

    public static void parseListenerView(Element element, Map<TypeElement, List<CodeBlock.Builder>> codeBuilderMap) {
        //獲取最外層的類(lèi)名,具體實(shí)際就是關(guān)聯(lián)某個(gè)Activity對(duì)象Element
        TypeElement classElement = (TypeElement) element.getEnclosingElement();

        List<CodeBlock.Builder> codeList = codeBuilderMap.get(classElement);
        if (codeList == null) {
            codeList = new ArrayList<>();
            codeBuilderMap.put(classElement, codeList);
        }

        //注解的值
        int[] annotationValue = element.getAnnotation(OnClick.class).value();

        //因?yàn)樽⒔釦Target是Method搓逾,所以這面拿到的就是方法名字的字符串
        String name = element.getSimpleName().toString();

        //創(chuàng)建代碼塊
        for (int value : annotationValue) {
            CodeBlock.Builder builder = CodeBlock.builder();
            builder.add("target.findViewById($L).setOnClickListener(new android.view.View.OnClickListener() { public void onClick(View v) { target.$L(v); }})", value, name);
            codeList.add(builder);
        }
    }

    public static void writeBindView(TypeElement classElement, List<CodeBlock.Builder> codeList, Filer filer) {
        // enclosingElement 卷谈,暗指 某個(gè)Activity.
        // 先拿到 Activity 所在包名( cn.citytag.aptdemo.Main3Activity)
        String packageName = classElement.getQualifiedName().toString();
        packageName = packageName.substring(0, packageName.lastIndexOf("."));//(cn.citytag.aptdemo)
        // 再拿到Activity類(lèi)名(Main3Activity))
        String className = classElement.getSimpleName().toString();

        //此元素定義的類(lèi)型
        TypeName type = TypeName.get(classElement.asType());

        //if (type instanceof ParameterizedTypeName) {
        // type = ((ParameterizedTypeName) type).rawType;
        //}

        ClassName bindingClassName = ClassName.get(packageName, className + "_ViewBindingPoet");
        MethodSpec.Builder methodSpecBuilder = MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .addParameter(type, "target", Modifier.FINAL)
                .addParameter(ClassName.get("android.view", "View"), "source", Modifier.FINAL);
        for (CodeBlock.Builder codeBuilder : codeList) {
            //方法里面 ,代碼是什么
            methodSpecBuilder.addStatement(codeBuilder.build());
        }
        methodSpecBuilder.build();

        // 創(chuàng)建類(lèi) MainActivity_ViewBinding
        TypeSpec bindClass = TypeSpec.classBuilder(bindingClassName.simpleName())
                .addModifiers(Modifier.PUBLIC)
                .addMethod(methodSpecBuilder.build())
                .build();

        try {
            // 生成文件
            JavaFile javaFile = JavaFile.builder(packageName, bindClass).build();
            //將文件寫(xiě)出
            javaFile.writeTo(filer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  List<CodeBlock.Builder> codeList = codeBuilderMap.get(classElement);
  if (codeList == null) {
          codeList = new ArrayList<>();
          codeBuilderMap.put(classElement, codeList);
   }

都會(huì)加以判斷是否存在此TypeElemen的key霞篡,在進(jìn)行put元素世蔗!

這樣的話代碼集合添加完成之后再進(jìn)行寫(xiě)入端逼,
還是這個(gè)代碼,每一個(gè)TypeElemen對(duì)應(yīng)一個(gè)代碼塊集合進(jìn)行寫(xiě)入代碼污淋;

   @Override
   public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        // 拿到每個(gè)類(lèi)顶滩,要生成的代碼集合;
        Map<TypeElement, List<CodeBlock.Builder>> builderMap = findAndBuilderByTargets(roundEnvironment);
        for (TypeElement typeElement : builderMap.keySet()) {
            List<CodeBlock.Builder> codeList = builderMap.get(typeElement);
            // 去生成對(duì)應(yīng)的 類(lèi)文件寸爆;
            BindViewCreatorByPoetHelper.writeBindView(typeElement, codeList, filer);
        }
        return true;
    }
 public static void writeBindView(TypeElement classElement, List<CodeBlock.Builder> codeList, Filer filer) {
        //  classElement 礁鲁,就是關(guān)聯(lián)的某個(gè)Activity
        // 先拿到 Activity 所在包名( cn.citytag.aptdemo.Main3Activity)
        String packageName = classElement.getQualifiedName().toString();
        packageName = packageName.substring(0, packageName.lastIndexOf("."));//(cn.citytag.aptdemo)
        // 再拿到Activity類(lèi)名(Main3Activity))
        String className = classElement.getSimpleName().toString();

        //此元素定義的類(lèi)型
        TypeName type = TypeName.get(classElement.asType());

        //if (type instanceof ParameterizedTypeName) {
        // type = ((ParameterizedTypeName) type).rawType;
        //}

        ClassName bindingClassName = ClassName.get(packageName, className + "_ViewBindingPoet");
        MethodSpec.Builder methodSpecBuilder = MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .addParameter(type, "target", Modifier.FINAL)
                .addParameter(ClassName.get("android.view", "View"), "source", Modifier.FINAL);
        for (CodeBlock.Builder codeBuilder : codeList) {
            //方法里面 ,代碼是什么
            methodSpecBuilder.addStatement(codeBuilder.build());
        }
        methodSpecBuilder.build();

        // 創(chuàng)建類(lèi) MainActivity_ViewBinding
        TypeSpec bindClass = TypeSpec.classBuilder(bindingClassName.simpleName())
                .addModifiers(Modifier.PUBLIC)
                .addMethod(methodSpecBuilder.build())
                .build();

        try {
            // 生成文件
            JavaFile javaFile = JavaFile.builder(packageName, bindClass).build();
            //將文件寫(xiě)出
            javaFile.writeTo(filer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
然后:apt_library module 建立Tools類(lèi)
public class BindViewByPoetTools {
    public static void bind(Activity activity) {
        //獲取activity的decorView(根view)
        View view = activity.getWindow().getDecorView();
        bind(activity, view);
    }

    private static void bind(Object obj, View view) {
        String className = obj.getClass().getName();
        //找到該activity對(duì)應(yīng)的Bind類(lèi)的名字
        String generateClass = className + "_ViewBindingPoet";
        //然后調(diào)用Bind類(lèi)的構(gòu)造方法,從而完成activity里view的初始化
        try {
            Class.forName(generateClass).getConstructor(obj.getClass(), View.class).newInstance(obj, view);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

最后:app module 進(jìn)行綁定注解赁豆,并調(diào)用Tools類(lèi)仅醇!

在app module添加依賴(lài)

   implementation project(':apt_annotation')
   implementation project(':apt_library')
   annotationProcessor project(':apt_processor')
為什么沒(méi)用apt呢!gradle高版本就不用那么麻煩了魔种!直接annotationProcessor這個(gè)就可以在編譯時(shí)處理注解了析二!
public class Main3Activity extends AppCompatActivity {
    @BindView(R.id.tv_one)
    TextView mTextViewOne;
    @BindView(R.id.tv_two)
    TextView mTextViewTwo;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        BindViewByPoetTools.bind(this);
        mTextViewOne.setText("one");
        mTextViewTwo.setText("two");
    }

    @OnClick({R.id.tv_one, R.id.tv_two})
    public void onBtn1Click(View v) {
        Toast.makeText(this, "", Toast.LENGTH_SHORT).show();
    }
}

最終:ReBuild as 則會(huì)生成如下代碼:
apt_java.png
package cn.citytag.aptdemo;

import android.view.View;

public class Main3Activity_ViewBindingPoet {
  public Main3Activity_ViewBindingPoet(final Main3Activity target, final View source) {
    target.mTextViewOne = (android.widget.TextView)target.findViewById(2131165325);
    target.mTextViewTwo = (android.widget.TextView)target.findViewById(2131165326);
    target.findViewById(2131165325).setOnClickListener(new android.view.View.OnClickListener() { public void onClick(View v) { target.onBtn1Click(v); }});
    target.findViewById(2131165326).setOnClickListener(new android.view.View.OnClickListener() { public void onClick(View v) { target.onBtn1Click(v); }});
  }
}
介紹下依賴(lài)庫(kù)auto-service:

auto-service的作用是向系統(tǒng)注冊(cè)processor(自定義注解處理器),
在javac編譯時(shí)节预,才會(huì)調(diào)用到我們這個(gè)自定義的注解處理器方法叶摄。

主要是自己建立我沒(méi)有試!這個(gè)具體我也不清楚心铃!

在使用注解處理器需要先聲明准谚,步驟:
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 文件寫(xiě)入注解處理器的全稱(chēng)奔滑,包括包路徑艾岂;
這樣聲明下來(lái)也太麻煩了?這就是用引入auto-service的原因朋其。
通過(guò)auto-service中的@AutoService可以自動(dòng)生成AutoService注解處理器是Google開(kāi)發(fā)的王浴,用來(lái)生成 META-INF/services/javax.annotation.processing.Processor 文件的

介紹下依賴(lài)庫(kù) javapoet:

助于在編譯期間生成java代碼,要不自己StringBuilder拼接很麻煩梅猿!
https://github.com/square/javapoet

如果在as ReBuild的時(shí)候報(bào)這個(gè)問(wèn)題:

錯(cuò)誤: 編碼GBK的不可映射字符
在apt_processor gradle
加入下面代碼氓辣!

tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市袱蚓,隨后出現(xiàn)的幾起案子钞啸,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件体斩,死亡現(xiàn)場(chǎng)離奇詭異梭稚,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)絮吵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)弧烤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蹬敲,你說(shuō)我怎么就攤上這事扼褪。” “怎么了粱栖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)脏毯。 經(jīng)常有香客問(wèn)我闹究,道長(zhǎng),這世上最難降的妖魔是什么食店? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任渣淤,我火速辦了婚禮,結(jié)果婚禮上吉嫩,老公的妹妹穿的比我還像新娘价认。我一直安慰自己,他們只是感情好自娩,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布用踩。 她就那樣靜靜地躺著,像睡著了一般忙迁。 火紅的嫁衣襯著肌膚如雪脐彩。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天姊扔,我揣著相機(jī)與錄音惠奸,去河邊找鬼。 笑死恰梢,一個(gè)胖子當(dāng)著我的面吹牛佛南,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嵌言,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼嗅回,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了呀页?” 一聲冷哼從身側(cè)響起妈拌,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后尘分,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體猜惋,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年培愁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了著摔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡定续,死狀恐怖谍咆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情私股,我是刑警寧澤摹察,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站倡鲸,受9級(jí)特大地震影響供嚎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜峭状,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一克滴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧优床,春花似錦劝赔、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至移层,卻和暖如春启摄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背幽钢。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工歉备, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人匪燕。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓蕾羊,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親帽驯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子龟再,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345