深入分析PMS服務(wù)(一)

在前面兩篇自己動手編譯最新Android源碼自己動手調(diào)試Android源碼中,我們掌握了Android源碼的編譯以及調(diào)試,現(xiàn)在呢,我們在這基礎(chǔ)上分析一些源碼的實現(xiàn).首先從PMS服務(wù)開始.

PMS服務(wù)即PackageManagerService,主要用來進(jìn)行APK的管理任務(wù).但是今天,我們并不直接分析PMS的源碼,而是從一個工具類PackageParse說起.

首先來認(rèn)識PackageParser類,它主要用來解析手機上的apk文件(支持Single APK和Multiple APK),主要包含兩個過程

  • APK->Package:解析APK文件為Package對象的過程
  • Package->PackageInfo:由Package對象生成PackageInfo的過程

介于不少童鞋不了解Single APK和Multiple APK,在這里做個簡單解釋:

Single APK是我們通常所開發(fā)的APK,即一個應(yīng)用只有一個apk文件.而Google Play還允許你為一個應(yīng)用中發(fā)布不同的apk文件,這些apk文件適用于不同設(shè)備.舉例說明,假設(shè)你現(xiàn)在開發(fā)了一款A(yù)PP叫做Monkey,但是目前該APP由于體積太大或者其他因素導(dǎo)致不能同時適用于手機和平板,此時你就可將原先的Monkey.apk拆分為了Monkey-Phone.apk和Monkey-Tablet,分別用于運行在Android手機和Android平板,只要保存兩者擁有相同的包名,并用相同key進(jìn)行簽名就可以在發(fā)布Monkey應(yīng)用的時候,一同發(fā)布Monkey-Phone.apk和Moneky-Tablet.apk,那么這種一個應(yīng)用擁有多個APK文件的程序就稱之為Multiple APK.
更多信息查看官網(wǎng):multiple-apks


解析APK文件為Package對象

該過程目的是通過解析磁盤上的APK文件來生成與之相關(guān)的Package對象.而Pakcage對象是APK經(jīng)過完整解析之后的結(jié)果.

該過程主要涉及到一系列parseXXX()格式的方法,起始方法是public Package parsePackage(File packageFile, int flags),那么我們就從該方法開始分析其流程:

public Package parsePackage(File packageFile, int flags) throws PackageParserException {
        if (packageFile.isDirectory()) {
            //多個apk文件的目錄
            return parseClusterPackage(packageFile, flags);
        } else {
            //單一APK文件
            return parseMonolithicPackage(packageFile, flags);
        }
    }

該方法接受兩個參數(shù)packageFile和flags.并根據(jù)packageFile是否是文件目錄來確定具體的解析流程.通常我們都是Single APK,因此我們重點關(guān)注Single APK的解析,不難發(fā)現(xiàn)其具體解析過程給委托給parseMonolithicPackage(packageFile, flags)方法:

 public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
        //如果是核心應(yīng)用則以更輕量級的方式進(jìn)行解析后,判斷是否是核心應(yīng)用,非核心應(yīng)用不執(zhí)行解析過程
        if (mOnlyCoreApps) {
            final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
            if (!lite.coreApp) {
                throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                        "Not a coreApp: " + apkFile);
            }
        }
        
        final AssetManager assets = new AssetManager();
        try {
            //調(diào)用parseBaseAPK()繼續(xù)解析操作
            final Package pkg = parseBaseApk(apkFile, assets, flags);
            pkg.codePath = apkFile.getAbsolutePath();
            return pkg;
        } finally {
            IoUtils.closeQuietly(assets);
        }
    }

在該方法中首先通過mOnlyCoreApps屬性判斷當(dāng)前系統(tǒng)是不是只解析核心APK,默認(rèn)是全部解析.至于什么是核心APK后面再說.現(xiàn)在我們繼續(xù)關(guān)注其解析過程.
這里其解析操作繼續(xù)由parseBaseApk(apkFile, assets, flags)完成:

 private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
            throws PackageParserException {
                ...
        final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
        Resources res = null;
        XmlResourceParser parser = null;
        try {
            res = new Resources(assets, mMetrics, null);
            assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                    Build.VERSION.RESOURCES_SDK_INT);
            //為AndroidManifest.xml生成xml文件解析器
            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);

            final String[] outError = new String[1];
            final Package pkg = parseBaseApk(res, parser, flags, outError);
                ...
            pkg.baseCodePath = apkPath;
            pkg.mSignatures = null;

            return pkg;

        } 
                ...
    }

而真正的解析又是通過該方法的同名函數(shù):parseBaseApk(Resources res, XmlResourceParser parser, int flags,String[] outError)完成的,為了突出重點,我對其方法進(jìn)行了簡化:

    private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
            String[] outError) throws XmlPullParserException, IOException {
       
             //....省略多行代碼....
             
       //循環(huán)解析AndroidManifest.xml中的元素
       while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
                if (tagName.equals("application")) {
                    //....省略多行代碼,重點關(guān)注其中調(diào)用的parseBaseApplication()方法
                }else if (tagName.equals("overlay")) {
                    //....省略多行代碼....
                }else if (tagName.equals("key-sets")){
                    //paseKeySets()
                }else if (tagName.equals("permission-group")) {
                    parsePermissionGroup(pkg, flags, res, parser, attrs, 
                }else if (tagName.equals("permission")) {
                    //parsePermission
                }else if (tagName.equals("uses-configuration")) {
                    //....省略多行代碼....
                }else if (tagName.equals("uses-feature")) {
                    //parseUsesFeature()
                }else if (tagName.equals("feature-group")) {
                    //....省略多行代碼....
                }else if (tagName.equals("uses-sdk")) {
                    //....省略多行代碼....
                }else if (tagName.equals("supports-screens")) {
                    //....省略多行代碼....
                }else if (tagName.equals("protected-broadcast")) {
                    //....省略多行代碼....
                }else if (tagName.equals("instrumentation")) {
                    //....省略多行代碼....
                }else if (tagName.equals("original-package")) {
                    //....省略多行代碼....
                }else if (tagName.equals("adopt-permissions")) {
                    //....省略多行代碼....
                }
              //....省略多行代碼....
            }
        //....省略多汗代碼....
    }


不難發(fā)現(xiàn)這里通過很多parseXXX()方法解析相應(yīng)的數(shù)據(jù),比如:parseBaseApplication(),parseKeySets(),parsePermissionGroup(),parseUsesPermission()等等.
下面,我們重點關(guān)注Application標(biāo)簽的解析,即:parseBaseApplication()方法:

 private boolean parseBaseApplication(Package owner, Resources res,
            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
        throws XmlPullParserException, IOException {

        //....省略對Application元素屬性解析多行代碼....
        
    //解析Application下的子元素結(jié)點,如activity,receiver,service等    
    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
             if (tagName.equals("activity")) {
                //....省略多行代碼,主要關(guān)注parseActivity()....
             } else if (tagName.equals("receiver")) {
                //....省略多行代碼,主要關(guān)注parseActivity()....
             }else if (tagName.equals("service")) {
                //....省略多行代碼,主要關(guān)注parseService()....
             }else if (tagName.equals("provider")) {
                //....省略多行代碼,主要關(guān)注parseProvider()....
             }else if (tagName.equals("activity-alias")) {
                //....省略多行代碼,主要關(guān)注parseActivityAlias()...
             }else if (parser.getName().equals("meta-data")) {
                //....省略多行代碼,重點關(guān)注parseMetaData()....
             }else if (tagName.equals("library")) {
                //....省略多行代碼....
             }else if (tagName.equals("uses-library")) {
                //....省略多行代碼....
             }else if (tagName.equals("uses-package")) {
                //....省略多行代碼....
             }else{
                //....省略多行代碼....
             }
                 
         }
        
       return true;      
        
}

在解析Application下子元素結(jié)點時,同樣也是通過很多parseXXX()方法來完成的.比如在解析activity結(jié)點時是通過parseActivity()來完成的,其余自行查閱代碼.

另外你可能已經(jīng)注意到對receiver的解析也是通過parseActivity()實現(xiàn)的.

到此為止,整個為止,解析的整個流程完成,并返回一個Package對象.

附:PackageParser中所有相關(guān)解析方法

這里寫圖片描述


由Package對象生成PackageInfo

該過程的目的是從Package中提取相關(guān)屬性,并封裝成PackageInfo類型的對象.

該過程主要涉及到一系列generateXXXInfo()格式的方法,起始方法是generatePackageInfo(),那么我們就從該方法開始分析其流程:

 public static PackageInfo generatePackageInfo(PackageParser.Package p,
            int gids[], int flags, long firstInstallTime, long lastUpdateTime,
            ArraySet<String> grantedPermissions, PackageUserState state) {

        return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
                grantedPermissions, state, UserHandle.getCallingUserId());
    }

不難看出這里由調(diào)用了其同名方法generatePackageInfo(PackageParser.Package p, int gids[], int flags, long firstInstallTime, long lastUpdateTime, ArraySet<String> grantedPermissions, PackageUserState state, int userId)來進(jìn)行繼續(xù)解析工作:

public static PackageInfo generatePackageInfo(PackageParser.Package p,
            int gids[], int flags, long firstInstallTime, long lastUpdateTime,
            ArraySet<String> grantedPermissions, PackageUserState state, int userId)
        
        if (!checkUseInstalledOrHidden(flags, state)) {
            return null;
        }
        //從Package對象p中取出一系列的屬性值用來初始化pi
        PackageInfo pi = new PackageInfo();
        pi.packageName = p.packageName;
        pi.splitNames = p.splitNames;
        pi.versionCode = p.mVersionCode;
        pi.baseRevisionCode = p.baseRevisionCode;
        pi.splitRevisionCodes = p.splitRevisionCodes;
        pi.versionName = p.mVersionName;
        pi.sharedUserId = p.mSharedUserId;
        pi.sharedUserLabel = p.mSharedUserLabel;
        pi.applicationInfo = generateApplicationInfo(p, flags, state, userId);
        pi.installLocation = p.installLocation;
        pi.coreApp = p.coreApp;
        if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0
                || (pi.applicationInfo.flags&ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
            pi.requiredForAllUsers = p.mRequiredForAllUsers;
        }
        pi.restrictedAccountType = p.mRestrictedAccountType;
        pi.requiredAccountType = p.mRequiredAccountType;
        pi.overlayTarget = p.mOverlayTarget;
        pi.firstInstallTime = firstInstallTime;
        pi.lastUpdateTime = lastUpdateTime;
        if ((flags&PackageManager.GET_GIDS) != 0) {
            pi.gids = gids;
        }
        
        if ((flags&PackageManager.GET_CONFIGURATIONS) != 0) {
            //....省略多行代碼....
        }
        
        if ((flags&PackageManager.GET_ACTIVITIES) != 0) {
            //....省略多行代碼,關(guān)注generateActivityInfo()....
        }
        
        if ((flags&PackageManager.GET_RECEIVERS) != 0) {
           //....省略多行代碼,關(guān)注generateActivityInfo()....
        }
        
        if ((flags&PackageManager.GET_SERVICES) != 0) {
            //....省略多行代碼,關(guān)注generateServiceInfo()....
        }
        
        if ((flags&PackageManager.GET_PROVIDERS) != 0) {
            //....省略多行代碼,關(guān)注generateProviderInfo()....
        }
        
        if ((flags&PackageManager.GET_INSTRUMENTATION) != 0) {
           //....省略多行代碼,關(guān)注generateInstrumentationInfo()....
        }
        
        if ((flags&PackageManager.GET_PERMISSIONS) != 0) {
            //....省略多行代碼,generatePermissionInfo....
        }
        
        if ((flags&PackageManager.GET_SIGNATURES) != 0) {
           //....省略多行代碼....
        }
        return pi;
    }

上面的過程主要從Package對象取出一系列的屬性用來初始化PackageInfo對象,該過程不再涉及磁盤文件的解析操作.
和解析過程相對,該過程借助了很多generateXXXInfo()方法來實現(xiàn).在解析過程中對于Application元素的解析提供了parseApplication(),而在該過程中也提供了generateApplicationInfo()來實現(xiàn)Application的取值操作

附:PackageParser中所有相關(guān)的generate方法

這里寫圖片描述

中途小結(jié)

到現(xiàn)在為止,我們已經(jīng)了解Package的生成和PackageInfo生成,不難發(fā)現(xiàn)Package的生成是以磁盤APK文件作為輸入,而PackageInfo是以Package對象作為輸入.得益于Google工程師良好的設(shè)計,PackageParse具有非常好的對稱性,非常容易理解.在這里,我只是簡單的介紹了該類,對于具體的操作并沒有深入的說明,其原因在于,其核心就是通過使用Pull Parser對xml文件進(jìn)行解析的操作.

附:PackageParser所有內(nèi)部類:


這里寫圖片描述

(細(xì)心的同學(xué)已經(jīng)發(fā)現(xiàn)在上面所示的內(nèi)部類中也存在Activity,Service等類,要注意這些并不是我們平常使用的Activity組件.)


相關(guān)實體類

接下來,我們來介紹與上述過程相關(guān)的幾個實體類,以便你有一個宏觀的認(rèn)識,從而為理解后面的PMS打下基礎(chǔ).
對于這幾個實體類,我們值做簡單的說明,其具體的點還是需要我們自己進(jìn)行深究.

Package

PackageParser的靜態(tài)內(nèi)部類,代表磁盤上APK文件完整解析后的對象,相當(dāng)于在內(nèi)存中Package的對象是對磁盤APK的描述.這里我們只需要關(guān)注其屬性即可,大部分屬性對你而來都是很熟悉的:

public final static class Package {
        
        public String packageName;

        /** Names of any split APKs, ordered by parsed splitName */
        public String[] splitNames;
        
        //apk文件在磁盤的路徑.可能是一個apk的路徑,也可能是包含多個apk文件的目錄
        public String codePath;

        /** Path of base APK */
        public String baseCodePath;
        /** Paths of any split APKs, ordered by parsed splitName */
        public String[] splitCodePaths;

        /** Revision code of base APK */
        public int baseRevisionCode;
        /** Revision codes of any split APKs, ordered by parsed splitName */
        public int[] splitRevisionCodes;

        /** Flags of any split APKs; ordered by parsed splitName */
        public int[] splitFlags;

        public boolean baseHardwareAccelerated;

        public final ApplicationInfo applicationInfo = new ApplicationInfo();

        //權(quán)限
        public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
        public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);

        //四大組件Activity,Receiver,Service,Provider
        public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
        public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
        public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
        public final ArrayList<Service> services = new ArrayList<Service>(0);
       
        public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);

        public final ArrayList<String> requestedPermissions = new ArrayList<String>();
        public final ArrayList<Boolean> requestedPermissionsRequired = new ArrayList<Boolean>();

        public ArrayList<String> protectedBroadcasts;

        public ArrayList<String> libraryNames = null;
        public ArrayList<String> usesLibraries = null;
        public ArrayList<String> usesOptionalLibraries = null;
        public String[] usesLibraryFiles = null;

        public ArrayList<ActivityIntentInfo> preferredActivityFilters = null;

        public ArrayList<String> mOriginalPackages = null;
        public String mRealPackage = null;
        public ArrayList<String> mAdoptPermissions = null;
        
        // We store the application meta-data independently to avoid multiple unwanted references
        public Bundle mAppMetaData = null;

        // The version code declared for this package.
        public int mVersionCode;

        // The version name declared for this package.
        public String mVersionName;
        
        // The shared user id that this package wants to use.
        public String mSharedUserId;

        // The shared user label that this package wants to use.
        public int mSharedUserLabel;

        // Signatures that were read from the package.
        public Signature[] mSignatures;
        public Certificate[][] mCertificates;

        // For use by package manager service for quick lookup of
        // preferred up order.
        public int mPreferredOrder = 0;

        // For use by package manager to keep track of where it needs to do dexopt.
        public final ArraySet<String> mDexOptPerformed = new ArraySet<>(4);

        // For use by package manager to keep track of when a package was last used.
        public long mLastPackageUsageTimeInMills;

        // // User set enabled state.
        // public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
        //
        // // Whether the package has been stopped.
        // public boolean mSetStopped = false;

        // Additional data supplied by callers.
        public Object mExtras;

        // Whether an operation is currently pending on this package
        public boolean mOperationPending;

        // Applications hardware preferences
        public ArrayList<ConfigurationInfo> configPreferences = null;

        // Applications requested features
        public ArrayList<FeatureInfo> reqFeatures = null;

        // Applications requested feature groups
        public ArrayList<FeatureGroupInfo> featureGroups = null;

        public int installLocation;

        public boolean coreApp;

        /* An app that's required for all users and cannot be uninstalled for a user */
        public boolean mRequiredForAllUsers;

        /* The restricted account authenticator type that is used by this application */
        public String mRestrictedAccountType;

        /* The required account type without which this application will not function */
        public String mRequiredAccountType;

        /**
         * 代表一個包文件的摘要,用于確定兩個package是否不一致
         */
        public ManifestDigest manifestDigest;

        public String mOverlayTarget;
        public int mOverlayPriority;
        public boolean mTrustedOverlay;

        /**
         * Data used to feed the KeySetManagerService
         */
        public ArraySet<PublicKey> mSigningKeys;
        public ArraySet<String> mUpgradeKeySets;
        public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping;

        public String cpuAbiOverride;

    }

PackageInfo

該類代表包的整體描述信息,即AndroidManifest.xml中的信息.如果說Package在內(nèi)存中代表完整的APK描述,那么PackageInfo則是其子集,來簡單的看一下其代碼:

public class PackageInfo implements Parcelable {
    public String packageName;
    public String[] splitNames;
    public int versionCode;
    public String versionName;
    public int baseRevisionCode;
    public int[] splitRevisionCodes;
    public String sharedUserId;
    public int sharedUserLabel;
    
    public ApplicationInfo applicationInfo;
    public long firstInstallTime;
    public long lastUpdateTime;
    public int[] gids;
    public ActivityInfo[] activities;
    public ActivityInfo[] receivers;
    public ServiceInfo[] services;
    public ProviderInfo[] providers;
    public InstrumentationInfo[] instrumentation;
    public PermissionInfo[] permissions;
    public String[] requestedPermissions;
    public int[] requestedPermissionsFlags;
    
    public Signature[] signatures;
    public ConfigurationInfo[] configPreferences;
    public FeatureInfo[] reqFeatures;
    public FeatureGroupInfo[] featureGroups;
    
    
}

對比Package和PackageInfo很容易發(fā)現(xiàn)期間的關(guān)系,接下來順便介紹PackageInfo中涉及到的實體類:

類名 描述
ActivityInfo 該實體類代表AndroidManiest.xml中的<activity><recevier>元素的信息
ServiceInfo 該實體類代表AndroidManiest.xml中的<service>元素中的信息
ProviderInfo 該實體類代表AndroidManiest.xml中的<provider>元素的信息
InstrumentationInfo 該實體類代表AndroidManiest.xml中的<instrumentation>元素的信息
PermissionInfo 該實體類代表AndroidManiest.xml中的<permission>元素的信息
ConfigurationInfo 關(guān)于程序要求的硬件信息,該實體類代表AndroidManiest.xml中<uses-configuration><uses-feature>元素的信息.
FeatureInfo 該實體類代表AndroidManiest.xml中的<uses-feature>元素的信息
FeatureGroupInfo 該實體類代表AndroidManiest.xml中的<feature-group>元素的信息
ManifestDigest 代表一個包文件的摘要信息

這里我們用一張類圖來描述其類間的關(guān)系:


此處輸入圖片的描述
此處輸入圖片的描述

總結(jié)

到現(xiàn)在PackageParser的基本解釋已經(jīng)完成,之所以在分析PMS之前先來談PackageParser的原因在于,該工具類可以脫離上下文,單獨進(jìn)行理解,而無關(guān)你目前的狀態(tài),這也就避免我們面對一大堆源碼,在閱讀過程找不到側(cè)重點的問題.接下來,是對PackageManager的分析.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市怔锌,隨后出現(xiàn)的幾起案子憨攒,更是在濱河造成了極大的恐慌舌涨,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件亦歉,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機帮辟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玩焰,“玉大人由驹,你說我怎么就攤上這事。” “怎么了蔓榄?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵并炮,是天一觀的道長。 經(jīng)常有香客問我甥郑,道長逃魄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任澜搅,我火速辦了婚禮伍俘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘勉躺。我一直安慰自己癌瘾,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布饵溅。 她就那樣靜靜地躺著妨退,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蜕企。 梳的紋絲不亂的頭發(fā)上碧注,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機與錄音糖赔,去河邊找鬼萍丐。 笑死,一個胖子當(dāng)著我的面吹牛放典,可吹牛的內(nèi)容都是我干的逝变。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼奋构,長吁一口氣:“原來是場噩夢啊……” “哼壳影!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起弥臼,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤宴咧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后径缅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掺栅,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年纳猪,在試婚紗的時候發(fā)現(xiàn)自己被綠了氧卧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡氏堤,死狀恐怖沙绝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤闪檬,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布星著,位于F島的核電站,受9級特大地震影響粗悯,放射性物質(zhì)發(fā)生泄漏虚循。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一为黎、第九天 我趴在偏房一處隱蔽的房頂上張望邮丰。 院中可真熱鬧,春花似錦铭乾、人聲如沸剪廉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽斗蒋。三九已至,卻和暖如春笛质,著一層夾襖步出監(jiān)牢的瞬間泉沾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工妇押, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留跷究,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓敲霍,卻偏偏與公主長得像俊马,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子肩杈,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

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