Android App安裝彈窗顯示流程

一、APP的安裝

1淑倾、常見(jiàn)安裝方式

  • 系統(tǒng)應(yīng)用和預(yù)制應(yīng)用安裝――開(kāi)機(jī)時(shí)完成征椒,沒(méi)有安裝界面,在PKMS的構(gòu)造函數(shù)中完成安裝

  • 網(wǎng)絡(luò)下載或第三方應(yīng)用安裝――調(diào)用PackageManager.installPackages()碍讨,有安裝界面蒙秒。

  • ADB工具安裝――沒(méi)有安裝界面,它通過(guò)啟動(dòng)pm腳本的形式税肪,然后調(diào)用com.android.commands.pm.Pm類榜田,之后調(diào)用到PMS.installStage()完成安裝箭券。


    image.png

2辩块、APK的簽名校驗(yàn)理解

V1簽名apk-signature-v1-location.png只是校驗(yàn)了apk資源,并沒(méi)有約束zip,簽名信息存儲(chǔ)在zip/META-INF中荆永。

v2簽名是一個(gè)對(duì)全文件進(jìn)行簽名的方案,能提供更快的應(yīng)用安裝時(shí)間、對(duì)未授權(quán)APK文件的更改提供更多保護(hù).

3具钥、APK安裝過(guò)程

  • 開(kāi)機(jī)后掃描應(yīng)用安裝目錄和系統(tǒng)App目錄,解析其中的apk文件將相關(guān)信息加載到PKMS中的數(shù)據(jù)結(jié)構(gòu)中掌动,同時(shí)對(duì)于沒(méi)有對(duì)應(yīng)數(shù)據(jù)目錄的App生成對(duì)應(yīng)的數(shù)據(jù)目錄
  • 注冊(cè)包名App等信息宁玫、以及相關(guān)的四大組件到PMS中
  • 將解析到的數(shù)據(jù)同步到/data/system/packages.xml中

4、App安裝涉及的目錄理解

  • 系統(tǒng)App安裝目錄

1眷射、 /system/app: Android系統(tǒng)App路徑
2佛掖、/system/priv-app: 同上,但比/system/app權(quán)限優(yōu)先級(jí)更高嗅绸,可以拿到ApplicationInfo.PRIVATE_FLAG_PRIVILEGED特殊權(quán)限
3撕彤、/vendor/app: odm或者oem廠商預(yù)制系統(tǒng)App目錄
4、/vendor/priva-app: 同上

  • 普通應(yīng)用App安裝目錄

/data/app:用戶App程序安裝的目錄蚀狰。安裝時(shí)Apk會(huì)被拷貝至此目錄

  • 用戶數(shù)據(jù)目錄

/data/data:存放應(yīng)用程序的數(shù)據(jù),無(wú)論是系統(tǒng)App還是普通App,App產(chǎn)生的用戶數(shù)據(jù)都存放在/data/data/包名/目錄下职员。

  • App注冊(cè)表目錄

/data/system
1、packages.xml:
記錄apk的permissions,,flags,ts,version,uesrid等信息焊切,這些信息主要通apk的AndroidManifest.xml解析獲取芳室,當(dāng)系統(tǒng)進(jìn)行程序安裝堪侯、卸載和更新等操作時(shí)荔仁,均會(huì)更新該文件。
2次洼、packages-backup.xml : 備份文件
3、packages-stopped.xml : 記錄被用戶強(qiáng)行停止的應(yīng)用的Package信息
4遇骑、packages-stopped-backup.xml : pakcages-stoped.xml文件的備份
5、packages.list : 記錄非系統(tǒng)自帶的APK的數(shù)據(jù)信息质蕉,這些APK有變化時(shí)會(huì)更新該文件

5模暗、package.xml文件解析

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<packages>
    <version sdkVersion="xxx" databaseVersion="xxx" fingerprint="xxx" />
    <version volumeUuid="xxx" sdkVersion="xxx" databaseVersion="xxx" fingerprint="xxx" />

    <permissions>
        <item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
        ...
    </permissions>   
  
    <package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="25" versionName="7.1.2" applicationName="電話和短信存儲(chǔ)" sharedUserId="1001" isOrphaned="true">
        <sigs count="1">
            <cert index="1" key="xxx" />
        </sigs>
        <perms>
            <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
            ...
        </perms>
        <proper-signing-keyset identifier="1" />
    </package>  
    ...       
    
    <updated-package name="xxx.xxx.xxx" codePath="/system/app/xxx" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="11" nativeLibraryPath="/system/app/xxx/lib" primaryCpuAbi="armeabi-v7a" sharedUserId="1000" />


    <shared-user name="android.media" userId="10005">
        <sigs count="1">
            <cert index="2" />
        </sigs>
        <perms>
            <item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" />
            ...
        </perms>
    </shared-user>
    ...
</packages>   

package.xml對(duì)應(yīng)的類圖關(guān)系

image.png
  • BasePermission

BasePermission對(duì)應(yīng)packages.xml中permissions標(biāo)簽的子標(biāo)簽item兑宇,對(duì)于上述所定義的每一項(xiàng)權(quán)限都會(huì)生成一個(gè)BasePermission隶糕。
protection :等級(jí)分為四個(gè)
1站玄、普通權(quán)限(normal)
2、運(yùn)行時(shí)權(quán)限(dangerous)
3株旷、簽名權(quán)限(signature)
4、特殊權(quán)限(privileged)

    <permissions>
        <item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
        ...
    <permissions/>  
  • PermissionsState


    image.png

PermissionState對(duì)應(yīng)的是<package>標(biāo)簽中的子標(biāo)簽<perms>標(biāo)簽中的內(nèi)容

<package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="25" versionName="7.1.2" applicationName="電話和短信存儲(chǔ)" sharedUserId="1001" isOrphaned="true">
        <perms>
            <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
            ...
        </perms>
</package>

  • PackageSignatures

PackageSignatures對(duì)應(yīng)的是<package>標(biāo)簽中的子標(biāo)簽<sigs>標(biāo)簽中的內(nèi)容

<package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="25" versionName="7.1.2" applicationName="電話和短信存儲(chǔ)" sharedUserId="1001" isOrphaned="true">
        <sigs count="1">
            <cert index="1" key="xxx" />
        </sigs>
</package>

  • PackageSetting

PackageSetting這個(gè)數(shù)據(jù)結(jié)構(gòu)類是packages.xml里面記錄安裝包信息標(biāo)簽<package>相對(duì)應(yīng)的類锉矢,可以看到PackageSetting繼承了PackageSettingBase類,PackageSettingBase類繼承自SettingBase類沽损。應(yīng)用的基本信息保存在PackageSettingBase類的成員變量中循头,簽名則保存在PackageSignatures中炎疆,權(quán)限狀態(tài)保存在父類的SettingBase的PermissionsState中磷雇。

 <package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="25" versionName="7.1.2" applicationName="電話和短信存儲(chǔ)" sharedUserId="1001" isOrphaned="true">
        <sigs count="1">
            <cert index="1" key="xxx" />
        </sigs>
        <perms>
            <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
            ...
        </perms>
        <proper-signing-keyset identifier="1" />
</package>  
  • SharedUserSetting

SharedUserSetting這個(gè)數(shù)據(jù)結(jié)構(gòu)類是packages.xml里面記錄安裝包信息標(biāo)簽<shared-user>相對(duì)應(yīng)的類躏救,它和PackageSetting有一個(gè)共同的父類即SettingBase,即都是通過(guò)父類的PermissionsState來(lái)保存權(quán)限信息崩掘。SharedUserSetting被設(shè)計(jì)的用途主要用來(lái)描述具有相同的sharedUserId的應(yīng)用信息少办,它的成員變量packages保存了所有具有相同sharedUserId的應(yīng)用信息引用,而成員變量userId則是記錄多個(gè)APK共享的UID英妓。共享用戶的應(yīng)用的簽名是相同的,簽名保存在成員變量signatures中(這里有一點(diǎn)需要注意辑畦,由于簽名相同腿倚,Android運(yùn)行時(shí)很容易檢索到某個(gè)應(yīng)用擁有相同的sharedUserId的其他應(yīng)用)。


image.png
 <shared-user name="android.media" userId="10005">
        <sigs count="1">
            <cert index="2" />
        </sigs>
        <perms>
            <item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" />
            ...
        </perms>
</shared-user>
  • Settings : package.xml 終極大管家類


    image.png

二暂筝、APP安裝整體流程

代碼倉(cāng)庫(kù):http://androidxref.com/9.0.0_r3/xref/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallStart.java

1硬贯、安裝APP代碼入口

    <activity android:name=".InstallStart"
    android:exported="true"
    android:excludeFromRecents="true">
    <intent-filter android:priority="1">
        <action android:name="android.intent.action.VIEW"/>
        <action android:name="android.intent.action.INSTALL_PACKAGE"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:scheme="file"/>
        <data android:scheme="content"/>
        <data android:mimeType="application/vnd.android.package-archive"/>
    </intent-filter>
    <intent-filter android:priority="1">
        <action android:name="android.intent.action.INSTALL_PACKAGE"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:scheme="file"/>
        <data android:scheme="package"/>
        <data android:scheme="content"/>
    </intent-filter>
    <intent-filter android:priority="1">
        <action android:name="android.content.pm.action.CONFIRM_PERMISSIONS"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
    </activity>

2饭豹、根據(jù)Uri的Scheme協(xié)議不同,跳轉(zhuǎn)到不同的界面

content協(xié)議跳轉(zhuǎn)到InstallStaging墨状,package協(xié)議跳轉(zhuǎn)到PackageInstallerActivity

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ......
        Intent nextActivity = new Intent(intent);
        nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
        // The the installation source as the nextActivity thinks this activity is the source, hence
        // set the originating UID and sourceInfo explicitly
        nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);
        nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
        nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);

        //1肾砂、content的Uri協(xié)議 : InstallStaging 
        //2、package的Url協(xié)議:PackageInstallerActivity 
        if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
            nextActivity.setClass(this, PackageInstallerActivity.class);
        } else {
            Uri packageUri = intent.getData();

            if (packageUri != null && (packageUri.getScheme().equals(ContentResolver.SCHEME_FILE)
                    || packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT))) {
                // Copy file to prevent it from being changed underneath this process
                   //1、content的Uri協(xié)議 : InstallStaging 
                nextActivity.setClass(this, InstallStaging.class);
            } else if (packageUri != null && packageUri.getScheme().equals(
                    PackageInstallerActivity.SCHEME_PACKAGE)) {
                //package的Url協(xié)議:PackageInstallerActivity 
                nextActivity.setClass(this, PackageInstallerActivity.class);
            } else {
                Intent result = new Intent();
                result.putExtra(Intent.EXTRA_INSTALL_RESULT,
                        PackageManager.INSTALL_FAILED_INVALID_URI);
                setResult(RESULT_FIRST_USER, result);

                nextActivity = null;
            }
        }
        
        if (nextActivity != null) {
            startActivity(nextActivity);
        }
        finish();
    }

3饼煞、InstallStaging類的介紹

主要內(nèi)容:將content協(xié)議的Uri轉(zhuǎn)換為package協(xié)議的Uri,然后通過(guò)IO形式寫入到mStagedFile文件中
作用:主要起了轉(zhuǎn)換的作用砖瞧,將content協(xié)議的Uri轉(zhuǎn)換為package協(xié)議,然后跳轉(zhuǎn)到PackageInstallerActivity

@Override
  protected void onResume() {
      super.onResume();
      if (mStagingTask == null) {
          if (mStagedFile == null) {
              try {
                  mStagedFile = TemporaryFileManager.getStagedFile(this);
              } catch (IOException e) {
                  showError();
                  return;
              }
          }
          mStagingTask = new StagingAsyncTask();
          mStagingTask.execute(getIntent().getData());
      }
  }

 private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {
        @Override
        protected Boolean doInBackground(Uri... params) {
            if (params == null || params.length <= 0) {
                return false;
            }
            Uri packageUri = params[0];
            try (InputStream in = getContentResolver().openInputStream(packageUri)) {
                if (in == null) {
                    return false;
                }
                try (OutputStream out = new FileOutputStream(mStagedFile)) {
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) >= 0) {
                        if (isCancelled()) {
                            return false;
                        }
                        out.write(buffer, 0, bytesRead);
                    }
                }
            } catch (IOException | SecurityException e) {
                Log.w(LOG_TAG, "Error staging apk from content URI", e);
                return false;
            }
            return true;
        }
        @Override
        protected void onPostExecute(Boolean success) {
           if (session != null) {
         Intent broadcastIntent = new Intent(BROADCAST_ACTION);
         broadcastIntent.setPackage(
                 getPackageManager().getPermissionControllerPackageName());
         broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
         PendingIntent pendingIntent = PendingIntent.getBroadcast(
                 InstallInstalling.this,
                 mInstallId,
                 broadcastIntent,
                 PendingIntent.FLAG_UPDATE_CURRENT);
        //APP安裝的啟動(dòng)入口
         session.commit(pendingIntent.getIntentSender());
         mCancelButton.setEnabled(false);
         setFinishOnTouchOutside(false);
     } else {
         getPackageManager().getPackageInstaller().abandonSession(mSessionId);
         if (!isCancelled()) {
             launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
         }
     }
}

4、PackageInstallerActivity類的介紹

  • 它就是在安裝應(yīng)用顯示彈窗的Activity
@Override
protected void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    if (icicle != null) {
        mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);
    }
    mPm = getPackageManager();
    mIpm = AppGlobals.getPackageManager();
    mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
    mInstaller = mPm.getPackageInstaller();
    mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
    ...
    //根據(jù)Uri的Scheme進(jìn)行預(yù)處理
    boolean wasSetUp = processPackageUri(packageUri);
    if (!wasSetUp) {
        return;
    }
    bindUi(R.layout.install_confirm, false);
    //判斷是否是未知來(lái)源的應(yīng)用床未,如果開(kāi)啟允許安裝未知來(lái)源選項(xiàng)則直接初始化安裝
    checkIfAllowedAndInitiateInstall();
}
  • 分別對(duì)content和package兩種不同協(xié)議處理
private boolean processPackageUri(final Uri packageUri) {
     mPackageURI = packageUri;
     final String scheme = packageUri.getScheme();//1
     switch (scheme) {
         case SCHEME_PACKAGE: {
             try {
              ...
         } break;
         case SCHEME_FILE: {
             File sourceFile = new File(packageUri.getPath());
             //得到sourceFile的包信息
             PackageParser.Package parsed = PackageUtil.getPackageInfo(this, sourceFile);
             if (parsed == null) {
                 Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
                 showDialogInner(DLG_PACKAGE_ERROR);
                 setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
                 return false;
             }
             //對(duì)parsed進(jìn)行進(jìn)一步處理得到包信息PackageInfo
             mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
                     PackageManager.GET_PERMISSIONS, 0, 0, null,
                     new PackageUserState());//3
             mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
         } break;
         default: {
             Log.w(TAG, "Unsupported scheme " + scheme);
             setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
             finish();
             return false;
         }
     }
     return true;
 }
  • 彈窗上顯示是否是非法安裝的處理
private void checkIfAllowedAndInitiateInstall() {
       //判斷如果允許安裝未知來(lái)源或者根據(jù)Intent判斷得出該APK不是未知來(lái)源
       if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
           //初始化安裝
           initiateInstall();
           return;
       }
       // 如果管理員限制來(lái)自未知源的安裝, 就彈出提示Dialog或者跳轉(zhuǎn)到設(shè)置界面
       if (isUnknownSourcesDisallowed()) {
           if ((mUserManager.getUserRestrictionSource(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
                   Process.myUserHandle()) & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {    
               showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
               return;
           } else {
               startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
               finish();
           }
       } else {
           handleUnknownSources();
       }
   }
  • InstallStaging.java session.commit() 去執(zhí)行系統(tǒng)framework層
 protected void onPostExecute(Boolean success) {
           if (session != null) {
         Intent broadcastIntent = new Intent(BROADCAST_ACTION);
         broadcastIntent.setPackage(
                 getPackageManager().getPermissionControllerPackageName());
         broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
         PendingIntent pendingIntent = PendingIntent.getBroadcast(
                 InstallInstalling.this,
                 mInstallId,
                 broadcastIntent,
                 PendingIntent.FLAG_UPDATE_CURRENT);
        //APP安裝的啟動(dòng)入口
         session.commit(pendingIntent.getIntentSender());
         mCancelButton.setEnabled(false);
         setFinishOnTouchOutside(false);
     } else {
         getPackageManager().getPackageInstaller().abandonSession(mSessionId);
         if (!isCancelled()) {
             launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
         }
     }
  • PackageInstaller.java 類
public void commit(@NonNull IntentSender statusReceiver) {
           try {
               mSession.commit(statusReceiver);
           } catch (RemoteException e) {
               throw e.rethrowFromSystemServer();
           }
       }
  • PackageInstallerSession.java類
    PackageInstallObserverAdapter繼承PackageInstallObserver : 監(jiān)聽(tīng)安裝APP的過(guò)程
    mSessionId是安裝包的會(huì)話id斋扰,mInstallId是等待的安裝事件id
@Override
   public void commit(IntentSender statusReceiver) {
       Preconditions.checkNotNull(statusReceiver);
       ...
       mActiveCount.incrementAndGet();
       final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
               statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
      //Handler發(fā)送一個(gè)類型為MSG_COMMIT的消息啃洋,通知PMS安裝應(yīng)用
       mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
   }
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
      @Override
      public boolean handleMessage(Message msg) {
          final PackageInfo pkgInfo = mPm.getPackageInfo(
                  params.appPackageName, PackageManager.GET_SIGNATURES
                          | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
          final ApplicationInfo appInfo = mPm.getApplicationInfo(
                  params.appPackageName, 0, userId);
          synchronized (mLock) {
              if (msg.obj != null) {
                  mRemoteObserver = (IPackageInstallObserver2) msg.obj;
              }
              try {
                  //PMS開(kāi)始安裝應(yīng)用
                  commitLocked(pkgInfo, appInfo);
              } catch (PackageManagerException e) {
                  final String completeMsg = ExceptionUtils.getCompleteMessage(e);
                  Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
                  destroyInternal();
                   //安裝時(shí)候出現(xiàn)異常問(wèn)題
                  dispatchSessionFinished(e.error, completeMsg, null);
              }
              return true;
          }
      }
  };
private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
          throws PackageManagerException {
     ...
    //通知 PMS開(kāi)始安裝應(yīng)用
      mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
              installerPackageName, installerUid, user, mCertificates);
  }

總結(jié):

  • 根據(jù)Uri的Scheme協(xié)議不同宏娄,跳轉(zhuǎn)到不同的界面,content協(xié)議跳轉(zhuǎn)到InstallStaging绝编,package跳轉(zhuǎn)到PackageInstallerActivity貌踏。
  • InstallStaging將content協(xié)議的Uri轉(zhuǎn)換為File協(xié)議,然后跳轉(zhuǎn)到PackageInstallerActivity祖乳。
  • PackageInstallerActivity會(huì)分別對(duì)package協(xié)議和file協(xié)議的Uri進(jìn)行處理,如果是file協(xié)議會(huì)解析APK文件得到包信息PackageInfo蜒秤。
  • PackageInstallerActivity中會(huì)對(duì)未知來(lái)源進(jìn)行處理,如果允許安裝未知來(lái)源或者根據(jù)Intent判斷得出該APK不是未知來(lái)源作媚,就會(huì)初始化安裝確認(rèn)界面,如果管理員限制來(lái)自未知源的安裝, 就彈出提示Dialog或者跳轉(zhuǎn)到設(shè)置界面纸泡。

參考鏈接:https://maoao530.github.io/2017/01/18/package-install/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末赖瞒,一起剝皮案震驚了整個(gè)濱河市蚤假,隨后出現(xiàn)的幾起案子吧兔,更是在濱河造成了極大的恐慌,老刑警劉巖境蔼,帶你破解...
    沈念sama閱讀 206,013評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異民逼,居然都是意外死亡涮帘,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門调缨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人俊犯,你說(shuō)我怎么就攤上這事⊙嘞溃” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 152,370評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵绢彤,是天一觀的道長(zhǎng)蜓耻。 經(jīng)常有香客問(wèn)我,道長(zhǎng)刹淌,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,168評(píng)論 1 278
  • 正文 為了忘掉前任疹启,我火速辦了婚禮,結(jié)果婚禮上喊崖,老公的妹妹穿的比我還像新娘。我一直安慰自己贷祈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,153評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布呜达。 她就那樣靜靜地躺著粟耻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪挤忙。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 48,954評(píng)論 1 283
  • 那天册烈,我揣著相機(jī)與錄音,去河邊找鬼大猛。 笑死,一個(gè)胖子當(dāng)著我的面吹牛挽绩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播唉堪,決...
    沈念sama閱讀 38,271評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼肩民,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了此改?” 一聲冷哼從身側(cè)響起侄柔,我...
    開(kāi)封第一講書人閱讀 36,916評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎暂题,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體薪者,經(jīng)...
    沈念sama閱讀 43,382評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,877評(píng)論 2 323
  • 正文 我和宋清朗相戀三年攻人,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怀吻。...
    茶點(diǎn)故事閱讀 37,989評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖猿棉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情萨赁,我是刑警寧澤,帶...
    沈念sama閱讀 33,624評(píng)論 4 322
  • 正文 年R本政府宣布杖爽,位于F島的核電站紫皇,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏坝橡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,209評(píng)論 3 307
  • 文/蒙蒙 一锣杂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧元莫,春花似錦、人聲如沸踱蠢。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,199評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至企锌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間撕攒,已是汗流浹背陡鹃。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,418評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工萍鲸, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留擦俐,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,401評(píng)論 2 352
  • 正文 我出身青樓捌肴,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親状知。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,700評(píng)論 2 345

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