直接拿來用的版本更新(Android 兼容7.0、8.0)

1喉刘、需求:

版本更新瞧柔,一個經(jīng)常用到的功能。寫一個工具類饱搏,拿去直接用非剃。

2、實現(xiàn)思路:

  • 1推沸、請求數(shù)據(jù) 拿到后臺的apk的版本號备绽、版本名、更新內(nèi)容鬓催、下載地址(可能還會包括是否強制更新的標志位)肺素。這里有一點需要注意,如果有強制更新的概念宇驾,這時候請求數(shù)據(jù)的時候要給服務(wù)器你當前的版本號倍靡。如果你現(xiàn)在的版本號是1 ,用戶很久沒有操作课舍,期間版本更新了2(強制更新)塌西、3(不強制更新)。這時候用戶打開版本號為1的版本筝尾,應(yīng)該是要強制更新的捡需。
  • 2、和本地的apk版本號對比時候需要更新
  • 3筹淫、這里用戶可能會忽略此版本站辉,這時候我要存儲一個版本號,用戶忽略的版本號,如果用戶的是主動更新的饰剥,都要提示殊霞。如果不是主動更新的判斷一下,判斷一下汰蓉。
  • 4绷蹲、在步驟2以后 如果需要更新,先判斷步驟3里邊存儲的版本號古沥,比較大小瘸右, 如果用戶忽略更新這個版本,那就不更新了岩齿;如果用戶沒有忽略過此版本太颤,繼續(xù)往下
  • 5、這時候給用戶一個提示盹沈,展示內(nèi)容是有新版本是否更新龄章,更新內(nèi)容
  • 5.1 這里可能涉及到一個強制更新,如果是強制更新的乞封,那就要求做裙,用戶沒有更新,就退出程序肃晚;不是強制更新的锚贱,可以忽略此版本
  • 6、啟動下載服務(wù)
  • 7关串、下載完成之后的自動安裝
  • 8拧廊、不要忘記兼容7.0 在res/xml文件中添加 file_paths.xml 文件,并且在AndroidManifest.xml文件中添加

3晋修、代碼實現(xiàn)

這里使用DownLoadManager來做下載吧碾,在UpdateAppService類中。
在UpdateAppUtils檢測是否要下載

UpdateAppUtils.java


import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.support.v4.app.ActivityCompat;

import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;


import com.example.lql.updateappdemo.message.EventMessage;
import com.example.lql.updateappdemo.service.UpdateAppService;
import com.example.lql.updateappdemo.utils.FinalData;
import com.example.lql.updateappdemo.utils.PreferenceUtils;
import com.example.lql.updateappdemo.utils.T;

import org.greenrobot.eventbus.EventBus;

import java.lang.ref.WeakReference;


/**
 * 類描述:版本更新工具類<br>
 * 這里使用DownLoadManager來做下載墓卦,在UpdateAppService類中<br>
 * 在UpdateAppUtils檢測是否要下載<br>
 * 邏輯關(guān)系說明:<br>
 * 1倦春、請求數(shù)據(jù)  拿到后臺的apk的版本號、版本名落剪、更新內(nèi)容睁本、下載地址(可能還會包括是否強制更新的標志位)<br>
 * 這里有一點需要注意,如果有強制更新的概念忠怖,這時候請求數(shù)據(jù)的時候要給服務(wù)器你當前的版本號呢堰,<br>
 * 如果你現(xiàn)在的版本號是1  ,用戶很久沒有操作脑又,期間版本更新了2(強制更新)暮胧、3(不強制更新)。這時候用戶打開版本號為1的版本问麸,應(yīng)該是要強制更新的往衷。<br>
 * 2、和本地的apk版本號對比時候需要更新<br>
 * 3严卖、這里用戶可能會忽略此版本席舍,這時候我要存儲一個版本號,用戶忽略的版本號哮笆,如果用戶的是主動更新的来颤,都要提示。如果不是主動更新的判斷一下稠肘,判斷一下福铅。<br>
 * 4、在步驟2以后   如果需要更新项阴,先判斷步驟3里邊存儲的版本號滑黔,比較大小,<br>
 * 如果用戶忽略更新這個版本环揽,那就不更新了略荡;如果用戶沒有忽略過此版本,繼續(xù)往下<br>
 * 5歉胶、這時候給用戶一個提示汛兜,展示內(nèi)容是有新版本是否更新,更新內(nèi)容<br>
 * 5.1  這里可能涉及到一個強制更新通今,如果是強制更新的粥谬,那就要求,用戶沒有更新衡创,就退出程序帝嗡,不是強制更新的,可以忽略此版本<br>
 * 6璃氢、啟動下載服務(wù)<br>
 * 7哟玷、下載完成之后的自動安裝<br>
 * 8、不要忘記兼容7.0  在res/xml文件中添加    file_paths.xml 文件一也,并且在AndroidManifest.xml文件中添加
 * 使用說明:<br>
 * 在外部直接調(diào)用UpdateApp()方法<br>
 * 作  者:李清林<br>
 * 時  間:2017.5.12<br>
 * 更新時間:2018.5.2<br>
 * 修改備注:增加申請權(quán)限功能<br>
 * 更新時間:2018.7.23<br>
 * 修改備注:兼容8.0<br>
 */
public class UpdateAppUtils {
    /**
     * 當前版本號
     */
    private static int mVersionCode = 0;
    private static Activity mActivity;
    private static Fragment mFragment;
    /**
     * 下載地址
     */
    private static String mDownloadUrl = "";
    /**
     * 寫SD卡權(quán)限
     */
    public static int REQUEST_PERMISSION_SDCARD_6_0 = 0x56;
    /**
     * 允許安裝未知來源權(quán)限
     */
    public static int REQUEST_PERMISSION_SDCARD_8_0 = 0x58;
    /**
     * 去設(shè)置頁面
     */
    public static int REQUEST_PERMISSION_SDCARD_SETTING = 0x57;
    /**
     * 服務(wù)器的版本號碼
     */
    private static int serviceVersionCode = 0;
    /**
     * 服務(wù)器的版本號名稱
     */
    private static String serviceVersionName = "";
    /**
     * 是否強制更新
     */
    private static boolean IsUpdate = false;
    /**
     * 更新說明
     */
    private static String content = "";

    public UpdateAppUtils() {
    }

    /**
     * @param activity       上下文
     * @param newVersionCode 服務(wù)器的版本號
     * @param newVersionName 服務(wù)器的版本名稱
     * @param content        更新了的內(nèi)容
     * @param downUrl        下載地址
     * @param IsUpdate       是否強制更新
     * @param IsToast        是否提示用戶當前已經(jīng)是最新版本
     */
    public static void UpdateApp(Activity activity, Fragment fragment, int newVersionCode, String newVersionName,
                                 String content, String downUrl, boolean IsUpdate, boolean IsToast) {

        UpdateAppUtils.mActivity = activity;
        UpdateAppUtils.mFragment = fragment;
        UpdateAppUtils.mDownloadUrl = downUrl;
        UpdateAppUtils.serviceVersionCode = newVersionCode;
        UpdateAppUtils.serviceVersionName = newVersionName;
        UpdateAppUtils.IsUpdate = IsUpdate;
        UpdateAppUtils.content = content;
        //首先拿到當前的版本號和版本名
        try {
            UpdateAppUtils.mActivity = activity;
            PackageManager pm = mActivity.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(mActivity.getPackageName(), 0);
            mVersionCode = pi.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }

        if (mVersionCode < serviceVersionCode) {
            //第2步驟
            if (PreferenceUtils.getInt(FinalData.VERSIONCODE, 0) < serviceVersionCode || IsToast) {
                //第3步驟
                //這時候要去更新题诵,展示下載的對話框
                showDownLoadDialog();
            }
        } else {
            if (IsToast) {
                T.shortToast(mActivity, "當前已是最新版本");
            }
        }
    }


    /**
     * 下載對話框叫胖,并且請求權(quán)限
     */
    private static void showDownLoadDialog() {
        AlertDialog dialog = new AlertDialog.Builder(mActivity).
                setCancelable(false).
                setTitle("更新到 " + serviceVersionName).
                setMessage(content).
                setPositiveButton("下載", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface arg0, int arg1) {//第6步驟绷跑,下載
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                                checkPermission();
                            } else {
                                downLoad();
                            }
                        } else {
                            downLoad();
                        }
                    }
                }).
                setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //這里涉及到下載的強制更新,是不是強制更新   強制更新吩谦,點取消按鈕芭挽,退出程序
                        if (IsUpdate) {
                            T.shortToast(mActivity, "此版本需要更新,程序即將退出");
                            MyHandler myHandler = new MyHandler(new UpdateAppUtils());
                            myHandler.sendEmptyMessageDelayed(0, 1000 * 3);
                        } else {
                            PreferenceUtils.setInt(FinalData.VERSIONCODE, serviceVersionCode);
                            dialog.dismiss();
                        }
                    }
                }).
                create();
        dialog.show();
    }

    /**
     * 檢查6.0權(quán)限
     */
    private static void checkPermission() {
        if (ContextCompat.checkSelfPermission(mActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                checkInstallPermission();
            } else {
                //有權(quán)限去下載
                downLoad();
            }
        } else {
            // 該方法在用戶上次拒絕后調(diào)用,因為已經(jīng)拒絕了這次你還要申請授權(quán)你得給用戶解釋一波
            // 一般建議彈個對話框告訴用戶 該方法在6.0之前的版本永遠返回的是fasle
            if (ActivityCompat.shouldShowRequestPermissionRationale(mActivity, Manifest.permission.READ_EXTERNAL_STORAGE)) {
                getPermissionDialog(0);
            } else {// 申請授權(quán)
                if (mFragment != null) {
                    mFragment.requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION_SDCARD_6_0);
                } else {
                    ActivityCompat.requestPermissions(mActivity,
                            new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION_SDCARD_6_0);
                }
            }
        }
    }

    /**
     * 檢查8.0權(quán)限
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    private static void checkInstallPermission() {
        boolean b = mActivity.getPackageManager().canRequestPackageInstalls();
        if (b) {
            downLoad();
        } else {
            getPermissionDialog(1);
        }
    }

    /**
     * 正式去下載
     */
    private static void downLoad() {
        T.shortToast(mActivity, "正在下載...");
        //如果要更新缴挖,并且是強制更新映屋,這里發(fā)一個事件,不讓其他的頁面做操作了
        EventBus.getDefault().post(new EventMessage(EventMessage.CheckApp, true));
        new Thread(new Runnable() {
            @Override
            public void run() {
                //啟動服務(wù)
                Intent service = new Intent(mActivity, UpdateAppService.class);
                service.putExtra("downLoadUrl", mDownloadUrl);
                mActivity.startService(service);
            }
        }).start();
    }


    /**
     * 申請權(quán)限的對話框
     *
     * @param type 0:6.0   1:8.0
     */
    private static void getPermissionDialog(int type) {
        String message = "更新軟件需要您允許我們獲取您的數(shù)據(jù)讀寫權(quán)限,否則將無法更新";
        if (type == 1) {
            message = "為了正常升級APP倔毙,請點擊設(shè)置-高級設(shè)置-允許安裝未知來源應(yīng)用乙濒,本功能只限用于APP版本升級";
        }
        AlertDialog dialog = new AlertDialog.Builder(mActivity).
                setCancelable(false).
                setTitle("權(quán)限提醒").
                setMessage(message).
                setPositiveButton("權(quán)限設(shè)置", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface arg0, int arg1) {//第6步驟,下載

                        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                        Uri uri = Uri.fromParts("package", mActivity.getPackageName(), null);

                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                            //注意這個是8.0新API,直接跳轉(zhuǎn)到允許安裝位置來源的頁面
                            intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, uri);
                        } else {
                            intent.setData(uri);
                        }

                        if (mFragment != null) {
                            mFragment.startActivityForResult(intent, REQUEST_PERMISSION_SDCARD_SETTING);
                        } else {
                            mActivity.startActivityForResult(intent, REQUEST_PERMISSION_SDCARD_SETTING);
                        }
                    }
                }).
                setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //這里涉及到下載的強制更新傻丝,是不是強制更新   強制更新,點取消按鈕滤愕,退出程序
                        if (IsUpdate) {
                            T.shortToast(mActivity, "此版本需要更新间影,程序即將退出");
                            MyHandler myHandler = new MyHandler(new UpdateAppUtils());
                            myHandler.sendEmptyMessageDelayed(0, 3000);
                        } else {
                            PreferenceUtils.setInt(FinalData.VERSIONCODE, serviceVersionCode);
                            dialog.dismiss();
                        }
                    }
                }).
                create();
        dialog.show();
    }


    /**
     * 申請權(quán)限返回結(jié)果時調(diào)用,用戶是否同意
     *
     * @param requestCode  之前申請權(quán)限的請求碼
     * @param permissions  申請的權(quán)限
     * @param grantResults 依次申請的結(jié)果
     */
    public static void onActRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                                     @NonNull int[] grantResults, Activity activity, Fragment fragment) {
        mActivity = activity;
        mFragment = fragment;
        if (requestCode == REQUEST_PERMISSION_SDCARD_6_0) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    checkInstallPermission();
                } else {
                    //有權(quán)限去下載
                    downLoad();
                }
            } else {
                getPermissionDialog(0);
            }
        } else if (requestCode == REQUEST_PERMISSION_SDCARD_8_0) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                downLoad();
            } else {
                getPermissionDialog(1);
            }
        } else if (requestCode == REQUEST_PERMISSION_SDCARD_SETTING) {
            //設(shè)置頁面
            checkPermission();
        }
    }

    static class MyHandler extends Handler {
        WeakReference<UpdateAppUtils> mWeakReference;

        public MyHandler(UpdateAppUtils mUpdateAppUtils) {
            mWeakReference = new WeakReference<UpdateAppUtils>(mUpdateAppUtils);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            exitApp();
        }
    }

    /**
     * 這里使用EventBus像Activity發(fā)送消息,當然你也可以使用廣播
     */
    private static void exitApp() {
        EventBus.getDefault().post(new EventMessage(EventMessage.Exitapp));
    }
}

UpdateAppService.java

import android.app.DownloadManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import android.support.v4.content.FileProvider;
import android.text.TextUtils;

import com.example.lql.updateappdemo.R;
import com.example.lql.updateappdemo.utils.T;

import java.io.File;

/**
 * 類描述:下載服務(wù)
 * 作  者:李清林
 * 時  間:
 * 修改備注:兼容7.0
 */
public class UpdateAppService extends Service {

    public UpdateAppService() {

    }

    /**
     * 安卓系統(tǒng)下載類
     **/
    DownloadManager manager;

    /**
     * 接收下載完的廣播
     **/
    DownloadCompleteReceiver receiver;

    /**
     * 初始化下載器
     **/
    private void initDownManager(String downLoadUrl) {
        manager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        receiver = new DownloadCompleteReceiver();

        if (TextUtils.isEmpty(downLoadUrl)) {
            T.shortToast(UpdateAppService.this, "下載地址為空");
            return;
        }

        //設(shè)置下載地址
        Uri parse = Uri.parse(downLoadUrl);
        DownloadManager.Request down = new DownloadManager.Request(parse);
        down.setTitle(getResources().getString(R.string.app_name) + ".apk");
        // 設(shè)置允許使用的網(wǎng)絡(luò)類型茄茁,這里是移動網(wǎng)絡(luò)和wifi都可以
        down.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);
        // 下載時魂贬,通知欄顯示途中
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            down.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
        }
        // 顯示下載界面
        down.setVisibleInDownloadsUi(true);
        // 設(shè)置下載后文件存放的位置
        String apkName = parse.getLastPathSegment();
        down.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DOWNLOADS, apkName);
        // 將下載請求放入隊列
        manager.enqueue(down);
        //注冊下載廣播
        registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        String downLoadUrl = intent.getStringExtra("downLoadUrl");
        // 調(diào)用下載
        initDownManager(downLoadUrl);
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        // 注銷下載廣播
        if (receiver != null) {
            unregisterReceiver(receiver);
        }
        super.onDestroy();
    }


    /**
     * 接受下載完成后的intent
     */
    class DownloadCompleteReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
            if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {
                DownloadManager.Query query = new DownloadManager.Query();
                // 在廣播中取出下載任務(wù)的id
                long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);
                query.setFilterById(id);
                Cursor c = manager.query(query);
                if (c.moveToFirst()) {
                    int fileUriIdx = c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);
                    String fileUri = c.getString(fileUriIdx);
                    String fileName = null;
                    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
                        if (fileUri != null) {
                            fileName = Uri.parse(fileUri).getPath();
                        }
                    } else {
                        //Android 7.0以上的方式:請求獲取寫入權(quán)限,這一步報錯
                        int fileNameIdx = c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME);
                        fileName = c.getString(fileNameIdx);
                    }
                    if (null != fileName && !TextUtils.isEmpty(fileName)) {
                        install1(context, fileName);
                    }
                    //停止服務(wù)并關(guān)閉廣播
                    UpdateAppService.this.stopSelf();
                }
            }
        }

        private boolean install1(Context context, String filePath) {
            Intent i = new Intent(Intent.ACTION_VIEW);
            File file = new File(filePath);
            if (file != null && file.length() > 0 && file.exists() && file.isFile()) {
                //判斷是否是AndroidN以及更高的版本
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    Uri contentUri = FileProvider.getUriForFile(context, context.getPackageName() + ".provider", file);
                    i.setDataAndType(contentUri, "application/vnd.android.package-archive");
                } else {
                    i.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
                    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                }
                context.startActivity(i);
                return true;
            }
            return false;
        }
    }

}

最后別忘了在清單文件中注冊服務(wù)裙顽。

AndroidManifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.lql.updateappdemo">

    <!--讀存儲的權(quán)限-->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <!--寫存儲的權(quán)限-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <!--8.0未知來源的應(yīng)用權(quán)限 更新App-->
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

    <application
        android:name=".application.MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <!--兼容7.x-->
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.lql.updateappdemo.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

        <activity android:name=".ui.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".service.UpdateAppService"
            android:enabled="true"></service>
    </application>
</manifest>

4、更新內(nèi)容(2018.7.23):

這次更新為了兼容android8.0。在8.0中增加了允許安裝位置來源應(yīng)用的權(quán)限娱两。使用時需要首先在AndroidManifest文件中添加響應(yīng)權(quán)限阻荒,然后在去檢查權(quán)限。整體過程和android6.0動態(tài)申請權(quán)限的過程相似漩怎,但是在具體代碼中有所區(qū)別萝嘁。具體使用請查看代碼UpdateAppUtils .checkInstallPermission()方法。這里提一句扬卷,在申請權(quán)限的時候牙言,不會像6.0權(quán)限那樣有系統(tǒng)提示,這里需要自己去寫一個dialog去提示用戶怪得。并且跳轉(zhuǎn)到設(shè)置頁面中手動開啟咱枉。具體跳轉(zhuǎn)頁面邏輯請查看UpdateAppUtils.getPermissionDialog()方法卑硫。可以直接跳轉(zhuǎn)到具體頁面蚕断。

5欢伏、就這樣結(jié)束啦。附上源碼下載地址(demo 已更新):

https://github.com/LiQinglin007/UpdateAppDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末亿乳,一起剝皮案震驚了整個濱河市硝拧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌葛假,老刑警劉巖障陶,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異聊训,居然都是意外死亡抱究,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門带斑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鼓寺,“玉大人,你說我怎么就攤上這事勋磕÷韬颍” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵挂滓,是天一觀的道長州丹。 經(jīng)常有香客問我,道長杂彭,這世上最難降的妖魔是什么墓毒? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮亲怠,結(jié)果婚禮上所计,老公的妹妹穿的比我還像新娘。我一直安慰自己团秽,他們只是感情好主胧,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著习勤,像睡著了一般踪栋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上图毕,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天夷都,我揣著相機與錄音,去河邊找鬼予颤。 笑死囤官,一個胖子當著我的面吹牛冬阳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播党饮,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼肝陪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了刑顺?” 一聲冷哼從身側(cè)響起氯窍,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蹲堂,沒想到半個月后狼讨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡贯城,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了霹娄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片能犯。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖犬耻,靈堂內(nèi)的尸體忽然破棺而出踩晶,到底是詐尸還是另有隱情,我是刑警寧澤枕磁,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布渡蜻,位于F島的核電站,受9級特大地震影響计济,放射性物質(zhì)發(fā)生泄漏茸苇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一沦寂、第九天 我趴在偏房一處隱蔽的房頂上張望学密。 院中可真熱鬧,春花似錦传藏、人聲如沸腻暮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哭靖。三九已至,卻和暖如春侈离,著一層夾襖步出監(jiān)牢的瞬間试幽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工卦碾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留抡草,地道東北人饰及。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像康震,于是被迫代替她去往敵國和親燎含。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,110評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理腿短,服務(wù)發(fā)現(xiàn)屏箍,斷路器,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 《裕語言》速成開發(fā)手冊3.0 官方用戶交流:iApp開發(fā)交流(1) 239547050iApp開發(fā)交流(2) 10...
    葉染柒丶閱讀 26,736評論 5 19
  • 《ilua》速成開發(fā)手冊3.0 官方用戶交流:iApp開發(fā)交流(1) 239547050iApp開發(fā)交流(2) 1...
    葉染柒丶閱讀 10,722評論 0 11
  • 這是第三篇文章橘忱,也許可以稱為流水賬赴魁,感謝自己的厚愛 一個熟人,畢業(yè)離開家鄉(xiāng)钝诚,在北方小城結(jié)婚生子颖御,預(yù)想和睦小康其樂...
    挨踢的瑜伽行者閱讀 1,081評論 2 0