Android知识体系(1)

Android知识体系(1)
Android知识体系(2)

一、Activity相关

1、生命周期流转

四个状态:running paused stopped killed running:当前显示在屏幕上的Activity,用户可见,可交互 paused:依旧用户可见,但是已经失去焦点,不可交互
stopped:用户不可见,也不可交互 killed:等待被系统回收

onCreate() >>onStart()>>onResume()>>onRestart()>>onPause()>>onStop()>>onDestory()

Activity A切换到B执行方法: A:onCreate()>>onStart()>>onResume()>>onPause()
B:onCreate()>>onStart()>>onResume()
A:onStop()>>onDestory()

A切换到B(B是以DialogActivity形式存在的)执行方法: A:onCreate()>>onStart()>>onResume()>>onPause()
B:onCreate()>>onStart()>>onResume()

当用户按下home后,再返回,执行的方法: onPause()>>onStop()>>home>>onRestart()>>onResume()

Activity中的onSaveInstanceState和onRestoreInstanceState()
onSaveInstanceState(Bundle outState):
保存有ID组件的状态,在系统销毁Activity时。调用时机是在onStop之前,但是不确定在onPause之前还是之后。用户主动销毁Activity不会调用。
onRestoreInstanceState(Bundle outState):
在系统销毁Activity后,重新创建时可以调用。该方法保存的 Bundle 对象在 Activity 恢复的时候也会通过参数传递到 onCreate() 方法中;

2、有关横竖屏切换生命周期的变化:

横竖屏切换会根据configChanges配置来决定是否销毁Activity.

不配置configChanges属性 设置android:configChanges="orientation"设置android:configChanges="orientation|keyboardHidden"(3.2系统之前的系统不会执行生命周期方法了)
以上三种配置,横竖屏切换时Activity均会销毁重建,Activity的生命周期都会重新执行一次

配置 android:configChanges="orientation|keyboardHidden|screenSize"不会销毁重建

3、setContentView方法

将Activity对应的xml布局添加到根布局中。调用mWindow.setContentView,添加到decorView中。

4、ViewRootImpl\WindowManagerService

相关资料文章:
Activity的生命周期
Android 横竖屏切换总结

二、Fragment相关

1、对应生命周期

activity: ----------------onCreate------------------------ | onStart | onResume onPause onStop | onDestroy ----------------------

fragment: onAttach onCreate onCreateView onActivityCreated | onStart | onResume onPause onStop | onDestroy onDestroyView onDetach

2、fragment如何懒加载

(1)Support时代:在setUserVisibleHint()方法中判断是否对用户可见。
(2)ViewPager
(2)ViewPager2

3、fragment的add、show、hide、replace

add:添加,一般配合hide使用。不会清空容器(FragmentTransaction)里面的内容
replace:替换。会清空容器里所有内容,只保留一个fragment显示。
hide:隐藏
show:显示

相关资料文章:
Fragment生命周期
Fragment全解析系列(一):那些年踩过的坑

三、启动模式(以及使用场景)

standard: 标准模式 不管有没有实例都会重新创建一个实例 场景:多数普通的Activity界面

singleTop: 栈顶模式 如果实例位于任务栈顶,则不重新创建。若不位于栈顶则重新创建实例 场景:消息接收界面例如QQ弹出的消息界面

singleTask: 栈内唯一模式 若当前任务栈中存在实例,那么将此实例之上的其它实例全部退出,使得此实例位于栈顶 场景:首页主界面

singleInstance: 新栈唯一模式 每次都会新建一个任务栈,并且在这个栈中只存在一个实例 场景:与主界面分离的页面如闹钟提醒

注意:
以上所谓的启动模式是不包含设置了 intent flag的情况下。在设置了intent flag时,启动的方式和结果可能和使用的启动模式不一致。 activity的启动和启动模式、intent
flag、taskAffinity、以及是否是Activity直接启动另一个Activity相关,并不是单独由启动模式决定的。

相关资料文章:
Android面试官装逼失败之:Activity的启动模式

Flag :
intent.flag_activity_new_task\flag_activity_clear_task\flag_activity_clear_top\flag_activity_single_top

四、APP启动过程

1、系统相关成员:

init进程: Android系统启动后首先唤醒的进程 Zygote进程:所有Android进程的父进程。 SystemServer进程:系统服务进程,负责系统中大大小小的各种事务。唤起
ActivityManagerService、PackageManagerService、WindowManagerService ActivityManagerService:
负责四大组件的启动、切换、调度以及应用进程的管理和调度等。对于一些进程的启动,会通过Binder通信机制传递给AMS,再处理给Zygote进程。 PackageManagerService :
负责应用包的一些操作,如安装、卸载、解析AndroidManifest.xml、扫描文件等。 WindowManagerService : 负责窗口相关的操作,如窗口启动、添加、移除等。
Launcher : 桌面应用

2、点击桌面图标启动流程:

(1)、Launcher接收点击事件,调用StartActivity准备启动目标Activity,同事调用checkStartActivityResult检查目标Activity。
内部核心逻辑通过AIDL Binder通信通知ATMS(ActivityTaskManagerService)处理打开对应APP. 【Launcher StartActivity】
(2)、ATMS接收到消息后,会使Launcher进入Paused状态。 【ATMS StartActivity】
(3)、ATMS判断目标APP对应的进程是否已经启动。如果已经启动则直接打开对应Activity,如果没有启动则需创建对应进程。 如何判断对应进程是否启动?
答:ATMS内部维护已经启动的相关进程,通过processName(一般为包名)和UUID去查找是否已经存在。 如何创建新的进程?
答:通过Zygote进程,调用fork方法新建对应进程,并返回新进程的pid。 【Zygote fork进程】
(4)、创建进程时,会通过反射调用ActivityThread的main方法,创建新的ActivityThread。在main方法中创建了主线程的Looper对象,并开始loop循环。
【ActivityThread main:
1、attach;2、handleBindApplication;3、attachBaseContext;4、installContentProviders;5、Application
onCreate】
(5)、在ActivityThread的main方法中通过bindApplication启动Application 【ActivityThread loop】
(6)、启动Application后会创建上下文context,并启动Activity。 【Activity 进入生命周期】

3、启动优化:

(1)、闪屏页优化
(2)、MultiDex优化
(3)、第三方库懒加载
(4)、WebView优化
(5)、线程优化
(6)、系统调用优化

思考:MultiDex.install的原理?
答:MUltiDex.install 调用了doInstallation()方法。 主要步骤有: 1、获取加载dex的classloader; 2、清除旧dex文件目录; 3、创建新的dex文件目录;
4、获取所有dex文件; 5、通过反射将所有dex文件写入dexElements中 6、通过classloader加载dexElements中所有的dex文件

相关资料文章:
女儿拿着小天才电话手表问我App启动流程
今日头条启动优化

五.Service相关

-基础定义:Service是一个在后台执行长时间运行操作而不用提供用户界面的应用组件,可由其他组件启动,即使用户切换到其他应用程序,Service 仍然在后台继续运行。

1、Service两种启动方式

startService: onCreate()>>onStartCommand()>>onDestroy()
调用stopService(intent)停止service。一旦服务开启,其生命周期与调用者无关,调用者不能调用service中的方法。

bindService: onCreate()>>onBind()>>onUnbind()>>onDestroy()
调用unbindService(serviceConnection)停止service。生命周期与调用者绑定,多个组件和Service绑定时,当所有组件销毁时Service才会停止。

2、Service注册

在AndroidManifest.xml文件中使用<Service>节点进行注册。使用android:exported属性设置 是否运行外部应用获取 。

3、如何调用服务里面的方法

bind方式启动时: 在服务类内部创建一个内部类,可以间接调用服务中的方法 实现onbind方法,返回的就是这个内部类 在activity中绑定service bindService(intent,serviceConnection,_)
在绑定成功的回调onServiceConnection中会传递一个IBinder对象 强制类型转换为自定义的接口类型,调用接口里面的方法 start方式启动时:
通过Bundle携带参数。在service类中的onStartCommand方法中接收,通过接收bundle携带的参数判断调用方法。

4、intentService (Android 8.0 推荐使用JobIntentService)

intentService可以看作是service和handlerThread的结合,在完成任务后会自动停止,intentService是继承自service处理异步请求的一个类,在内部有一个工作线程来处理耗时操作。 完成任务后会自动停止不需要手动停止。
如果启动多次,每一个耗时操作会以工作队列的方式在intentService的onHandlerIntent回调中执行。

5、前台服务

  • 定义:前台服务是用户认可的且在系统内存不足时不允许系统销毁的服务,运行的优先级比普通后台服务高。

6、保证Service不被后台销毁

(1)在onStartCommand方法中返回 START_STICKY

    @Override
    public int onStartCommand(Intent intent,int flags,int startId){
        flags =  START_STICKY;
        retrun super.onStartCommand(intent,flags,startId);
    }

(2)通过前台服务来提升Service的优先级

 @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: ");
        Notification.Builder builder = new Notification.Builder(this.getApplicationContext());
        Intent intent1 = new Intent(this, MainActivity.class);
        builder.setContentIntent(PendingIntent.getActivity(this, 0, intent1, 0))
                .setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher))
                .setContentTitle("测试ContentTitle")
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentText("测试ContentText")
                .setWhen(System.currentTimeMillis());

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel("CHANNEL_ONE_ID", "CHANNEL_ONE_NAME", NotificationManager.IMPORTANCE_MIN);
            channel.enableLights(false);
            channel.setShowBadge(false);
            channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
            NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            manager.createNotificationChannel(channel);
            builder.setChannelId("CHANNEL_ONE_ID");
        }
        Notification notification = builder.build();
        notification.defaults = Notification.DEFAULT_SOUND;

        startForeground(100, notification);
        return super.onStartCommand(intent, flags, startId);
    }

(3)在onDestroy中发送广播重新启动service

相关资料文章:
Android Service两种启动方式的区别
Android Service和IntentService知识点详细总结

Android Service体系图

六.Broadcast Receiver

BroadCastReceiver是对发送出来的broadcast进行过滤、接收和响应的组件。在执行完onReceiver()方法后,生命周期结束。

1、类型

标准广播:sendBroadcast()发送,完全异步执行,所有广播几乎在同一时间接收到,没有先后顺序,效率高,无法被截断

有序广播:sendOrderedBroadcast()发送,同步执行,同一时间只有一个广播接收器接收到这个消息,有先后顺序,如果消息被截断,那么后面的广播接收器就无法接收到这条消息

2、注册

静态注册: 在manifest.xml文件中进行注册,使用<<receiver>>声明,并在标签内用<<intent-filter>>配置过滤器。 这种形式的广播接收器生命周期伴随着整个应用。
优先级由<<intent-filter>>中priority决定,数值越到优先级越高。


   <receiver android:name=".MyBroadcastReceiver">
       <intent-filter android:proiorty="100">
           <action android:name=""/>
       </intent-filter>
   </receiver>

动态注册: 在代码中定义并设置好一个IntentFilter对象。调用registerReceiver()进行注册。

-区别:
1、动态广播的系统优先级比静态广播高;
2、静态广播的生存周期比动态广播长。可用于监听手机电量低、手机开机等。

3、LocalBroadcastReceiver\LocalBroadcastManager

系统广播:可用于应用间、应用与系统间、应用内部的广播接收。
本地广播:只有应用内部的广播才能接收到。LocalBroadcastManager对本地广播进行注册和发送。

localBroadManager.sendBroadcast();
localBroadManager.register()

相关资料文章:
Android之BroadcastReceiver总结

七.Content Provider

1、定义&作用

内容提供器是用于不同程序之间共享数据的功能,它提供了一套完整的机制。允许一个程序访问另一个程序的数据,同时保证被访问数据安全性。
其为数据存储和读取提供了统一的接口,使用表的形式对数据进行封装。

2、种类

3、使用

通过URI(uniform resource identifier 统一资源标识符)访问对应ContentProvider。

外部进程通过ContentResolver访问对应ContentProvider。


    ContentResolver resolver  = getContentResolver();
    
    Uri uri = Uri.parse("content://com.demo.myprovider/user");
    
    Cursor cursor = resolver.query(uri,null,null,null,"userid desc");

4、原理

android中binder原理

相关资料文章:
关于ContentProvider的知识都在这里了!
四大组件-Content Provider详解

八.Intent相关

1、定义

Intent意图:是一个消息传递对象,可以通过它携带数据,来向其它组件发起请求操作。例如:启动Activity、启动或者绑定Service、发送广播等。

2、分类

显示意图:直接指定调用组件的名称,一般用于应用内部。
打开内部activity、启动or绑定Service、发送广播
隐示意图:没有指定组件的名称,而是声明action行为,从而允许其它应用的组件来处理。
打开拨号界面\打开指定网页

    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_VIEW);
    intent.setData(Uri.parse("tel:123456789"));  // intent.setData(Uri.parse("www.baidu.com"));
    startActivity(intent);

3、intent-filter

(1)、<action>:指定组件要完成的操作,系统内置:MAIN、VIEW、SEND等等。
(2)、<category>:为action增加额外的附加类别信息,常见有:DEFAULT、LAUNCHER等。
(3)、<data>&<type>:为action提供要操作的数据。

    
    <activity android:name = ".view.ShareActivity">
        <intent-filter>
            <action android:name = "android.intent.action.SEND"/>
            <category android:name = "android.intent.category.DEFAULT"/>
            <data android:mineType = "text/plain"/>
        </intent-filter>
    </activity>

相关资料文章:
Android 基础知识5:Intent 和 Intent 过滤器

九.网络编程相关

1、网络基础

网络体系结构分层 TCP/IP模型 :(自上往下)
应用层(HTTP、FTP、DNS、SMTP等等)
运输层(TCP、UDP)
网络层(IP)
数据链路层(ARP)
物理层

(1)、应用层:如Http协议,它实际定义了如何包装和解析数据,应用层采用HTTP协议后,会按照协议包装数据,如按照请求行、请求头、请求体包装。
包装好后将数据传输至运输层。
(2)、运输层:这一层指定了将数据以何种方式传送到端口号。涉及到如何建立连接?如何保证数据不丢失?如何调节流量控制和拥塞控制?等。
(3)、网络层:这一层指定了将数据传送到那个IP地址。涉及到最优路线,路由选择算法等。
(4)、数据链路层:如ARP协议,负责把IP地址解析成对应的MAC地址,即硬件地址,这样就能找到对应的唯一机器。
(5)、物理层:提供二进制传输服务,也就是真正开始通过传输介质(有线、无线)开始进行数据的传输了。

2、Http & Https

(1)、无连接:Http约定了每次连接只处理一个请求,一次请求完成后就断开连接。主要是为了缓解服务器的压力,减少资源占用。
(2)、无状态:每个请求之间都是独立的,对于之前的请求事务没有记忆的能力。
(3)、HTTP缓存:主要通过Header中的Cache-Control和ETag来实现。
Cache-Control: private\public\max-age\no-cache\no-store
ETag:即用来进行对比缓存,ETag是服务端资源的一个标识码
(4)、对称加密:客户端和服务端双方统一采用一个密钥进行加密解密。
(5)、非对称加密:公钥加密的信息只能用私钥解开,私钥加密的信息只能被公钥解开。只有服务端存在私钥,公钥传递给客户端。
(6)、HTTPS:即http + ssl ,采用了非对称加密的方式包装数据。
(7)、Keep-Alive模式:又称为持久连接、连接重用。使客户端到服务端的连接持续有效,当出现后续请求后不用再次建立连接。

客户端与服务端通信流程
(1)客户端发送https请求
(2)服务端配置一套证书(相当于公钥和私钥)。收到请求后向客户端发送证书(公钥)。
(3)客户端收到证书后验证是否有效.验证完毕后,产生一个随机值,利用证书对随机值加密后向服务端发送
(4)服务端收到加密数据后利用证书(私钥)解密(这里非对称加密过程完成)。拿到随机数后作为密钥,通过密钥加密数据后向客户端发送。
(5)客户端收到数据后也用随机值作为密钥进行解密数据(这里是对称加密)

3、Socket

  • Socket简单介绍
    • Socket就是为网络服务提供的一种机制
    • 通信的两端都有Socket
    • 网络通信其实就是Socket间的通信
    • 数据在两个Socket间通过IO传输
    • 玩Socket主要就是记住流程,代码查文档就行
    • Socket的简单使用的话应该都会,两个端各建立一个Socket,服务端的叫ServerSocket,然后建立连接即可。

4、TCP & UDP

TCP:1、必须建立连接,形成传输数据的通道。
2、在连接中可进行大量数据的传输。
3、通过三次握手完成连接,是可靠协议。
4、必须建立连接,效率低。

UDP:1、面向无连接
2、每个数据包大小在64k以内
3、无需建立连接,是不可靠协议
4、效率高

5、三次握手、四次挥手

三次握手流程:
(1)、客户端 发送SYN = 1 Seq = X 表示请求建立连接,Seq = X 是客户端生成的随机数;
(2)、服务端 收到请求后,发送 SYN = 1 ACK = X+1 Seq = Y 表示回复客户端建立连接的请求,Seq = Y 是服务端生成的随机数;
(3)、客户端 收到回复后,再次发送 ACK = Y + 1 Seq = Z


三次握手流程

思考:为什么需要三次握手?
答:前两次握手是建立一个连接必须的。第三次握手是为了防止已经失效的连接请求报文段突然又传到服务端,因而产生错误

四次挥手流程:
(1)、客户端 发送FIN = 1 ACK = Z Seq = X
(2)、服务端 收到后回复 ACK = X + 1 Seq = Z
(3)、服务端 发送FIN = 1 ACK = X Seq = Y
(4)、客户端 收到后回复 ACK = Y Seq = X


四次挥手流程

思考:为什么需要四次挥手?
答:第2 ACK回复消息 和 第3 FIN终止消息并不是同时发出,先发送回复ACK再发送FIN终止消息。这也很好理解,当客户端要求断开连接时,此时服务端可能还有未发送完的数据,
所以先ACK,然后等数据发送完再FIN。这样就变成了四次握手了。

相关资料文章:
HTTPS和HTTP的区别
Android技能树 — 网络小结(3)之HTTP/HTTPS

十.事件分发机制

思考:当发生手指触摸、点击、移动等事件时,这些事件是如何被转换成手机系统可识别的代码事件(event)? 这个过程中有那些系统组件参与?
答:(1)-首先,在Android系统中将所有的输入事件定义为InputEvent,根据输入类型又进一步分为KeyEvent(键盘输入事件) 和 MotionEvent(屏幕触摸事件)
-硬件接收到事件后,通过InputManager进行识别,并通过ViewRootImpl将事件分发给当前激活的窗口。
-在应用窗口中有对应的PhoneWindow\DecorView实例,它们通过与InputManagerService通信来接收分发的事件。
(2)参与的系统组件有:SystemServer、WindowManager、InputManager等。

-InputEvent:输入事件。根据类型又可划分为KeyEvent和MotionEvent
-SystemServer:系统服务。由Zygote进程对齐进行初始化。SystemServer启动后对AMS、WMS、PMS等关键服务进行初始化。
-WindowManagerService:窗口管理服务。管理窗口添加、移除等操作。对InputManagerService初始化。
-InputManagerService:输入管理服务。启动了InputManager输入管理器。
-InputManager:输入管理器。与硬件交互识别转换输入事件。
-ViewRootImpl:作为整个控件树的根部,它是View树正常运作的动力所在,控件的测量、布局、绘制以及输入事件的分发都由ViewRootImpl控制。
ViewRootImpl作为链接WindowManager和DecorView的纽带,同时实现了ViewParent接口。

思考:代码事件是如何被分发?被处理?被拦截等?
答:Android系统的事件分发使用了递归传递的思想,通过责任链的方式将事件自顶向下传递,找到事件的消费者后,再自低向上返回结果。

由外到内传递,由内到外处理
事件传递:Activity>>ViewGroup>>View

三个重要的方法:
dispatchTouchEvent :分发事件
onInterceptTouchEvent:拦截事件
onTouchEvent:处理事件
1.当onInterceptTouchEvent返回ture时,若onTouchEvent返回true,后续事件将不再经过该ViewGroup的onInterceptTouchEvent方法,直接交由该ViewGroup的onTouchEvent方法处理;若onTouchEvent方法返回false,后续事件都将交由父ViewGroup处理,不再经过该ViewGroup的onInterceptTouchEvent方法和onTouchEvent方法。
2.当onInterceptTouchEvent返回false时,事件继续向子View分发;
3.对于子View,当onTouchEvent返回true,父ViewGroup派发过来的touch事件已被该View消费,后续事件不会再向上传递给父ViewGroup,后续的touch事件都将继续传递给子View。
4.对于子View,onTouchEvent返回false,表明该View并不消费父ViewGroup传递来的down事件,而是向上传递给父ViewGroup来处理;后续的move、up等事件将不再传递给该View,直接由父ViewGroup处理掉。
5.onTouch先于onTouchEvent调用,onClick事件是在onTouchEvent中ACTION_UP中触发的。

事件分发序列:
针对MotionEvent添加了一个Action以描述该事件的行为。
·ACTION_DOWN:手指触摸到屏幕
·ACTION_MOVE:手指在屏幕上移动
·ACTION_UP:手指离开屏幕
·其它ACTION_CANCEL...

当用户发生一次触摸屏幕的事件时,必然会产生一个事件序列,例如:ACTION_DOWN、ACTION_MOVE....ACTION_MOVE、ACTION_UP。其中ACTION_MOVE发生的次数不确定为0到n,
但是ACTION_DOWN和ACTION_UP发生次数则为1。当ACTION_DOWN事件从ViewGroup中分发到消费事件的子View中时,ViewGroup会保存该消费View,后续发生的ACTION_MOVE、ACTION_UP会直接跳过递归
将后续事件直接传递给保存的子View。

相关资料文章:
反思|Android 事件分发机制的设计与实现
关于反思系列(Thinking in Android)
浅谈Android事件分发机制
Android事件分发机制完全解析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容