Android 捕獲異常exception燃异,GreenDao存取携狭,上傳服務器(后臺)或 發(fā)送指定郵箱中

app上線后并不能保證程序不會崩潰,為了更好的優(yōu)化項目回俐,需要獲取異常日志逛腿,供開發(fā)人員修改bug稀并,所以希望在程序崩潰時,能捕獲異常单默,并保存異常碘举。這些需求可以用第三方的框架,比如騰訊的bugly雕凹,github上也有些開源的等殴俱,但如果不想經(jīng)過第三方的后臺,或者想自己寫個效果枚抵,可以考慮采用本文提供的方法---考慮網(wǎng)絡異常時线欲,崩潰信息不能上傳后臺,用數(shù)據(jù)庫存儲異常日志汽摹,當開啟應用或有網(wǎng)絡時李丰,上傳異常日志。同時逼泣,本文也提供了通過郵件來接收異常日志的方法趴泌。

地址:歡迎star https://github.com/comeonwyf/SteveCrashHandleDemo

一、捕獲異常部分

第一步:定義 CrashException 模型拉庶,用于異常信息對象的創(chuàng)建

該類說明:注釋代碼是greendao的方式(不熟悉嗜憔,可先去了解greendao的簡單使用方法)

@Entity
public class CrashException {
    @Transient
    public static final int TYPE_TOSEND = 1;
    @Transient
    public static final int TYPE_SENDING = 2;

    @Id
    private Long crashId;
    private String crashTime;
    private String appName;
    private String appVersion;
    private String OSVersion;
    private String mobileBrand;
    private String mobileModel;
    private String crashExceptionInfor;
    private int sendStat = 1;//1表示待發(fā)送,2表示正在發(fā)送

    @Generated(hash = 1063750127)
    public CrashException(Long crashId, String crashTime, String appName,
            String appVersion, String OSVersion, String mobileBrand,
            String mobileModel, String crashExceptionInfor, int sendStat) {
        this.crashId = crashId;
        this.crashTime = crashTime;
        this.appName = appName;
        this.appVersion = appVersion;
        this.OSVersion = OSVersion;
        this.mobileBrand = mobileBrand;
        this.mobileModel = mobileModel;
        this.crashExceptionInfor = crashExceptionInfor;
        this.sendStat = sendStat;
    }

    @Generated(hash = 2138202335)
    public CrashException() {
    }

    public void setCrashExceptionInfor(String crashExceptionInfor) {
        this.crashExceptionInfor = crashExceptionInfor;
    }

    public static Long creatCrashId(){
        return (Long)((System.currentTimeMillis()+17)/3);
    }

    public Long getCrashId() {
        return this.crashId;
    }

    public void setCrashId(Long crashId) {
        this.crashId = crashId;
    }

    public String getCrashTime() {
        return this.crashTime;
    }

    public void setCrashTime(String crashTime) {
        this.crashTime = crashTime;
    }

    public String getAppName() {
        return this.appName;
    }

    public void setAppName(String appName) {
        this.appName = appName;
    }

    public String getAppVersion() {
        return this.appVersion;
    }

    public void setAppVersion(String appVersion) {
        this.appVersion = appVersion;
    }

    public String getOSVersion() {
        return this.OSVersion;
    }

    public void setOSVersion(String OSVersion) {
        this.OSVersion = OSVersion;
    }

    public String getMobileBrand() {
        return this.mobileBrand;
    }

    public void setMobileBrand(String mobileBrand) {
        this.mobileBrand = mobileBrand;
    }

    public String getMobileModel() {
        return this.mobileModel;
    }

    public void setMobileModel(String mobileModel) {
        this.mobileModel = mobileModel;
    }

    public String getCrashExceptionInfor() {
        return this.crashExceptionInfor;
    }

    public int getSendStat() {
        return this.sendStat;
    }

    public void setSendStat(int sendStat) {
        this.sendStat = sendStat;
    }

}
第二步:自定義AppUncaughtExceptionHandler 類實現(xiàn)Thread.UncaughtExceptionHandler 接口 此處會獲得崩潰異常

該類主要:采用單例模式氏仗,初始化時吉捶,將該類設置為默認處理器,用于監(jiān)聽異常的回調皆尔,獲取異常呐舔。獲取異常后,存入數(shù)據(jù)庫慷蠕,并將異常信息發(fā)送給指定郵箱珊拼。

public class AppUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {

    //用于輸出異常信息
    private PrintWriter pw;
    private StringWriter sw;

    //構造方法私有,防止外部構造多個實例流炕,即采用單例模式
    private AppUncaughtExceptionHandler() {
    }
    //程序的Context對象
    private Context applicationContext;
    private volatile boolean crashing;

    /**
     * 日期格式器
     */
    private DateFormat mFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    /**
     * 系統(tǒng)默認的UncaughtException處理類
     */
    private Thread.UncaughtExceptionHandler mDefaultHandler;
    /**
     * 單例
     */
    private static AppUncaughtExceptionHandler sAppUncaughtExceptionHandler;
    public static synchronized AppUncaughtExceptionHandler getInstance() {
        if (sAppUncaughtExceptionHandler == null) {
            synchronized (AppUncaughtExceptionHandler.class) {
                if (sAppUncaughtExceptionHandler == null) {
                    sAppUncaughtExceptionHandler = new AppUncaughtExceptionHandler();
                }
            }
        }
        return sAppUncaughtExceptionHandler;
    }

    /**
     * 初始化
     * @param context
     */
    public void init(Context context) {
        applicationContext = context.getApplicationContext();
        crashing = false;
        sw =  new StringWriter();
        pw = new PrintWriter(sw);
        //獲取系統(tǒng)默認的UncaughtException處理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        //設置該CrashHandler為程序的默認處理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    //捕獲異常
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (crashing) {
            return;
        }
        crashing = true;
        // 打印異常信息
        ex.printStackTrace();
        // 我們沒有處理異常 并且默認異常處理不為空 則交給系統(tǒng)處理
        if (!handlelException(ex) && mDefaultHandler != null) {
            // 系統(tǒng)處理
            mDefaultHandler.uncaughtException(thread, ex);
        }
        
        byebye();
    }

    private void byebye() {
        SystemClock.sleep(2000);
        android.os.Process.killProcess(android.os.Process.myPid());
        System.exit(0);
    }

    private boolean handlelException(Throwable ex) {
        if (ex == null) {
            return false;
        }
        try {
            //處理異常信息澎现,并存入數(shù)據(jù)庫,然后發(fā)送至服務器
            handleCrashReport(ex);
            // 提示對話框
            showPatchDialog();

        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     *處理異常信息每辟,并存入數(shù)據(jù)庫昔头,然后發(fā)送至郵箱
     * @param ex
     * @return
     */
    private String handleCrashReport(Throwable ex) {
        String exceptionStr = "";
        PackageInfo pinfo = CrashApp.getInstance().getLocalPackageInfo();
        CrashException crashException = null;
        if (pinfo != null) {
            if (ex != null) {
                //得到異常全部信息
                if(sw==null){
                    sw = new StringWriter();
                }
                if(pw==null){
                    pw = new PrintWriter(sw);
                }
                ex.printStackTrace(pw);
                String errorStr = sw.toString();

                if (TextUtils.isEmpty(errorStr)) {
                    errorStr = ex.getMessage();
                }
                if (TextUtils.isEmpty(errorStr)) {
                    errorStr = ex.toString();
                }
                exceptionStr = errorStr;

                //存入數(shù)據(jù)庫中
                crashException = new CrashException(CrashException.creatCrashId(),
                        mFormatter.format(new Date()),getApplicationName(applicationContext),pinfo.versionName,
                        Build.VERSION.RELEASE,Build.MANUFACTURER,Build.MODEL,errorStr,CrashException.TYPE_TOSEND);

                Log.e("print", "異常信息: "+ crashException.getCrashExceptionInfor());
                //發(fā)送到郵箱
                SendExceptionManager.getInstance().sendToEmail(crashException);

            } else {
                exceptionStr = "no exception. Throwable is null";
            }

            return exceptionStr;
        } else {
            return "";
        }
    }

    //崩潰后彈出提示框,是否取消還是重啟
    private void showPatchDialog() {
        Intent intent = PatchDialogActivity.newIntent(applicationContext, getApplicationName(applicationContext), null);
        applicationContext.startActivity(intent);
    }
    
    //獲取應用名稱
    private String getApplicationName(Context context) {
        PackageManager packageManager = context.getPackageManager();
        ApplicationInfo applicationInfo = null;
        String name = null;
        try {
            applicationInfo = packageManager.getApplicationInfo(
                    context.getApplicationInfo().packageName, 0);
            name = (String) packageManager.getApplicationLabel(applicationInfo);
        } catch (final PackageManager.NameNotFoundException e) {
            String[] packages = context.getPackageName().split(".");
            name = packages[packages.length - 1];
        }
        return name;
    }
}

此處感謝:http://www.reibang.com/p/fb28a5322d8a

二影兽、定義CrashApp繼承Application(注意需在AndroidManifest.xml引用此App)

該類主要是完成GreenDao 和AppUncaughtExceptionHandler初始化工作

public class CrashApp extends Application {

    private static CrashApp mInstance = null;
    private static DaoSession daoSession;

    public static CrashApp getInstance() {
        if (mInstance == null) {
            throw new IllegalStateException("Application is not created.");
        }
        return mInstance;
    }

    public static DaoSession getDaoInstance(){
        if (daoSession == null) {
            throw new IllegalStateException("GreenDao is not created.");
        }
        return daoSession;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;

        // 捕捉異常初始化
        AppUncaughtExceptionHandler crashHandler = AppUncaughtExceptionHandler.getInstance();
        crashHandler.init(getApplicationContext());

        //初始化greendao數(shù)據(jù)庫
        setupDatabase();

    }

    /**
     * 獲取自身App安裝包信息
     * @return
     */
    public PackageInfo getLocalPackageInfo() {
        return getPackageInfo(getPackageName());
    }

    /**
     * 獲取App安裝包信息
     * @return
     */
    public PackageInfo getPackageInfo(String packageName) {
        PackageInfo info = null;
        try {
            info = getPackageManager().getPackageInfo(packageName, 0);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return info;
    }

    private void setupDatabase() {
        //創(chuàng)建數(shù)據(jù)庫myDB.db
        DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(mInstance,"myDB.db");
        SQLiteDatabase db = devOpenHelper.getWritableDatabase();

        //獲取數(shù)據(jù)庫對象
        DaoMaster daoMaster = new DaoMaster(db);
        //獲取Dao對象的管理者
        daoSession = daoMaster.newSession();
    }
}

三揭斧、在應用的啟動界面,監(jiān)聽網(wǎng)絡狀態(tài),當網(wǎng)絡可用時讹开,檢查是否有需要發(fā)送的異常日志盅视,如果有,就發(fā)送旦万,發(fā)送成功后闹击,刪除對應日志

public class MainActivity extends AppCompatActivity {

    private NetworkBroadcastReceiver mReceiver;
    private boolean doing;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        registerNetworkReceiver();
    }


    public void onClick(View view){

        if(view.getId()== R.id.tv1){
            Intent intent = new Intent(this,SecondActivity.class);
            startActivity(intent);
        }

        if(view.getId()==R.id.tv2){
            String test = null;
            test.length();
        }


    }

    private void registerNetworkReceiver() {
        mReceiver = new NetworkBroadcastReceiver();
        IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        registerReceiver(mReceiver,filter);
        mReceiver.setOnNetChange(new NetworkBroadcastReceiver.NetEvent() {
            @Override
            public void onNetChange(int netMobile) {
                if(netMobile!=-1){
                    Log.e("print", "網(wǎng)絡變化:可用! ");
                    //網(wǎng)絡可以用的時候成艘,看看數(shù)據(jù)庫中是否有需要發(fā)送的錯誤日志
                    if(doing){
                      return;
                    }
                    doing = true;
                    List<CrashException> crashExceptionList = CrashExceptionHelper.getCrashExceptionList();
                    if(crashExceptionList!=null && crashExceptionList.size()!=0){
                        for(CrashException crashException : crashExceptionList){
                            crashException.setSendStat(CrashException.TYPE_SENDING);
                            SendExceptionManager.getInstance().sendToServer(crashException);
                        }

                    }
                    doing = false;
                }else {
                    Log.e("print", "網(wǎng)絡變化:不可用赏半! ");
                }

            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(mReceiver!=null){
           unregisterReceiver(mReceiver);
        }
    }
}

四、補充細節(jié)部分:(1)發(fā)送異常信息的管理類淆两;(2)發(fā)送郵件需要有三個依賴包(在提供的github項目源碼中有)

發(fā)送異常信息的管理類

public class SendExceptionManager {

    /**
     * 單例
     */
    private static SendExceptionManager mSendExceptionManager;
    private static ScheduledExecutorService scheduledThreadPool;
    public static Context applicationContext;

    private SendExceptionManager() {
        //創(chuàng)建定長的5個線程
        scheduledThreadPool = Executors.newScheduledThreadPool(5);

    }

    public static synchronized SendExceptionManager getInstance() {

        if (mSendExceptionManager == null) {
            synchronized (SendExceptionManager.class) {
                if (mSendExceptionManager == null) {
                    mSendExceptionManager = new SendExceptionManager();
                }
            }
        }
        return mSendExceptionManager;
    }

    //發(fā)送到郵箱
    public void sendToEmail(final CrashException crashException) {

        if (scheduledThreadPool == null) {
            scheduledThreadPool = Executors.newScheduledThreadPool(5);
        }

        scheduledThreadPool.execute(new Runnable() {
            @Override
            public void run() {

                //賬號密碼
                //用此郵箱來發(fā)送郵件(賬號:********@qq.com , 密碼:(開啟POP3/SMTP服務的授權碼))
                Mail mail = new Mail("填賬號", "填授權碼");

                //接受者郵箱 可以是多個

                mail.set_to(new String[]{"******@qq.com","******@hotmail.com"});
                //郵件來源
                mail.set_from("******@qq.com");
                //設置主題標題
                mail.set_subject(getApplicationName(CrashApp.getInstance()) + "錯誤日志");
                mail.setBody(crashException.toString());

                try {
                    if (mail.send()) {
                        Log.e("crashInfor: ", "發(fā)送郵件成功");
                    } else {
                        Log.e("crashInfor:", "發(fā)送郵件失敗");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        });

    }

    //發(fā)送到服務器(Ion)
    public void sendToServer(final CrashException crashException) {
        String device = crashException.getMobileBrand()+"--"+crashException.getMobileModel()+"--"+crashException.getOSVersion();
        Ion.with(CrashApp.getInstance())
                .load("url")//填寫后臺的的地址.
                .setTimeout(3000)
                .setBodyParameter("note", crashException.getCrashExceptionInfor())
                .setBodyParameter("device",device)
                .asJsonObject()
                .setCallback(new FutureCallback<JsonObject>() {
                    @Override
                    public void onCompleted(Exception e, JsonObject result) {
                        crashException.setSendStat(CrashException.TYPE_TOSEND);
                        if (e != null) {
                            CrashExceptionHelper.addCrashException(crashException);
                        }
                        if (result != null) {
                            Log.e("crashInfor:", "崩潰信息上傳成功:"+result);
                            Boolean stat = result.get("success").getAsBoolean();
                            if(stat){
                                CrashExceptionHelper.deleteCrashException(crashException);
                            }else {
                                CrashExceptionHelper.addCrashException(crashException);
                            }
                        } else {
                            Log.e("crashInfor:", "崩潰信息上傳失敗");
                            CrashExceptionHelper.addCrashException(crashException);
                        }

                    }
                });

    }
    
    private static String getApplicationName(Context context) {
        PackageManager packageManager = context.getPackageManager();
        ApplicationInfo applicationInfo = null;
        String name = null;
        try {
            applicationInfo = packageManager.getApplicationInfo(
                    context.getApplicationInfo().packageName, 0);
            name = (String) packageManager.getApplicationLabel(applicationInfo);
        } catch (final PackageManager.NameNotFoundException e) {
            String[] packages = context.getPackageName().split(".");
            name = packages[packages.length - 1];
        }
        return name;
    }
}

發(fā)送郵件:注意在Mail類中按實際 修改 _host = "smtp.qq.com"

public class Mail extends javax.mail.Authenticator{
    private String _user;
    private String _pass;

    private String[] _to;
    private String _from;

    private String _port;
    private String _sport;

    private String _host;

    private String _subject;
    private String _body;

    private boolean _auth;

    private boolean _debuggable;

    private Multipart _multipart;


    public Mail() {

         //以下三個才有可能需要改動断箫,我是用qq郵箱發(fā)送,就是如下設置秋冰,比如你也可以用126郵箱 則改為smtp.126.com
        // default smtp server
        _host = "smtp.qq.com";
        // default smtp port
        _port = "465";
        // default socketfactory port
        _sport = "465";

        _user = ""; // username
        _pass = ""; // password
        _from = ""; // email sent from
        _subject = ""; // email subject
        _body = ""; // email body

        _debuggable = false; // debug mode on or off - default off
        _auth = true; // smtp authentication - default on

        _multipart = new MimeMultipart();

        // There is something wrong with MailCap, javamail can not find a handler for the multipart/mixed part, so this bit needs to be added.
        MailcapCommandMap mc = (MailcapCommandMap) CommandMap.getDefaultCommandMap();
        mc.addMailcap("text/html;; x-java-content-handler=com.sun.mail.handlers.text_html");
        mc.addMailcap("text/xml;; x-java-content-handler=com.sun.mail.handlers.text_xml");
        mc.addMailcap("text/plain;; x-java-content-handler=com.sun.mail.handlers.text_plain");
        mc.addMailcap("multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed");
        mc.addMailcap("message/rfc822;; x-java-content-handler=com.sun.mail.handlers.message_rfc822");
        CommandMap.setDefaultCommandMap(mc);
    }

    public Mail(String user, String pass) {
        this();

        _user = user;
        _pass = pass;
    }

    public boolean send() throws Exception {
        Properties props = _setProperties();

        if(!_user.equals("") && !_pass.equals("") && _to.length > 0 && !_from.equals("") && !_subject.equals("") && !_body.equals("")) {
            Session session = Session.getInstance(props, this);

            MimeMessage msg = new MimeMessage(session);

            msg.setFrom(new InternetAddress(_from));

            InternetAddress[] addressTo = new InternetAddress[_to.length];
            for (int i = 0; i < _to.length; i++) {
                addressTo[i] = new InternetAddress(_to[i]);
            }
            msg.setRecipients(MimeMessage.RecipientType.TO, addressTo);

            msg.setSubject(_subject);
            msg.setSentDate(new Date());

            // setup message body
            BodyPart messageBodyPart = new MimeBodyPart();
            messageBodyPart.setText(_body);
            _multipart.addBodyPart(messageBodyPart);

            // Put parts in message
            msg.setContent(_multipart);

            // send email
            Transport.send(msg);

            return true;
        } else {
            return false;
        }
    }

    public void addAttachment(String filename) throws Exception {
        BodyPart messageBodyPart = new MimeBodyPart();
        DataSource source = new FileDataSource(filename);
        messageBodyPart.setDataHandler(new DataHandler(source));
        messageBodyPart.setFileName(filename);

        _multipart.addBodyPart(messageBodyPart);
    }

    @Override
    public PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication(_user, _pass);
    }

    private Properties _setProperties() {
        Properties props = new Properties();

        props.put("mail.smtp.host", _host);

        if(_debuggable) {
            props.put("mail.debug", "true");
        }

        if(_auth) {
            props.put("mail.smtp.auth", "true");
        }

        props.put("mail.smtp.port", _port);
        props.put("mail.smtp.socketFactory.port", _sport);
        props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        props.put("mail.smtp.socketFactory.fallback", "false");

        return props;
    }

    // the getters and setters
    public String getBody() {
        return _body;
    }

    public void setBody(String _body) {
        this._body = _body;
    }

    public void set_to(String[] _to) {
        this._to = _to;
    }

    public void set_from(String _from) {
        this._from = _from;
    }
    public void set_subject(String _subject) {
        this._subject = _subject;
    }
    // more of the getters and setters …..
}

五仲义、詳情請參考 github Demo 歡迎star

地址 https://github.com/comeonwyf/SteveCrashHandleDemo


水滴石穿!---Steve剑勾,從基礎做起埃撵!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市虽另,隨后出現(xiàn)的幾起案子暂刘,更是在濱河造成了極大的恐慌,老刑警劉巖捂刺,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谣拣,死亡現(xiàn)場離奇詭異,居然都是意外死亡叠萍,警方通過查閱死者的電腦和手機芝发,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門绪商,熙熙樓的掌柜王于貴愁眉苦臉地迎上來苛谷,“玉大人,你說我怎么就攤上這事格郁「沟睿” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵例书,是天一觀的道長锣尉。 經(jīng)常有香客問我,道長决采,這世上最難降的妖魔是什么自沧? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上拇厢,老公的妹妹穿的比我還像新娘爱谁。我一直安慰自己,他們只是感情好孝偎,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布访敌。 她就那樣靜靜地躺著,像睡著了一般衣盾。 火紅的嫁衣襯著肌膚如雪寺旺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天势决,我揣著相機與錄音阻塑,去河邊找鬼。 笑死徽龟,一個胖子當著我的面吹牛叮姑,可吹牛的內容都是我干的。 我是一名探鬼主播据悔,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼传透,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了极颓?” 一聲冷哼從身側響起朱盐,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎菠隆,沒想到半個月后兵琳,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡骇径,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年躯肌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片破衔。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡清女,死狀恐怖,靈堂內的尸體忽然破棺而出晰筛,到底是詐尸還是另有隱情嫡丙,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布读第,位于F島的核電站曙博,受9級特大地震影響,放射性物質發(fā)生泄漏怜瞒。R本人自食惡果不足惜父泳,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惠窄,春花似錦逝她、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至擒贸,卻和暖如春臀晃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背介劫。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工徽惋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人座韵。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓险绘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親誉碴。 傳聞我的和親對象是個殘疾皇子宦棺,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內容