PMS安装程序

PMS负责扫描系统中特定的目录,找到里面的apk文件,然后对这些文件进行解析得到相关信息,然后完成安装的过程。
PMS安装APP,其实也就是解析APP配置文件AndroidManifest.xml的过程。
解析apk文件的核心类是PackageParser,这个类负责从apk文件中解析各种标签以及权限等信息,并将这些信息注册进PMS。
以下就以4.2.2_r1版本的PackageParser为例来看看PackageParser是如何解析apk文件的。
PackageParser的核心方法是parsePackage()

public Package parsePackage(File sourceFile, String destCodePath,
        DisplayMetrics metrics, int flags) {
    mParseError = PackageManager.INSTALL_SUCCEEDED;

    /** 首先对apk文件进行检查,如果出错则打印相应的错误日志并返回null. **/
    mArchiveSourcePath = sourceFile.getPath();
    if (!sourceFile.isFile()) {
        Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath);
        mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
        return null;
    }
    if (!isPackageFilename(sourceFile.getName())
            && (flags&PARSE_MUST_BE_APK) != 0) {
        if ((flags&PARSE_IS_SYSTEM) == 0) {
            // We expect to have non-.apk files in the system dir,
            // so don't warn about them.
            Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath);
        }
        mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
        return null;
    }

    if (DEBUG_JAR)
        Slog.d(TAG, "Scanning package: " + mArchiveSourcePath);

    /** 这里则是尝试读取AndroidManifest.xml文件和资源文件以及通过apk来初始化AssetManager. 
        如果出错,则打印相应的日志并记录错误同时返回空值;否则将调用另一个parsePackage方法继续解析apk. 
        另一个方法的解析贴在本方法的下方。 **/
    XmlResourceParser parser = null;
    AssetManager assmgr = null;
    Resources res = null;
    boolean assetError = true;
    try {
        assmgr = new AssetManager();
        int cookie = assmgr.addAssetPath(mArchiveSourcePath);
        if (cookie != 0) {
            res = new Resources(assmgr, metrics, null);
            assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                    Build.VERSION.RESOURCES_SDK_INT);
            parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
            assetError = false;
        } else {
            Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);
        }
    } catch (Exception e) {
        Slog.w(TAG, "Unable to read AndroidManifest.xml of "
                + mArchiveSourcePath, e);
    }
    if (assetError) {
        if (assmgr != null) assmgr.close();
        mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
        return null;
    }
    String[] errorText = new String[1];
    Package pkg = null;
    Exception errorException = null;
    try {
        // XXXX todo: need to figure out correct configuration.
        pkg = parsePackage(res, parser, flags, errorText);
    } catch (Exception e) {
        errorException = e;
        mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
    }


    if (pkg == null) {
        // If we are only parsing core apps, then a null with INSTALL_SUCCEEDED
        // just means to skip this app so don't make a fuss about it.
        if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) {
            if (errorException != null) {
                Slog.w(TAG, mArchiveSourcePath, errorException);
            } else {
                Slog.w(TAG, mArchiveSourcePath + " (at "
                        + parser.getPositionDescription()
                        + "): " + errorText[0]);
            }
            if (mParseError == PackageManager.INSTALL_SUCCEEDED) {
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            }
        }
        parser.close();
        assmgr.close();
        return null;
    }

    parser.close();
    assmgr.close();

    // Set code and resource paths
    pkg.mPath = destCodePath;
    pkg.mScanPath = mArchiveSourcePath;
    //pkg.applicationInfo.sourceDir = destCodePath;
    //pkg.applicationInfo.publicSourceDir = destRes;
    pkg.mSignatures = null;

    return pkg;
}

另外一个parsePackage()方法,该方法为私有方法,在上面个公有的parsePackage()方法中调用:

private Package parsePackage(
    Resources res, XmlResourceParser parser, int flags, String[] outError)
    throws XmlPullParserException, IOException {
    /** 由于XmlResourceParser实现了AttributeSet接口,因此这里直接当作AttributeSet来使用 **/
    AttributeSet attrs = parser;

    mParseInstrumentationArgs = null;
    mParseActivityArgs = null;
    mParseServiceArgs = null;
    mParseProviderArgs = null;
    
    /** 先检查包名,如果包名有问题则直接失败返回 **/
    String pkgName = parsePackageName(parser, attrs, flags, outError);
    if (pkgName == null) {
        mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
        return null;
    }
    int type;

    /** coreApp 同加密机制有关 **/
    if (mOnlyCoreApps) {
        boolean core = attrs.getAttributeBooleanValue(null, "coreApp", false);
        if (!core) {
            mParseError = PackageManager.INSTALL_SUCCEEDED;
            return null;
        }
    }

    final Package pkg = new Package(pkgName);
    boolean foundApp = false;
    
    /** 获取版本号、版本名和shareUserId,并检查shareUserId是否正确 **/
    TypedArray sa = res.obtainAttributes(attrs,
            com.android.internal.R.styleable.AndroidManifest);
    pkg.mVersionCode = sa.getInteger(
            com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
    pkg.mVersionName = sa.getNonConfigurationString(
            com.android.internal.R.styleable.AndroidManifest_versionName, 0);
    if (pkg.mVersionName != null) {
        pkg.mVersionName = pkg.mVersionName.intern();
    }
    String str = sa.getNonConfigurationString(
            com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
    if (str != null && str.length() > 0) {
        String nameError = validateName(str, true);
        if (nameError != null && !"android".equals(pkgName)) {
            outError[0] = "<manifest> specifies bad sharedUserId name \""
                + str + "\": " + nameError;
            mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
            return null;
        }
        pkg.mSharedUserId = str.intern();
        pkg.mSharedUserLabel = sa.getResourceId(
                com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
    }
    sa.recycle();

    pkg.installLocation = sa.getInteger(
            com.android.internal.R.styleable.AndroidManifest_installLocation,
            PARSE_DEFAULT_INSTALL_LOCATION);
    pkg.applicationInfo.installLocation = pkg.installLocation;

    /* Set the global "forward lock" flag */
    if ((flags & PARSE_FORWARD_LOCK) != 0) {
        pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FORWARD_LOCK;
    }

    /* Set the global "on SD card" flag */
    if ((flags & PARSE_ON_SDCARD) != 0) {
        pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
    }

    // Resource boolean are -1, so 1 means we don't know the value.
    int supportsSmallScreens = 1;
    int supportsNormalScreens = 1;
    int supportsLargeScreens = 1;
    int supportsXLargeScreens = 1;
    int resizeable = 1;
    int anyDensity = 1;
    
    int outerDepth = parser.getDepth();
    /** 开始迭代处理各种标签 
        对于大多数Android开发者来说,这里最重要的是parseApplication.
        这里会将Application标签下的各种信息解析出来,并注册进PMS. 
        迭代检查完所有预定义的标签之后如果没有问题,则将记录了apk信息的Package对象返回。**/
    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
            && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
        if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
            continue;
        }

        String tagName = parser.getName();
        if (tagName.equals("application")) {
            if (foundApp) {
                if (RIGID_PARSER) {
                    outError[0] = "<manifest> has more than one <application>";
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return null;
                } else {
                    Slog.w(TAG, "<manifest> has more than one <application>");
                    XmlUtils.skipCurrentTag(parser);
                    continue;
                }
            }

            foundApp = true;
            if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {
                return null;
            }
        } else if (tagName.equals("permission-group")) {
            if (parsePermissionGroup(pkg, flags, res, parser, attrs, outError) == null) {
                return null;
            }
        } else if (tagName.equals("permission")) {
            if (parsePermission(pkg, res, parser, attrs, outError) == null) {
                return null;
            }
        } else if (tagName.equals("permission-tree")) {
            if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
                return null;
            }
        } else if (tagName.equals("uses-permission")) {
            sa = res.obtainAttributes(attrs,
                    com.android.internal.R.styleable.AndroidManifestUsesPermission);

            // Note: don't allow this value to be a reference to a resource
            // that may change.
            String name = sa.getNonResourceString(
                    com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
            /* Not supporting optional permissions yet.
            boolean required = sa.getBoolean(
                    com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true);
            */

            sa.recycle();

            if (name != null && !pkg.requestedPermissions.contains(name)) {
                pkg.requestedPermissions.add(name.intern());
                pkg.requestedPermissionsRequired.add(Boolean.TRUE);
            }

            XmlUtils.skipCurrentTag(parser);

        } else if (tagName.equals("uses-configuration")) {
            ConfigurationInfo cPref = new ConfigurationInfo();
            sa = res.obtainAttributes(attrs,
                    com.android.internal.R.styleable.AndroidManifestUsesConfiguration);
            cPref.reqTouchScreen = sa.getInt(
                    com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
                    Configuration.TOUCHSCREEN_UNDEFINED);
            cPref.reqKeyboardType = sa.getInt(
                    com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
                    Configuration.KEYBOARD_UNDEFINED);
            if (sa.getBoolean(
                    com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
                    false)) {
                cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
            }
            cPref.reqNavigation = sa.getInt(
                    com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
                    Configuration.NAVIGATION_UNDEFINED);
            if (sa.getBoolean(
                    com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
                    false)) {
                cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
            }
            sa.recycle();
            pkg.configPreferences.add(cPref);

            XmlUtils.skipCurrentTag(parser);

        } else if (tagName.equals("uses-feature")) {
            FeatureInfo fi = new FeatureInfo();
            sa = res.obtainAttributes(attrs,
                    com.android.internal.R.styleable.AndroidManifestUsesFeature);
            // Note: don't allow this value to be a reference to a resource
            // that may change.
            fi.name = sa.getNonResourceString(
                    com.android.internal.R.styleable.AndroidManifestUsesFeature_name);
            if (fi.name == null) {
                fi.reqGlEsVersion = sa.getInt(
                        com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion,
                        FeatureInfo.GL_ES_VERSION_UNDEFINED);
            }
            if (sa.getBoolean(
                    com.android.internal.R.styleable.AndroidManifestUsesFeature_required,
                    true)) {
                fi.flags |= FeatureInfo.FLAG_REQUIRED;
            }
            sa.recycle();
            if (pkg.reqFeatures == null) {
                pkg.reqFeatures = new ArrayList<FeatureInfo>();
            }
            pkg.reqFeatures.add(fi);
            
            if (fi.name == null) {
                ConfigurationInfo cPref = new ConfigurationInfo();
                cPref.reqGlEsVersion = fi.reqGlEsVersion;
                pkg.configPreferences.add(cPref);
            }

            XmlUtils.skipCurrentTag(parser);

        } else if (tagName.equals("uses-sdk")) {
            if (SDK_VERSION > 0) {
                sa = res.obtainAttributes(attrs,
                        com.android.internal.R.styleable.AndroidManifestUsesSdk);

                int minVers = 0;
                String minCode = null;
                int targetVers = 0;
                String targetCode = null;
                
                TypedValue val = sa.peekValue(
                        com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
                if (val != null) {
                    if (val.type == TypedValue.TYPE_STRING && val.string != null) {
                        targetCode = minCode = val.string.toString();
                    } else {
                        // If it's not a string, it's an integer.
                        targetVers = minVers = val.data;
                    }
                }
                
                val = sa.peekValue(
                        com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
                if (val != null) {
                    if (val.type == TypedValue.TYPE_STRING && val.string != null) {
                        targetCode = minCode = val.string.toString();
                    } else {
                        // If it's not a string, it's an integer.
                        targetVers = val.data;
                    }
                }
                
                sa.recycle();

                if (minCode != null) {
                    if (!minCode.equals(SDK_CODENAME)) {
                        if (SDK_CODENAME != null) {
                            outError[0] = "Requires development platform " + minCode
                                    + " (current platform is " + SDK_CODENAME + ")";
                        } else {
                            outError[0] = "Requires development platform " + minCode
                                    + " but this is a release platform.";
                        }
                        mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
                        return null;
                    }
                } else if (minVers > SDK_VERSION) {
                    outError[0] = "Requires newer sdk version #" + minVers
                            + " (current version is #" + SDK_VERSION + ")";
                    mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
                    return null;
                }
                
                if (targetCode != null) {
                    if (!targetCode.equals(SDK_CODENAME)) {
                        if (SDK_CODENAME != null) {
                            outError[0] = "Requires development platform " + targetCode
                                    + " (current platform is " + SDK_CODENAME + ")";
                        } else {
                            outError[0] = "Requires development platform " + targetCode
                                    + " but this is a release platform.";
                        }
                        mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
                        return null;
                    }
                    // If the code matches, it definitely targets this SDK.
                    pkg.applicationInfo.targetSdkVersion
                            = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
                } else {
                    pkg.applicationInfo.targetSdkVersion = targetVers;
                }
            }

            XmlUtils.skipCurrentTag(parser);

        } else if (tagName.equals("supports-screens")) {
            sa = res.obtainAttributes(attrs,
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens);

            pkg.applicationInfo.requiresSmallestWidthDp = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp,
                    0);
            pkg.applicationInfo.compatibleWidthLimitDp = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp,
                    0);
            pkg.applicationInfo.largestWidthLimitDp = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp,
                    0);

            // This is a trick to get a boolean and still able to detect
            // if a value was actually set.
            supportsSmallScreens = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens,
                    supportsSmallScreens);
            supportsNormalScreens = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens,
                    supportsNormalScreens);
            supportsLargeScreens = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens,
                    supportsLargeScreens);
            supportsXLargeScreens = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_xlargeScreens,
                    supportsXLargeScreens);
            resizeable = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_resizeable,
                    resizeable);
            anyDensity = sa.getInteger(
                    com.android.internal.R.styleable.AndroidManifestSupportsScreens_anyDensity,
                    anyDensity);

            sa.recycle();
            
            XmlUtils.skipCurrentTag(parser);
            
        } else if (tagName.equals("protected-broadcast")) {
            sa = res.obtainAttributes(attrs,
                    com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);

            // Note: don't allow this value to be a reference to a resource
            // that may change.
            String name = sa.getNonResourceString(
                    com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);

            sa.recycle();

            if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {
                if (pkg.protectedBroadcasts == null) {
                    pkg.protectedBroadcasts = new ArrayList<String>();
                }
                if (!pkg.protectedBroadcasts.contains(name)) {
                    pkg.protectedBroadcasts.add(name.intern());
                }
            }

            XmlUtils.skipCurrentTag(parser);
            
        } else if (tagName.equals("instrumentation")) {
            if (parseInstrumentation(pkg, res, parser, attrs, outError) == null) {
                return null;
            }
            
        } else if (tagName.equals("original-package")) {
            sa = res.obtainAttributes(attrs,
                    com.android.internal.R.styleable.AndroidManifestOriginalPackage);

            String orig =sa.getNonConfigurationString(
                    com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);
            if (!pkg.packageName.equals(orig)) {
                if (pkg.mOriginalPackages == null) {
                    pkg.mOriginalPackages = new ArrayList<String>();
                    pkg.mRealPackage = pkg.packageName;
                }
                pkg.mOriginalPackages.add(orig);
            }

            sa.recycle();

            XmlUtils.skipCurrentTag(parser);
            
        } else if (tagName.equals("adopt-permissions")) {
            sa = res.obtainAttributes(attrs,
                    com.android.internal.R.styleable.AndroidManifestOriginalPackage);

            String name = sa.getNonConfigurationString(
                    com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);

            sa.recycle();

            if (name != null) {
                if (pkg.mAdoptPermissions == null) {
                    pkg.mAdoptPermissions = new ArrayList<String>();
                }
                pkg.mAdoptPermissions.add(name);
            }

            XmlUtils.skipCurrentTag(parser);
            
        } else if (tagName.equals("uses-gl-texture")) {
            // Just skip this tag
            XmlUtils.skipCurrentTag(parser);
            continue;
            
        } else if (tagName.equals("compatible-screens")) {
            // Just skip this tag
            XmlUtils.skipCurrentTag(parser);
            continue;
            
        } else if (tagName.equals("eat-comment")) {
            // Just skip this tag
            XmlUtils.skipCurrentTag(parser);
            continue;
            
        } else if (RIGID_PARSER) {
            outError[0] = "Bad element under <manifest>: "
                + parser.getName();
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return null;

        } else {
            Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
                    + " at " + mArchiveSourcePath + " "
                    + parser.getPositionDescription());
            XmlUtils.skipCurrentTag(parser);
            continue;
        }
    }

    if (!foundApp && pkg.instrumentation.size() == 0) {
        outError[0] = "<manifest> does not contain an <application> or <instrumentation>";
        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY;
    }

    final int NP = PackageParser.NEW_PERMISSIONS.length;
    StringBuilder implicitPerms = null;
    for (int ip=0; ip<NP; ip++) {
        final PackageParser.NewPermissionInfo npi
                = PackageParser.NEW_PERMISSIONS[ip];
        if (pkg.applicationInfo.targetSdkVersion >= npi.sdkVersion) {
            break;
        }
        if (!pkg.requestedPermissions.contains(npi.name)) {
            if (implicitPerms == null) {
                implicitPerms = new StringBuilder(128);
                implicitPerms.append(pkg.packageName);
                implicitPerms.append(": compat added ");
            } else {
                implicitPerms.append(' ');
            }
            implicitPerms.append(npi.name);
            pkg.requestedPermissions.add(npi.name);
            pkg.requestedPermissionsRequired.add(Boolean.TRUE);
        }
    }
    if (implicitPerms != null) {
        Slog.i(TAG, implicitPerms.toString());
    }

    final int NS = PackageParser.SPLIT_PERMISSIONS.length;
    for (int is=0; is<NS; is++) {
        final PackageParser.SplitPermissionInfo spi
                = PackageParser.SPLIT_PERMISSIONS[is];
        if (pkg.applicationInfo.targetSdkVersion >= spi.targetSdk
                || !pkg.requestedPermissions.contains(spi.rootPerm)) {
            continue;
        }
        for (int in=0; in<spi.newPerms.length; in++) {
            final String perm = spi.newPerms[in];
            if (!pkg.requestedPermissions.contains(perm)) {
                pkg.requestedPermissions.add(perm);
                pkg.requestedPermissionsRequired.add(Boolean.TRUE);
            }
        }
    }

    if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
            && pkg.applicationInfo.targetSdkVersion
                    >= android.os.Build.VERSION_CODES.DONUT)) {
        pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
    }
    if (supportsNormalScreens != 0) {
        pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
    }
    if (supportsLargeScreens < 0 || (supportsLargeScreens > 0
            && pkg.applicationInfo.targetSdkVersion
                    >= android.os.Build.VERSION_CODES.DONUT)) {
        pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
    }
    if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0
            && pkg.applicationInfo.targetSdkVersion
                    >= android.os.Build.VERSION_CODES.GINGERBREAD)) {
        pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
    }
    if (resizeable < 0 || (resizeable > 0
            && pkg.applicationInfo.targetSdkVersion
                    >= android.os.Build.VERSION_CODES.DONUT)) {
        pkg.applicationInfo.flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
    }
    if (anyDensity < 0 || (anyDensity > 0
            && pkg.applicationInfo.targetSdkVersion
                    >= android.os.Build.VERSION_CODES.DONUT)) {
        pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
    }

    return pkg;
}

在解析以上方法中调用的类似parseApplication()、parsePermission() ...之类的方法就不贴出来了,他们都是从属于parsePackage(),负责解析相应标签的。
而从源码中也可以看到,整个PackageParser做的工作就是从apk中AndroidManifest.xml里各个属性和标签的内容

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

推荐阅读更多精彩内容