Realm中文文档(翻译)

目录

Getting Started
Getting Help
Models
Relationships
Writes
Queries
Realms
Threading
Schemas
JSON
Notifications
Migrations
Encryption
Working With Android
Other Libraries
Testing and Debugging
Current Limitations
Best Practices
Recipes
FAQ
Api Reference

开始

realm-java文档链接

先决条件

我们现在只支持Android里面用到的Java语法
Android Studio >= 1.5.1
最新的Android sdk版本
JDK version >=7.
我们支持从API 9(2.3姜饼)以上的版本

安装

Realm 是被作为一个gradle插件来安装的
安装Realm为gradle插件需要2步

  • 第一步:加入下面class path dependency到工程目录下的build.gradle文件中
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath "io.realm:realm-gradle-plugin:1.1.0"
    }
}

工程目录下的build.gradle文件在这里


工程目录下的build.gradle
工程目录下的build.gradle
  • 第二步:在应用目录下的build.gradle文件中添加 realm-android 插件到application的顶部
apply plugin: 'realm-android'

application级别的build.gradle在这


应用目录下的build.gradle
应用目录下的build.gradle

一旦这两个有所改动,简单地刷新一下你的gradle dependencies就可以了。如果你从一个更高的版本到v0.88版本提升,那么你也需要clean一下你的gradle工程(命令./gradlew clean)来移除任何之前缓存的安装的东西

想查看两个文件build.gradle的简单例子在这里

其他build systems

maven 或者ant 构建系统,不支持。如果你想看关于对这些构建系统的支持,下面有,请向我们表明你的兴趣,我们会权衡而定

在是否实现ant和maven支持上,你的意见是很有决定性的

1.0.0版本之后,eclipse是不支持的,请你去用Android studio

混淆

一个混淆配置在中realm library已经支持。这就意味着你不需要加入任何realm 混淆规则进混淆文件配置里面。(意思就是说,你不用混淆了,少操这份心)

Realm Browser

我们提供一个提供了一个独立的叫Realm Brower 的mac app来读realm数据库文件(生成的.realm数据库文件,就可以用这个app来打开查看)

Realm Browser

你可以可以使用menu item tools->Generate demo databases集成一个带用例的测试数据库demo

如果你需要查找你app的realm 文件,可以去看StackOverflow的对具体相关说明的回答

Realm Brower在Mac app商店上面是有的,或者你可以从我们的Github网页跳过去下载

这个浏览器现在不能在Linux和window上面运行。在这些平台上你可以使用Stetho-Realm来代替。Stetho 是一个Android的Facebook创建的Google浏览器调试桥

API参考

你可以查阅我们所有的API参考的类、方法或者其他更多

例子

看一看我们的例子一边能够了解Realm在一个app中实际操作中的使用。你只需要用Android studio导入工程,运行就行了

introExample包含了用例和使用最新的api

gridviewExample是不太重要,只是为了向我们展示如何使用Realm作为gridview的后备数据库。它也向你展示如何用json和gson来写入数据库,如果使用ABI分叉来最小化你APK的大小

theadExample是一个简单的向我们展示Realm在多线程环境中怎么运行app

adapterExample向我们展示如何使用RealmBaseAdapterRealmRecyclerViewAdapter来让Realm和Android的ListViewRecyclerView一起使用变得优雅

jsonExample阐述了如何使用新的Realm Json工具

encryptionExample向你展示如何加密Realms

rxJavaExamlple向你展示Realm如何和Rxjava一起使用

unitTestExample展示了使用Realm怎么写单元测试

获得帮助

需要代码上的帮助么?去StackOverflow上面问吧。我们会很积极的查看和回答在上面的问题
有一个bug需要提交?提一个issue给我们。如果可能,我们会把它加入进去。一个完善的log,Realm文件,和一个工程都可以作为issue给我们看
有功能上的问题?提一个issue给我们,告诉我们哪些功能应该做,和为什么你想要这个功能
想follow我们接下来新的功能?看我们的changelog。这个log展示了最新加入的东西和我们即将要发布的东西,还有Realm的发展历程

模型

Realm model类的创建需要继承RealmObject基类

public class User extends RealmObject {

    private String          name;
    private int             age;

    @Ignore
    private int             sessionId;

    // Standard getters & setters generated by your IDE…
    public String getName() { return name; }
    public void   setName(String name) { this.name = name; }
    public int    getAge() { return age; }
    public void   setAge(int age) { this.age = age; }
    public int    getSessionId() { return sessionId; }
    public void   setSessionId(int sessionId) { this.sessionId = sessionId; }
}

一个Realm model类也支持publicprotectedprivate变量和自定义方法

public class User extends RealmObject {

    public String name;

    public boolean hasLongName() {
      return name.length() > 7;
    }

    @Override
    public boolean equals(Object o) {
      // Custom equals comparison
    }
}

变量类型

Field types
Realm支持这些类型:boolean, byte, short, int, long, float, double, String, Date and byte[]。在realm中,integer类型 byte、short、int和long其实都是最终映射为long类型。除此之外,RealmObject的子类和RealmList<? extends RealmObject>都支持模型类的关联(就是可以成员变量是关联的其他类,而不仅仅是基础类型)

包装类Boolean,Byte,Short,Integer,Long, Float and Double都可以在model类里面使用。使用这些类,也可以置空,设置值为null

非空变量和null值

在某些情况下,null不是一个恰当的变量值。@Required注解能够检测并报null值的错误。只有Boolean, Byte, Short, Integer, Long, Float, Double, String, byte[] and Date 能被@Required注解。当其他类型用@Required注解的时候编译会失败。主键类型的变量和RealmList类型的变量自身就是非空的、必要的(意思就是主键和RealmList肯定已经不能为空了,已经有Required属性了)。RealmObject的变量是可为空的

ignoring 属性

@Ignore注解表明一个变量不是必要的,是可有可无的。Ignore变量在你写入你model之外的其他属性而你又不想在太多特别的情况下去处理这些没有太大用处的属性的时候是很有用的(意思就是,那些是可忽略的,比如购物车里面每个东西的价格是必要的,但是总价就不是必要的,因为可以推算出来,那么总结这个属性就是可以ignore的,不需要写入数据库)

Auto-Updating Objects

RealmObject是动态的、自动更新到底层的数据去的,这意味着你不需要刷新,就能修改。影响查询的修改会被立马反射到结果中去。(这里涉及多线程的问题,一边在修改一边在查询,只要你这边修改了,那边插叙的结果就是最新修改的,这就是动态的、自动更新的意思)

realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        Dog myDog = realm.createObject(Dog.class);
        myDog.setName("Fido");
        myDog.setAge(1);
    }
});
Dog myDog = realm.where(Dog.class).equalTo("age", 1).findFirst();

realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        Dog myPuppy = realm.where(Dog.class).equalTo("age", 1).findFirst();
        myPuppy.setAge(2);
    }
});

myDog.getAge(); // => 2

所有RealmObject和RealmResults的性能不仅让Realm快和高效,而且还让你的代码变得更简单更灵活。例如,如果你的Activity或者Fragment关联了一个RealmObject或者RealmResult实例,在更新UI的时候你不需要担心刷新或者重新获取

你可以订阅Realm提醒,知道什么时候Realm数据被更新了,在你app UI应该被更新的时候提醒你

Indexing属性

Indexing properties

The annotation @Index will add a search index to the field. This will make inserts slower and the data file larger but queries will be faster. So it’s recommended to only add index when optimizing specific situations for read performance. We support indexing: String, byte, short, int, long, boolean and Date fields.

Primary keys
To promote a field to primary key, you use the annotation @PrimaryKey, and the field type has to be either string (String) or integer (byte, short, int, or long) and its boxed variants (Byte, Short, Integer, and Long). It is not possible to use multiple fields (compound key) as a primary key. Using a string field as a primary key implies that the field is indexed (the annotation @PrimaryKey implicitly sets the annotation @Index).

Using primary keys makes it possible to use the copyToRealmOrUpdate() method, which will look for an existing object with this primary key, and update it if one is found; if none is found, it will create a new object instead. When calling copyToRealmOrUpdate() on classes without primary keys, an exception will be thrown.

Using primary keys has an effect on the performance. Creating and updating object will be a little slower while querying is expected to be a bit faster. It is hard to give numbers as the changes in performance depend on the size of your dataset.

When calling Realm.createObject(), it will return a new object with all fields set to the default value. In this case, there might be a conflict with an existing object whose primary key field is the default value. To avoid this, it is suggested to create an unmanaged object, set values of the fields, and then copy it to Realm by copyToRealm() method.

final MyObject obj = new MyObject();
obj.setId(42);
obj.setName("Fish");
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
// This will create a new object in Realm or throw an exception if the
// object already exists (same primary key)
// realm.copyToRealm(obj);

    // This will update an existing object with the same primary key
    // or create a new object if an object with no primary key = 42
    realm.copyToRealmOrUpdate(obj);
}

});
For String (String) and boxed integer (Byte, Short, Integer, and Long), Primary keys can have the value null unless the @PrimaryKey annotation is additionally combined with @Required annotation.

Customizing Objects
It is possible to use RealmObjects almost like POJOs. Extending from RealmObject, you can let the fields be public, and use simple assignments instead of setters and getter. An example of such a model class is:

public class Dog extends RealmObject {
public String name;
public int age;
}
You can use Dog like any other class. In order to create a managed Dog object in Realm, you can use the createObject() or copyToRealm() methods.

realm.executeTransaction(new Realm.Transaction() {
@Overrride
public void execute(Realm realm) {
Dog dog = realm.createObject(Dog.class);
dog.name = "Fido";
dog.age = 5;
}
};
You can add logic to your setters and getters if that fits your needs better. This can be useful if you wish to validate values before storing them in your Realm. Moreover, you can easily add custom methods to your RealmObjects.

Limitations
Currently there’s no support for final, transient and volatile fields. This is mainly to avoid discrepancies between how an object would behave as managed by Realm or unmanaged.

Realm model classes are not allowed to extend any other object than RealmObject. If declared, the default constructor (constructor with no parameters) must always be empty. The reason is that a default contructor will call methods which assume a Realm instance is present. But that instance isn’t create before the contructor returns. You can add other constructors for your convienence.

RealmModel interface
An alternative to extending the RealmObject base class is implementing the RealmModel interface and adding the @RealmClass annotation.

@RealmClass
public class User implements RealmModel {

}
All methods available on RealmObject are then available through static methods.

// With RealmObject
user.isValid();
user.addChangeListener(listener);

// With RealmModel
RealmObject.isValid(user);
RealmObject.addChangeListener(user, listener);
Relationships

Any two RealmObjects can be linked together.

public class Email extends RealmObject {
private String address;
private boolean active;
// ... setters and getters left out
}

public class Contact extends RealmObject {
private String name;
private Email email;
// ... setters and getters left out
}
Relationships are generally cheap in Realm. This means that following a link is not expensive in terms of speed, and the internal presentation of relationships is highly efficient in terms of memory consumption.

Many-to-One
Simply declare a property with the type of one of you RealmObject subclasses:

public class Contact extends RealmObject {
private Email email;
// Other fields…
}
Each contact (instance of Contact) have either 0 or 1 email (instance of Email). In Realm, nothing prevent you from using the same email object in multiple contacts, and the model above can be a many-to-one relationship but often used to model one-to-one relationships.

Setting the RealmObject field to null will clear the reference but the object will not be deleted from the Realm.

Many-to-Many
You can establish a relationship to any number of objects from a single object via a RealmList<T> field declaration. For example, consider a contact with multiple email addresses:

public class Contact extends RealmObject {
public String name;
public RealmList<Email> emails;
}

public class Email extends RealmObject {
public String address;
public boolean active;
}
RealmLists are basically containers of RealmObjects, and a RealmList behaves very much like a regular Java List. There are no limitations in Realm to use the same object twice (or more) in different RealmLists, and you can use this to model both one-to-many, and many-to-many relationships.

You can create objects, and use RealmList.add() to add the Email objects to the Contact object:

realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
Contact contact = realm.createObject(Contact.class);
contact.name = "John Doe";

    Email email1 = realm.createObject(Email.class);
    email1.address = "john@example.com";
    email1.active = true;
    contact.emails.add(email1);

    Email email2 = realm.createObject(Email.class);
    email2.address = "jd@example.com";
    email2.active = false;
    contact.emails.add(email2);
}

});
It is possible to declare recursive relationships which can be useful when modeling certain types of data.

public class Person extends RealmObject {
public String name;
public RealmList<Person> friends;
// Other fields…
}
Setting the value to null for a RealmList field will clear the list. That is, the list will be empty (length zero), but no objects have been deleted. The getter for a RealmList will never return null. The returned object is always a list but the length might be zero.

Link queries
It is possible to query links or relationships. Consider the model below:

public class Person extends RealmObject {
private String id;
private String name;
private RealmList<Dog> dogs;
// getters and setters
}

public class Dog extends RealmObject {
private String id;
private String name;
private String color;
// getters and setters
}
Each Person object has multiple dog relationships as shown in this table diagram:

Table Diagram

Let’s find some persons with link queries …

// persons => [U1,U2]
RealmResults<Person> persons = realm.where(Person.class)
.equalTo("dogs.color", "Brown")
.findAll();
First of all, notice that the field name in the equalsTo condition contains the path through the relationships (separated by period .).

The query above should read, find all Persons who have dogs who are ‘Brown’. It is important to understand that the result will contain the Dog objects which do not fulfill the condition since they are part of the Person’s object:

persons.get(0).getDogs(); // => [A,B]
persons.get(1).getDogs(); // => [B,C,D]
This can be further examined by the following two queries.

// r1 => [U1,U2]
RealmResults<Person> r1 = realm.where(Person.class)
.equalTo("dogs.name", "Fluffy")
.findAll();

// r2 => [U1,U2]
RealmResults<Person> r2 = r1.where()
.equalTo("dogs.color", "Brown")
.findAll();
Notice how the first query returned both Person objects because the condition matched both persons. Each Person in the query result contains a list of Dog objects - all of their dog objects (even ones that do not fulfill the original query condition). Remember, we’re searching for people who have particular kinds of dogs (names and colors), not the actual dogs themselves. Therefore, the second query will be evaluated against the first Person query result (r1) and each of the Persons dogs. The second query also matches both persons as well, but this time it’s because of the color of the dog.

Let’s dig a little deeper to help solidify this concept. Please review the following example:

// r1 => [U1,U2]
RealmResults<Person> r1 = realm.where(Person.class)
.equalTo("dogs.name", "Fluffy")
.equalTo("dogs.color", "Brown")
.findAll();

// r2 => [U2]
RealmResults<Person> r2 = realm.where(Person.class)
.equalTo("dogs.name", "Fluffy")
.findAll()
.where()
.equalTo("dogs.color", "Brown")
.findAll();
.where()
.equalTo("dogs.color", "Yellow")
.findAll();
The first query should read, find all Persons who have dogs named ‘Fluffy’ and also find all Persons who have dogs who are ‘Brown’ and then give me the intersection of the two. The second query should read, find all Persons who have dogs named ‘Fluffy’. Then, given that result set, find all Persons who have dogs whose color is ‘Brown’ and given that result set find all Persons who have dogs whose color is ‘Yellow’.

Let’s take a look at the query behind r1 to fully understand what is happening. The two conditions are equalTo("dogs.name", "Fluffy") and equalTo("dogs.color", "Brown"). The first condition is fulfilled for U1 and U2 - this is set C1. The second condition is also fulfilled for U1 and U2 - this is set C2. The logical-and in the query is the same as an intersection of the two sets C1 and C2. The intersection between C1 and C2 is U1 and U2. Therefore, r1 is U1 and U2.

The query behind r2 is different. Let’s begin by breaking this query apart. The first portion of the query looks like this: RealmResults<Person> r2a = realm.where(Person.class).equalTo("dogs.name", "Fluffy").findAll();. It matches U1 and U2. Then, r2b = r2a.where().equalTo("dogs.color", "Brown").findAll(); also matches U1 and U2 (both persons have brown dogs). The final query, r2 = r2b.where().equalTo("dogs.color", "Yellow").findAll(); matches only U2 since the only person in the brown dog result set that has a Yellow dog is U2.

Writes

Read operations are implicit which means that objects can be accessed and queried at any time. All write operations (adding, modifying, and removing objects) must be wrapped in write transactions. A write transaction can either be committed or cancelled. During the commit, all changes will be written to disk, and the commit will only succeed if all changes can be persisted. By cancelling a write transaction, all changes will be discarded. Using write transactions, your data will always be in a consistent state.

Write transactions are also used to ensure thread safety:

// Obtain a Realm instance
Realm realm = Realm.getDefaultInstance();

realm.beginTransaction();

//... add or update objects here ...

realm.commitTransaction();
While working with your RealmObjects inside a write transaction, you might end up in a situation where you wish to discard the change. Instead of committing it, and then reverting it, you can simply cancel the write transaction:

realm.beginTransaction();
User user = realm.createObject(User.class);

// ...

realm.cancelTransaction();
Please note that write transactions block each other. This can cause ANR errors if you are creating write transactions on both the UI and background threads at the same time. To avoid this, use async transactions when creating write transactions on the UI thread.

Thanks to Realm’s MVCC architecture, reads are not blocked while a write transaction is open! This means that unless you need to make simultaneous transactions from many threads at once, you can favor larger transactions that do more work over many fine-grained transactions. When you commit a write transaction to a Realm, all other instances of that Realm will be notified, and be updated automatically.

Read & write access in Realm is ACID.

Creating objects
Because RealmObjects are strongly tied to a Realm, they should be instantiated through the Realm directly:

realm.beginTransaction();
User user = realm.createObject(User.class); // Create a new object
user.setName("John");
user.setEmail("john@corporation.com");
realm.commitTransaction();
Alternatively you can create an instance of an object first and add it later using realm.copyToRealm(). Realm supports as many custom constructors as you like as long as one of them is a public no arguments constructor.

User user = new User("John");
user.setEmail("john@corporation.com");

// Copy the object to Realm. Any further changes must happen on realmUser
realm.beginTransaction();
User realmUser = realm.copyToRealm(user);
realm.commitTransaction();
When using realm.copyToRealm() it is important to remember that only the returned object is managed by Realm, so any further changes to the original object will not be persisted.

Transaction blocks
Instead of manually keeping track of realm.beginTransaction(), realm.commitTransaction(), and realm.cancelTransaction() you can use the realm.executeTransaction() method, which will automatically handle begin/commit, and cancel if an error happens.

realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
User user = realm.createObject(User.class);
user.setName("John");
user.setEmail("john@corporation.com");
}
});
Asynchronous Transactions
As transactions are blocked by other transactions it can be an advantage to do all writes on a background thread in order to avoid blocking the UI thread. By using an asynchronous transaction, Realm will run that transaction on a background thread and report back when the transaction is done.

realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm bgRealm) {
User user = bgRealm.createObject(User.class);
user.setName("John");
user.setEmail("john@corporation.com");
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
// Transaction was a success.
}
}, new Realm.Transaction.OnError() {
@Override
public void onError(Throwable error) {
// Transaction failed and was automatically canceled.
}
});
OnSuccess and OnError callbacks are both optional, but if provided they will be called when the transaction completes successfully or fails, respectively. Callbacks are controlled by the Looper, so they are only allowed on Looper threads.

RealmAsyncTask transaction = realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm bgRealm) {
User user = bgRealm.createObject(User.class);
user.setName("John");
user.setEmail("john@corporation.com");
}
}, null);
An asynchronous transaction is represented by the RealmAsyncTask object. This object can be used to cancel any pending transaction if you are quitting the Activity/Fragment before the transaction is completed. Forgetting to cancel a transaction can crash the app if the callback updates the UI.

public void onStop () {
if (transaction != null && !transaction.isCancelled()) {
transaction.cancel();
}
}
Updating strings and byte arrays
Realm is working on entire fields, and it is not possible to update individual elements of strings or byte arrays. Suppose you need to update the 5th element of a string, you will have to do something like

realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
bytes[] bytes = realmObject.binary;
bytes[4] = 'a';
realmObject.binary = bytes;
}
});
This is due to Realm’s MVCC architecture which avoids mutating existing data in place to ensure that other threads or processes reading the data see it in a consistent state.

Queries

All fetches (including queries) are lazy in Realm, and the data is never copied.
Realm’s query engine uses a Fluent interface to construct multi-clause queries.

Using the User class -

public class User extends RealmObject {

@PrimaryKey
private String          name;
private int             age;

@Ignore
private int             sessionId;

// Standard getters & setters generated by your IDE…
public String getName() { return name; }
public void   setName(String name) { this.name = name; }
public int    getAge() { return age; }
public void   setAge(int age) { this.age = age; }
public int    getSessionId() { return sessionId; }
public void   setSessionId(int sessionId) { this.sessionId = sessionId; }

}
To find all users named John or Peter you would write:

// Build the query looking at all users:
RealmQuery<User> query = realm.where(User.class);

// Add query conditions:
query.equalTo("name", "John");
query.or().equalTo("name", "Peter");

// Execute the query:
RealmResults<User> result1 = query.findAll();

// Or alternatively do the same all at once (the "Fluent interface"):
RealmResults<User> result2 = realm.where(User.class)
.equalTo("name", "John")
.or()
.equalTo("name", "Peter")
.findAll();
This gives you a new instance of the class RealmResults, containing the users with the name John or Peter. Objects are not copied - you get a list of references to the matching objects, and you work directly with the original objects that matches your query. The RealmResults inherits from Java’s AbstractList, and behaves in similar ways. For example, RealmResults are ordered, and you can access the individual objects through an index.

When a query does not have any matches, the returned RealmResults object will not be null, but the size() method will return 0.

If you wish modify or delete any of the objects in a RealmResults, you must do so in a write transaction.

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

推荐阅读更多精彩内容