IPC機制總結(jié)

本文基于《Android藝術(shù)探索》中知識的總結(jié)而創(chuàng)


1 簡介

1.1 簡述

IPC 進程間通訊或跨進程通訊

  • android中IPC方式 Binder(AIDL)、Socket孔飒、Bundle鸦概、文件共享蒙挑、Messenger俯抖、ContentProvider
  • IPC的基礎場景是多進程:一般由兩種颊糜,應用內(nèi)多進程模式拨与,應用間數(shù)據(jù)共享

1.2 進程與線程

  • 線程是cpu調(diào)度的最小單位稻据,是一種有限的系統(tǒng)資源
  • 進程指一個執(zhí)行單元,是一個獨立的應用或者程序
  • 一個進程可包含多個線程
  • ANR 主線程無響應买喧,以多線程解決

2 多進程模式

2.1 開啟

  • 一般只有一種方法 就是在AndroidMenifest文件中給四大組件指定android:process屬性捻悯。
    當然也可以通過JNI的natice層去fork匆赃。但是很少這么玩
  • 查看當前進程的命令:adb shell ps | grep com.gjg.text
  • 進程名以":"開頭的進程屬于應用的私有進程,其它應用的組件不可以和它泡在同一個進程中秋度,非":"開頭的進程屬于全局進程炸庞,其它應用可以要通過ShareUID方式(ShareUID相同且簽名相同)和它跑在同一個進程中谨设。

2.2 多進程模式運行機制

不同的進程有獨立的虛擬機宜雀,Applicaition以及內(nèi)存空間简软,所以開啟了多進程模式的應用應注意一下問題:

1 靜態(tài)成員和單利模式完全失效
2 線程同步機制完全失效
3 SharedPreferences的可靠性下降
4 Application會多次創(chuàng)建

3 IPC基礎載體

3.1 Serializable

  • serialVersionUID:用來輔助序列化和反序列化過程的璧亮,原則上序列化后的數(shù)據(jù)中的serialVersionUID只有和當前類的serialVersionUID相同才能夠被正常的反序列化交惯。
  • 系統(tǒng)默認用hash值作為serialVersionUID,當變量數(shù)和類型改變時溃睹,hash會變化拌阴,所以系統(tǒng)默認情況下這種改變發(fā)生時反序列化會crash
public class User implements Serializable{
}
//序列化過程
User user = new User();
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
out.writeObject(user);
out.close();
//反序列化過程
ObjectInputStream in = new ObjectInputStream(new FileInputStream("cache.txt"));
User user = (User)in.readObject();
in.close();

3.2 Parcelable

public class User implements Parcelable{
    private String name;
    private int age;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //反序列化
    protected User(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

    //用于反序列化的創(chuàng)建器
    public static final Creator<User> CREATOR = new Creator<User>() {
        //從序列化的數(shù)據(jù)中創(chuàng)建原始對象
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }
        //創(chuàng)建指定長度的原始對象數(shù)組
        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    /**
     * 返回當前對象的內(nèi)容描述
     * 如果含有文件描述符朽们,返回1(CONTENTS_FILE_DESCRIPTOR)兽泣,否則返回0
     * 多數(shù)都返回0
     * @return
     */
    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * 序列化操作
     * @param dest
     * @param flags
     *  當為1(PARCELABLE_WRITE_RETURN_VALUE) 標示當前對象需要作為返回值返回绎橘,不能立即釋放資源
     *  幾乎所有情況都為0
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
    }
    /**
     * Parcel
     * 內(nèi)部包裝了可序列化的數(shù)據(jù)的native操作,例如
     *   @FastNative
     *   private static native void nativeWriteInt(long nativePtr, int val);
     *   @FastNative
     *   private static native void nativeWriteLong(long nativePtr, long val);
     */
}
  • Serializabble 是java中的序列化接口唠倦,使用簡單但開銷大称鳞,序列化和反序列化過程需要大量IO操作,適用于序列化到設備或者網(wǎng)絡傳輸中
  • Parcelable 是android 中的序列化方式稠鼻,使用麻煩但是效率高冈止,主要用于內(nèi)存序列化上。

3.3 Binder

(1)Binder 繼承在IBinder候齿。是IPC的框進程通訊方式
(2)還可以理解為一種虛擬的物理設備
(3)從android framework層來說Binder是ServiceManager連接各種Manager(ActivityManager WindowManager)和響應ManagerService的橋梁熙暴。
(4)從android 應用層來說是客戶端和服務端進行通訊的媒介。當bindService時慌盯,返回段會返回一個包含服務端業(yè)務調(diào)用的Binder對象周霉。

AIDL創(chuàng)建及核心源碼解析

創(chuàng)建AIDL文件

//1.創(chuàng)建User.java 同上
//2.創(chuàng)建User在AIDL中的聲明文件 User.aidl
package gjg.com.fundemo.dbg;
parcelable User;
//3.創(chuàng)建User的管理aidl文件
package gjg.com.fundemo.dbg;
//這里要注意使用全包名,且User有對應的User.aidl文件亚皂,否則會提示找不到
//aidl只支持方法生命俱箱,不支持靜態(tài)變量
import gjg.com.fundemo.dbg.User;
interface IUserManager {
    List<User> getUserList();
   /**
    * in 輸入型參數(shù)
    * out 輸出型參數(shù)
    * inout 輸入輸出型參數(shù)
    */
    void addUser(in User user);
}
//4.重新編譯后系統(tǒng)會生成IUserManager.java

package gjg.com.fundemo.dbg;

public interface IUserManager extends android.os.IInterface {
    /**
     * 集成Binder 實現(xiàn)了IUserManager接口本身
     * 這是個抽象方法,并沒有實現(xiàn)IUserManager具體方法孕讳,需要在服務端自定義Binder時實現(xiàn)
     * 一般在服務端繼承自改抽象類匠楚,并在onBind中返回其實現(xiàn)
     */
    public static abstract class Stub extends android.os.Binder implements gjg.com.fundemo.dbg.IUserManager {
        //Binder的唯一標示
        private static final java.lang.String DESCRIPTOR = "gjg.com.fundemo.dbg.IUserManager";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * 用于將服務端的Binder對象轉(zhuǎn)換成客戶端所需的AIDL接口類型的對象
         */
        public static gjg.com.fundemo.dbg.IUserManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            //如果客戶端和服務端在同一進程,返回的就是服務端的Stub對象本身Stub.Proxy對象
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof gjg.com.fundemo.dbg.IUserManager))) {
                return ((gjg.com.fundemo.dbg.IUserManager) iin);
            }
            //如果客戶端和服務端不在同一進程厂财,返回的是封裝后的
            return new gjg.com.fundemo.dbg.IUserManager.Stub.Proxy(obj);
        }

        /**
         * 用于返回當前Binder對象芋簿,在IInterface中定義的
         */
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        /**
         * 該方法運行在服務端中Binder線程池中
         * 當客戶端發(fā)起請求時,遠程請求會通過系統(tǒng)底層封裝后交由此方法來處理
         * 只有跨進程時才會調(diào)用此方法
         * @param code
         *  服務端通過code可以確定所請求的目標方法
         * @param data
         *  data中存有目標方法所需的參數(shù)
         * @param reply
         *  當目標方法執(zhí)行完后會將返回值寫入reply中
         * @param flags
         * @return 返回false 證明客戶端請求失敗
         * @throws android.os.RemoteException
         */
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getUserList: {
                    data.enforceInterface(DESCRIPTOR);
                    //調(diào)用服務端方法得到返回值
                    java.util.List<gjg.com.fundemo.dbg.User> _result = this.getUserList();
                    reply.writeNoException();
                    //返回值寫入reply
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addUser: {
                    data.enforceInterface(DESCRIPTOR);
                    gjg.com.fundemo.dbg.User _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = gjg.com.fundemo.dbg.User.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    //調(diào)用服務端方法得到返回值
                    this.addUser(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        /**
         * 如果是跨進程通訊璃饱,該代理會被返回
         */
        private static class Proxy implements gjg.com.fundemo.dbg.IUserManager {
            //這個mRemote就是服務端繼承自Stub的Binder對象
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            //在客戶端調(diào)用与斤,最終會通過transact調(diào)服務端方法
            @Override
            public java.util.List<gjg.com.fundemo.dbg.User> getUserList() throws android.os.RemoteException {
                //創(chuàng)建所需的輸入型Parcel對象
                android.os.Parcel _data = android.os.Parcel.obtain();
                //創(chuàng)建輸出型Parcel對象
                android.os.Parcel _reply = android.os.Parcel.obtain();
                //創(chuàng)建返回值對象
                java.util.List<gjg.com.fundemo.dbg.User> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    //發(fā)起RPC(遠程過程調(diào)用)請求,同時當前線程掛起
                    //transact被調(diào)用,知道RPC過程返回后撩穿,當前線程繼續(xù)執(zhí)行
                    //從reply中取出RPC過程的返回結(jié)果
                    mRemote.transact(Stub.TRANSACTION_getUserList, _data, _reply, 0);
                    _reply.readException();
                    //返回reply中的結(jié)果磷支,反序列化,這里可以看出AIDL的對象需要細線parcelable接口
                    _result = _reply.createTypedArrayList(gjg.com.fundemo.dbg.User.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addUser(gjg.com.fundemo.dbg.User user) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((user != null)) {
                        _data.writeInt(1);
                        //將參數(shù)寫入輸出型parcel對象中
                        user.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addUser, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getUserList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }
    //接口的方法定義
    public java.util.List<gjg.com.fundemo.dbg.User> getUserList() throws android.os.RemoteException;
    //接口的方法定義
    public void addUser(gjg.com.fundemo.dbg.User user) throws android.os.RemoteException;
}
// 5.給Binder設置死亡代理
// 當服務端Binder連接斷裂時食寡,客戶端可以收到通知雾狈,一遍再次發(fā)起請求從而恢復連接的解決方案
//示例代碼如下
//服務端Service
public class UserRemoteService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
    //返回個客戶端的Binder
    IUserManager.Stub binder = new IUserManager.Stub() {

        @Override
        public List<User> getUserList() throws RemoteException {
            return null;
        }

        @Override
        public void addUser(User user) throws RemoteException {

        }
    };
}
public class UserTestActivity extends AppCompatActivity {
    //客戶端對IUserManager
    private IUserManager mUserManager;
    //服務端Binder死亡代理對象
    private DeathRecipient mDeathRecipient = new DeathRecipient();
    private ServiceConnection mSc = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //根據(jù)服務端Binder創(chuàng)建客戶端IUserManager對象
            mUserManager = IUserManager.Stub.asInterface(service);
            try {
                //綁定服務端Binder死亡代理
                service.linkToDeath(mDeathRecipient,0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = new Intent(this,UserRemoteService.class);
        //綁定服務
        bindService(intent,mSc,0);
        if(mUserManager != null){
            try {
                List<User> users = mUserManager.getUserList();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Binder死亡代理
     * 一般在bindService中Connection后創(chuàng)建并設置
     * IUserManager mServiceBinder = IUserManager.Stub.asInterface(binder);
     * binder.linkToDeath(this,0);
     */
    public  class DeathRecipient implements IBinder.DeathRecipient {
        //當Binder死亡是會回調(diào)此方法
        @Override
        public void binderDied() {
            if(mUserManager == null){
                return;
            }
            //解綁服務端Binder死亡代理對象
            mUserManager.asBinder().unlinkToDeath(this,0);
            mUserManager = null;
        }
    }
}

4 Android中的IPC方式

4.1 Bundle

Bundle implements Parcelable 所以可以方便的在不同進程進行通訊

4.2 使用文件共享

適合同步要求不高的進程間通訊

4.3 Messenger

是對AIDL的簡單封裝,以Message作為載體

4.4 AIDL

流程:
-->服務端創(chuàng)建AIDL文件抵皱,編譯生成接口善榛,服務端實現(xiàn)接口的.Stub返回
-->客戶端綁定Service,綁定成功后呻畸,將服務端返回的Binder對象轉(zhuǎn)成AIDL文件對應的接口對象
注意:
aidl文件中不是所有類型都支持移盆,支持的類型如下:

基本數(shù)據(jù)類型
String CharSequence
List-ArrayList,里面的每個元素必須被aidl支持,這里指服務端最終會返回ArrayList類型
Map-HashMap伤为,里面的每個元素必須被aidl支持
Parcelable咒循,所有實現(xiàn)了Parcelable接口的對象
AIDL:所有aidl接庫本身也可以在aidl文件中使用

自定義的Parcelable對象,在aidl中使用時绞愚,必須新建一個和它同名的AIDL文件叙甸,并在其中生命它為parcleable類型。例如:

package com.gjg
parcelable User;

RemoteCallbackList

  • 跨進程傳輸對象的最想都會經(jīng)過反序列化位衩,也就是說兩端針對相同內(nèi)容的對象卻不是真正的同一個對象蚁署,所以就會導致跨進程解注冊時會出現(xiàn)問題,而RemoteCallbackList就為解決此問題而生蚂四。
  • RemoteCallbackList是專門提供的用于刪除款進程listener的接口,它是一個泛型哪痰,支持管理任意的aidl接口,其聲明如下:
    public class RemoteCallbackList<E extends IInterface>
    其本質(zhì)就是利用了跨進程對象的相同對象在兩端擁有相同的底層Binder對象

安全驗證遂赠,在androidMenifest中聲明,可在服務端onBind或者onTransact中進行驗證是否有權(quán)限

4.5 ContentProvider

  • 底層也是Binder機制
  • 六個方法 onCreate() getType() CRUD
  • CRUD 在Binder線程池
  • android:authorities 聲明 ContentResolver中Uri參數(shù)與之配對
  • UriMatcher 實現(xiàn)外界訪問的匹配功能

4.6 Socket

通過網(wǎng)絡了晌杰。不光能跨進程跷睦,還能跨設備

5 Binder連接池

  • 背景有很多aidl時不可能對應創(chuàng)建很多Service
  • Binder連接池的作用就是將每個業(yè)務模塊的Binder請求統(tǒng)一轉(zhuǎn)發(fā)到遠程的Service中去執(zhí)行,從而避免重復創(chuàng)建Service的過程
//1. 創(chuàng)建兩個簡單aidl文件
package gjg.com.fundemo.binderchi;

interface ICompute {
    int add(int a,int b);
}
package gjg.com.fundemo.binderchi;

interface ISecurityCenter {
    String encrypt(String content);
    String decrypt(String password);
}
//2. 創(chuàng)建aidl文件對應的實現(xiàn)
package gjg.com.fundemo.binderchi;

import android.os.RemoteException;

public class SecurityCenerImpl extends ISecurityCenter.Stub {
    @Override
    public String encrypt(String content) throws RemoteException {
        return null;
    }

    @Override
    public String decrypt(String password) throws RemoteException {
        return null;
    }
}

package gjg.com.fundemo.binderchi;

public class ComputeImpl extends ICompute.Stub {
    @Override
    public int add(int a, int b) throws RemoteException {
        return 0;
    }
}

//3. 創(chuàng)建IBinderPool的aidl文件
package gjg.com.fundemo.binderchi;

interface IBinderPool {
    IBinder queryBinder(int binderCode);
}

//4. 創(chuàng)建IBinderPool的實現(xiàn)肋演,作為BinderPool的內(nèi)部類
public static class BinderPoolImpl extends IBinderPool.Stub{

        @Override
        public IBinder queryBinder(int binderCode) throws RemoteException {
            IBinder binder = null;
            switch (binderCode){
                case BINDER_SECURITY_CENTER:
                    binder = new SecurityCenerImpl();
                    break;
                case BINDER_COMPUTE:
                    binder = new ComputeImpl();
                    break;
            }
            return binder;
        }
    }
//5. 創(chuàng)建Binder連接池的Service
public class BinderPoolService extends Service {
    private BinderPool.BinderPoolImpl mBinderPoolImpl = new BinderPool.BinderPoolImpl();
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //返回的是服務端的BinderPoolImpl
        return mBinderPoolImpl;
    }
}
// 6. 創(chuàng)建BinderPool
public class BinderPool {
    public static final int BINDER_SECURITY_CENTER = 1;
    public static final int BINDER_COMPUTE = 2;

    private Context mContext;
    private IBinderPool mBinderPool;
    private static volatile BinderPool sInstance;
    private CountDownLatch mCountDownLatch;//用于保證線程同步
    private BinderPool(Context context){
        this.mContext = context;
        connectBinderPoolService();
    }

    public static BinderPool getsInstance(Context context){
        if(sInstance == null){
            synchronized (BinderPool.class){
                sInstance = new BinderPool(context);
            }
        }
        return sInstance;
    }
    public IBinder queryBinder(int binderCode){
        IBinder iBinder = null;
        if(mBinderPool != null){
            try {
                mBinderPool.queryBinder(binderCode);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        return iBinder;
    }
    private void connectBinderPoolService() {
        mCountDownLatch = new CountDownLatch(1);
        Intent service = new Intent(mContext,BinderPoolService.class);
        mContext.bindService(service,mBinderPoolConnection,Context.BIND_AUTO_CREATE);
        try {
            mCountDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //連接
    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinderPool  = IBinderPool.Stub.asInterface(service);
            try {
                mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient,0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mCountDownLatch.countDown();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    /**
     * Binder死亡監(jiān)控
     */
    private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient,0);
            mBinderPool = null;
            connectBinderPoolService();
        }
    };
    public static class BinderPoolImpl extends IBinderPool.Stub{

        @Override
        public IBinder queryBinder(int binderCode) throws RemoteException {
            IBinder binder = null;
            switch (binderCode){
                case BINDER_SECURITY_CENTER:
                    binder = new SecurityCenerImpl();
                    break;
                case BINDER_COMPUTE:
                    binder = new ComputeImpl();
                    break;
            }
            return binder;
        }
    }
}
// 7. 使用
public class BinderPoolTestActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        doWork();
    }

    private void doWork() {
        BinderPool binderPool = BinderPool.getsInstance(this.getApplicationContext());
        IBinder securityCenterBinder = binderPool.queryBinder(BinderPool.BINDER_SECURITY_CENTER);
        ISecurityCenter securityCenter = SecurityCenerImpl.asInterface(securityCenterBinder);
        try {
            securityCenter.encrypt("");
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        IBinder computeBinder = binderPool.queryBinder(BinderPool.BINDER_COMPUTE);
        ICompute compute = ComputeImpl.asInterface(computeBinder);
        try {
            compute.add(1,2);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抑诸,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子爹殊,更是在濱河造成了極大的恐慌蜕乡,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梗夸,死亡現(xiàn)場離奇詭異层玲,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門辛块,熙熙樓的掌柜王于貴愁眉苦臉地迎上來畔派,“玉大人,你說我怎么就攤上這事润绵∠咭” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵尘盼,是天一觀的道長憨愉。 經(jīng)常有香客問我,道長悔叽,這世上最難降的妖魔是什么莱衩? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮娇澎,結(jié)果婚禮上笨蚁,老公的妹妹穿的比我還像新娘。我一直安慰自己趟庄,他們只是感情好括细,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著戚啥,像睡著了一般奋单。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上猫十,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天览濒,我揣著相機與錄音,去河邊找鬼拖云。 笑死贷笛,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的宙项。 我是一名探鬼主播乏苦,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼尤筐!你這毒婦竟也來了汇荐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤盆繁,失蹤者是張志新(化名)和其女友劉穎掀淘,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體油昂,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡繁疤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年咖为,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稠腊。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡躁染,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出架忌,到底是詐尸還是另有隱情吞彤,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布叹放,位于F島的核電站饰恕,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏井仰。R本人自食惡果不足惜埋嵌,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望俱恶。 院中可真熱鬧雹嗦,春花似錦、人聲如沸合是。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽聪全。三九已至泊藕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間难礼,已是汗流浹背娃圆。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蛾茉,地道東北人踊餐。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像臀稚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子三痰,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

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