Android后臺數(shù)據(jù)采集服務奔菘撸活的探索與發(fā)現(xiàn)

github源碼下載地址:https://github.com/geduo83/TrackDataCollect/blob/sync/app/src/main/java/com/geduo/datacollect/alive/

今天是2018年的最后一天牺勾,幸好元旦放假三天青柄,有時間來整理下這陣子研究的一點兒新東西冰评,把它記錄分享出來映胁,本來這篇文章應該是上個周要完成,但是由于工作忙沒有時間寫甲雅,就拖到今天了解孙,再過幾個小時就2019年了坑填,愿新的一年我們都能心想事成。

在上一篇文章Android車輛運動軌跡大數(shù)據(jù)采集最佳實踐(https://blog.csdn.net/geduo_83/article/details/84943984)這篇文章中我們講到弛姜,數(shù)據(jù)采集服務是一個持久的操作脐瑰,當采集服務進入后臺后,有可能被系統(tǒng)殺死的可能廷臼,可用通過賬號同步服務SysAdapter來提升進程的優(yōu)先級來降低采集服務被后臺殺死的幾率苍在。 本文共介紹了6種后臺服務保活方案荠商,下文將逐一介紹寂恬。

1.賬號同步服務SysAdapter保活

  • 1.1 通過ContentProvider實現(xiàn)數(shù)據(jù)同步
public class SyncContentProvider extends ContentProvider {
    public static final String CONTENT_URI_BASE = "content://" + SyncControl.CONTENT_AUTHORITY;
    public static final String TABLE_NAME = "data";
    public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_BASE + "/" + TABLE_NAME);

    @Override
    public boolean onCreate() {
        return true;
    }

    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
        return new String();
    }

    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }
}

image.gif
  • 1.2 關(guān)聯(lián)SyncAdapter通信服務
public class SyncAdapterService extends Service {
    private static final Object lock = new Object();
    private static SyncAdapter mSyncAdapter = null;
    private static int sync_count = 0;
    @Override
    public void onCreate() {
        synchronized (lock) {
            if (mSyncAdapter == null) {
                mSyncAdapter = new SyncAdapter(getApplicationContext(), true);
            }
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mSyncAdapter.getSyncAdapterBinder();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public void onTaskRemoved(Intent rootIntent) {
        super.onTaskRemoved(rootIntent);
    }

    static class SyncAdapter extends AbstractThreadedSyncAdapter {
        public SyncAdapter(Context context, boolean autoInitialize) {
            super(context, autoInitialize);
        }

        @Override
        public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider,
                SyncResult syncResult) {
            // 具體的同步操作莱没,這里主要是為了提高進程優(yōu)先級
            Log.v("MYTAG", "onPerformSync start...");
            SyncSharedPreference.getInstance(getContext()).addSyncTime("sync_time"+(++sync_count));
        }
    }
}

image.gif
  • 1.3 使用Sync服務
public class SyncControl {
    public static final long SYNC_FREQUENCY = 5; // 1 hour (in seconds)
    public static final String CONTENT_AUTHORITY = "com.geduo.datacollect";
    public static final String ACCOUNT_TYPE = "com.geduo.datacollect.account";

    @TargetApi(Build.VERSION_CODES.FROYO)
    public static void createSyncAccount(Context context) {
        Log.v("MYTAG", "createSyncAccount start...");
        Account account = SyncAccountService.getAccount(ACCOUNT_TYPE);
        AccountManager accountManager = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
        if (accountManager.addAccountExplicitly(account, null, null)) {
            ContentResolver.setIsSyncable(account, CONTENT_AUTHORITY, 1);
            ContentResolver.setSyncAutomatically(account, CONTENT_AUTHORITY, true);
            ContentResolver.addPeriodicSync(account, CONTENT_AUTHORITY, new Bundle(), SYNC_FREQUENCY);
        }
    }
    public static void triggerRefresh() {
        Log.v("MYTAG", "triggerRefresh start...");
        Bundle b = new Bundle();
        // Disable sync backoff and ignore sync preferences. In other words...perform sync NOW!
        b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
        b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
        ContentResolver.requestSync(
                SyncAccountService.getAccount(ACCOUNT_TYPE), // Sync account
                CONTENT_AUTHORITY,                 // Content authority
                b);                                             // Extras
    }
}

image.gif

2.Notication背跞猓活

  • 2.1 創(chuàng)建前臺守護服務DaemonService
public class DaemonService extends Service {
    private static final String TAG = DaemonService.class.getSimpleName();
    public static final int NOTICE_ID = 100;

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

    @Override
    public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            Notification.Builder builder = new Notification.Builder(this);
            builder.setSmallIcon(R.mipmap.ic_launcher);
            builder.setContentTitle("DataCollect");
            builder.setContentText("DataCollect runing...");
            startForeground(NOTICE_ID, builder.build());

            Intent intent = new Intent(this, CancelService.class);
            startService(intent);
        } else {
            startForeground(NOTICE_ID, new Notification());
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            NotificationManager mManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            mManager.cancel(NOTICE_ID);
        }
        Intent intent = new Intent(getApplicationContext(), DaemonService.class);
        startService(intent);
    }
}

image.gif
  • 2.2 創(chuàng)建一個取消通知圖標的服務CancelService,讓用戶在前臺無感知
public class CancelService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
            Notification.Builder builder = new Notification.Builder(this);
            builder.setSmallIcon(R.mipmap.ic_launcher);
            startForeground(DaemonService.NOTICE_ID, builder.build());
            new Thread(new Runnable() {
                @Override
                public void run() {
                    SystemClock.sleep(1000);
                    stopForeground(true);
                    NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                    manager.cancel(DaemonService.NOTICE_ID);
                    stopSelf();
                }
            }).start();
        }
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

image.gif
  • 2.3 服務的注冊
<service android:name=".alive.notifiy.DaemonService"
                 android:enabled="true"
                 android:exported="true"
                 android:process=":daemon_service"/>

<service android:name=".alive.notifiy.CancelService"
                 android:enabled="true"
                 android:exported="true"
                 android:process=":service"/>

image.gif
  • 2.4 服務開啟
  private void startDaemonService() {
        Intent intent = new Intent(this, DaemonService.class);
        startService(intent);
      }

image.gif

3.通過1像素Activity笔味悖活

  • 3.1 創(chuàng)建1像素透明Activity
public class SinglePixelActivity extends AppCompatActivity {
    private static final String TAG = "SinglePixelActivity";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Window mWindow = getWindow();
        mWindow.setGravity(Gravity.LEFT | Gravity.TOP);
        WindowManager.LayoutParams attrParams = mWindow.getAttributes();
        attrParams.x = 0;
        attrParams.y = 0;
        attrParams.height = 300;
        attrParams.width = 300;
        mWindow.setAttributes(attrParams);
        // 綁定SinglePixelActivity到ScreenManager
        ScreenManager.getScreenManagerInstance(this).setSingleActivity(this);
    }

    @Override
    protected void onDestroy() {
        if(! SystemUtils.isAPPALive(this,"com.geduo.datacollect")){
            Intent intentAlive = new Intent(this, MainActivity.class);
            intentAlive.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intentAlive);
        }
        super.onDestroy();
    }
}

image.gif
  • 3.2 創(chuàng)建屏幕鎖屏牙咏、開屏監(jiān)聽工具類
public class ScreenReceiverUtil {
    private Context mContext;
    // 鎖屏廣播接收器
    private SreenBroadcastReceiver mScreenReceiver;
    // 屏幕狀態(tài)改變回調(diào)接口
    private SreenStateListener mStateReceiverListener;

    public ScreenReceiverUtil(Context mContext){
        this.mContext = mContext;
    }
    public void setScreenReceiverListener(SreenStateListener mStateReceiverListener){
        mStateReceiverListener = mStateReceiverListener;
        // 動態(tài)啟動廣播接收器
        mScreenReceiver = new SreenBroadcastReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_USER_PRESENT);
        mContext.registerReceiver(mScreenReceiver,filter);
    }

    public void stopScreenReceiverListener(){
        mContext.unregisterReceiver(mScreenReceiver);
    }

    public  class SreenBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if(mStateReceiverListener == null){
                return;
            }
            if(Intent.ACTION_SCREEN_ON.equals(action)){         // 開屏
                mStateReceiverListener.onSreenOn();
            }else if(Intent.ACTION_SCREEN_OFF.equals(action)){  // 鎖屏
                mStateReceiverListener.onSreenOff();
            }else if(Intent.ACTION_USER_PRESENT.equals(action)){ // 解鎖
                mStateReceiverListener.onUserPresent();
            }
        }
    }
    // 監(jiān)聽sreen狀態(tài)對外回調(diào)接口
    public interface SreenStateListener {
        void onSreenOn();
        void onSreenOff();
        void onUserPresent();
    }
}

image.gif

4.雙服務保活

  • 4.1 創(chuàng)建主服務MainService
public class MainService extends Service {
    MyBinder binder;
    MyConn conn;

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

    @Override
    public void onCreate() {
        super.onCreate();
        Log.v("MYTAG", "main onCreate start...");
        binder = new MyBinder();
        conn = new MyConn();

        //設(shè)置該服務未前臺服務
        Intent targetIntent = new Intent(this, MainActivity.class);
        Notification notification = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
            PendingIntent intent = PendingIntent.getActivity(this, 0, targetIntent, 0);
            notification = new Notification.Builder(this).setSmallIcon(R.mipmap.ic_launcher).setContentTitle("DataCollect").setContentText("正在進行數(shù)據(jù)采集").setContentIntent(intent).build();
        } else {
            notification = new Notification();
            notification.flags = Notification.FLAG_ONGOING_EVENT;
            notification.flags |= Notification.FLAG_NO_CLEAR;
            notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
        }
        startForeground(1001, notification);
        bindService(new Intent(MainService.this, RemoteService.class), conn, Context.BIND_IMPORTANT);
    }

    class MyBinder extends IMyAidlInterface.Stub {
        @Override
        public String getServiceName() throws RemoteException {
            return MainService.class.getSimpleName();
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    class MyConn implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.v("MYTAG", "main onServiceConnected start...");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.v("MYTAG", "main onServiceDisconnected start...");
            startService(new Intent(MainService.this, RemoteService.class));
            bindService(new Intent(MainService.this, RemoteService.class), conn, Context.BIND_IMPORTANT);
        }
    }

    @Override
    public void onDestroy() {
        Log.v("onDestroy", "main onDestroy start...");
        startService(new Intent(MainService.this, RemoteService.class));
        bindService(new Intent(MainService.this, RemoteService.class), conn, Context.BIND_IMPORTANT);
    }
}

image.gif
  • 4.2 創(chuàng)建守護服務
public class RemoteService extends Service {
    MyConn conn;
    MyBinder binder;

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

    @Override
    public void onCreate() {
        super.onCreate();
        Log.v("MYTAG", "remote onCreate start...");
        conn = new MyConn();
        binder = new MyBinder();
        bindService(new Intent(this, MainService.class), conn, Context.BIND_IMPORTANT);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    class MyBinder extends IMyAidlInterface.Stub {
        @Override
        public String getServiceName() throws RemoteException {
            return RemoteService.class.getSimpleName();
        }
    }

    class MyConn implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.v("MYTAG", "remote onServiceConnected start...");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.v("MYTAG", "remote onServiceDisconnected start...");
            startService(new Intent(RemoteService.this, MainService.class));
            bindService(new Intent(RemoteService.this, MainService.class), conn, Context.BIND_IMPORTANT);
        }

    }

    @Override
    public void onDestroy() {
        Log.v("MYTAG", "remote onDestroy start...");
    }
}

image.gif
  • 4.3 注冊服務
<service
            android:name=".alive.doubles.two.MainService"
            android:enabled="true"
            android:exported="true"
            />
        <service
            android:name=".alive.doubles.two.RemoteService"
            android:enabled="true"
            android:exported="true"
            android:process=":remote"
            />

image.gif
  • 4.4 調(diào)用服務
startService (new Intent (this, MainService.class));
startService (new Intent (this, RemoteService.class));

image.gif

5.定時服務JobService编诹眩活

  • 5.1 創(chuàng)建定時服務AliveJobService
@TargetApi(21)
public class AliveJobService extends JobService {
    private final static String TAG = AliveJobService.class.getSimpleName();
    private volatile static Service mKeepAliveService = null;
    public static boolean isJobServiceAlive() {
        return mKeepAliveService != null;
    }

    private static final int MESSAGE_ID_TASK = 0x01;
    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (SystemUtils.isAPPALive(getApplicationContext(), "com.geduo.datacollect")) {
                Toast.makeText(getApplicationContext(), "app alive", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(getApplicationContext(), "app die", Toast.LENGTH_SHORT).show();
                startActivity(new Intent(AliveJobService.this,MainActivity.class));
            }
            jobFinished((JobParameters) msg.obj, false);
            return true;
        }
    });

    @Override
    public boolean onStartJob(JobParameters params) {
        Log.d(TAG, "onStartJob start...");
        mKeepAliveService = this;
        Message msg = Message.obtain(mHandler, MESSAGE_ID_TASK, params);
        mHandler.sendMessage(msg);
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        Log.d(TAG, "onStopJob start...");
        mHandler.removeMessages(MESSAGE_ID_TASK);
        return false;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy start...");
    }

}

image.gif
  • 5.2 創(chuàng)建JobSchedulerManager管理定時服務
public class JobSchedulerManager {
    private static final int JOB_ID = 1;
    private static JobSchedulerManager mJobManager;
    private JobScheduler mJobScheduler;
    private static Context mContext;

    private JobSchedulerManager(Context ctxt){
        this.mContext = ctxt;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            mJobScheduler = (JobScheduler)ctxt.getSystemService(Context.JOB_SCHEDULER_SERVICE);
        }
    }

    public final static JobSchedulerManager getJobSchedulerInstance(Context ctxt){
        if(mJobManager == null){
            mJobManager = new JobSchedulerManager(ctxt);
        }
        return mJobManager;
    }

    @SuppressLint("MissingPermission")
    public void startJobScheduler(){
        // 如果JobService已經(jīng)啟動或API<21妄壶,返回
        if(AliveJobService.isJobServiceAlive() || isBelowLOLLIPOP()){
            return;
        }
        // 構(gòu)建JobInfo對象,傳遞給JobSchedulerService
        int id = JOB_ID;
        mJobScheduler.cancel(id);
        JobInfo.Builder builder = new JobInfo.Builder(JOB_ID,new ComponentName(mContext, AliveJobService.class));
        if (Build.VERSION.SDK_INT >= 24) {
            builder.setMinimumLatency(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS); //執(zhí)行的最小延遲時間
            builder.setOverrideDeadline(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS);  //執(zhí)行的最長延時時間
            builder.setMinimumLatency(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS);
            builder.setBackoffCriteria(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS, JobInfo.BACKOFF_POLICY_LINEAR);//線性重試方案
        } else {
            builder.setPeriodic(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS);
        }
        builder.setPersisted(true);  // 設(shè)置設(shè)備重啟時寄狼,執(zhí)行該任務
        builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
        builder.setRequiresCharging(false); // 當插入充電器丁寄,執(zhí)行該任務
        JobInfo info = builder.build();
        mJobScheduler.schedule(info); //開始定時執(zhí)行該系統(tǒng)任務

   }

    @TargetApi(21)
    public void stopJobScheduler(){
        if(isBelowLOLLIPOP())
            return;
        mJobScheduler.cancelAll();
    }

    private boolean isBelowLOLLIPOP(){
        // API< 21
        return Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP;
    }
}

image.gif

6.流氓手段通過無限循環(huán)播放一個無聲音樂來保活

  • 6.1 創(chuàng)建服務PlayerMusicService
public class PlayerMusicService extends Service {
    private final static String TAG = "PlayerMusicService";
    private MediaPlayer mMediaPlayer;

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

    @Override
    public void onCreate() {
        super.onCreate();
            Log.d(TAG,"onCreate start...");
        mMediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.silent);
        mMediaPlayer.setLooping(true);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                startPlayMusic();
            }
        }).start();
        return START_STICKY;
    }

    private void startPlayMusic(){
        if(mMediaPlayer != null){
                Log.d(TAG,"startPlayMusic start...");
            mMediaPlayer.start();
        }
    }

    private void stopPlayMusic(){
        if(mMediaPlayer != null){
                Log.d(TAG,"stopPlayMusic start...");
            mMediaPlayer.stop();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        stopPlayMusic();
        Log.d(TAG,TAG+"onDestroy start...");
        // 重啟自己
        Intent intent = new Intent(getApplicationContext(),PlayerMusicService.class);
        startService(intent);
    }
}

image.gif
  • 6.2 注冊服務
   <service android:name=".alive.music.PlayerMusicService"
                 android:enabled="true"
                 android:exported="true"
                 android:process=":music_service"/>

image.gif

探索發(fā)現(xiàn)

以上就幾種常見的后臺服務崩觯活常用的解決方案狡逢,通過測試發(fā)現(xiàn)宁舰,Activity逼绰眩活,Notication甭瑁活腋腮,雙Services守護,SyncAdapter壤蚜、JobService即寡,循環(huán)播放音樂,這些方案不管是明手段還是流氓手段在android5.0下都還算好使袜刷,但是在Android6.0以后不管是哪種方案聪富,在app進入后臺,亮屏情況下好用著蟹,如果手機黑屏情況下墩蔓,頂多半個小時進程就被系統(tǒng)殺死了梢莽,為此我?guī)缀醢讶W(wǎng)關(guān)于Android后臺進程保活的文章都看了一遍奸披,方案都大同小異昏名,無外乎就上面所羅列的幾種方案。

那么在6.0以后應用進入后臺后阵面,當終端處于黑屏模式轻局,也就是Doze模式時,系統(tǒng)到底進行了哪些操作:

  1. 暫停網(wǎng)絡訪問样刷。
  2. 系統(tǒng)忽略所有的WakeLock仑扑。
  3. 標準的AlarmManager alarms被延緩到下一個maintenance window,但使用AlarmManager的 setAndAllowWhileIdle置鼻、setExactAndAllowWhileIdle和setAlarmClock時夫壁,alarms定義事件仍會啟動。 在這些alarms啟動前沃疮,系統(tǒng)會短暫地退出Doze模式盒让。
  4. 系統(tǒng)不再進行WiFi掃描。
  5. 系統(tǒng)不允許sync adapters運行司蔬。
  6. 系統(tǒng)不允許JobScheduler運行邑茄。

android6.0以后google對系統(tǒng)安全這塊的保護措施更進一步增強了,程序進入后臺系統(tǒng)為了省電俊啼,就認為該應用用戶已經(jīng)不再使用了肺缕,系統(tǒng)就自動清理掉該進程,有的人可能會問微信授帕,支付寶這些應用為什么用戶殺都殺不掉同木,原因就在于他們已經(jīng)進入了系統(tǒng)進程白名單了如果你不信你可以去自己看看。 設(shè)置白名單有兩種方式跛十,一種就是手機廠商自行設(shè)置彤路,另外一種就需要用戶進入手機省電模式自己進行手動設(shè)置。

所以最終結(jié)論: 在7.0這個版本芥映,如果不通過用戶或廠家設(shè)置洲尊,至少service是絕對沒有任何辦法保活的奈偏,絕對坞嘀,除非你還能找到未知的BUG。進入進程白名單惊来,然后通過雙服務崩錾活,加上JobService定時任務保駕護航裁蚁,就可以解決問題了矢渊。但我真的很贊同谷歌這樣的做法检眯,如果每個應用都是后臺常駐,整個系統(tǒng)的性能將大打折扣昆淡,都說android 手機耗電锰瘸,現(xiàn)在android可以說是將一切統(tǒng)統(tǒng)殺掉,然后最后的決定權(quán)留給用戶昂灵。

問題反饋

在使用中有任何問題避凝,歡迎反饋給我,可以用以下聯(lián)系方式跟我交流

關(guān)于作者

  var geduo_83 = {
    nickName  : "geduo_83",
    site : "http://www.weibo.com/geduo83"
  }
image.gif
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末眨补,一起剝皮案震驚了整個濱河市管削,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌撑螺,老刑警劉巖含思,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異甘晤,居然都是意外死亡含潘,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門线婚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遏弱,“玉大人,你說我怎么就攤上這事塞弊∈荩” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵游沿,是天一觀的道長饰抒。 經(jīng)常有香客問我,道長诀黍,這世上最難降的妖魔是什么袋坑? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮蔗草,結(jié)果婚禮上咒彤,老公的妹妹穿的比我還像新娘疆柔。我一直安慰自己咒精,他們只是感情好,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布旷档。 她就那樣靜靜地躺著模叙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鞋屈。 梳的紋絲不亂的頭發(fā)上范咨,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天故觅,我揣著相機與錄音,去河邊找鬼渠啊。 笑死输吏,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的替蛉。 我是一名探鬼主播贯溅,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼躲查!你這毒婦竟也來了它浅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤镣煮,失蹤者是張志新(化名)和其女友劉穎姐霍,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體典唇,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡镊折,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了介衔。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腌乡。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖夜牡,靈堂內(nèi)的尸體忽然破棺而出与纽,到底是詐尸還是另有隱情,我是刑警寧澤塘装,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布急迂,位于F島的核電站,受9級特大地震影響蹦肴,放射性物質(zhì)發(fā)生泄漏僚碎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一阴幌、第九天 我趴在偏房一處隱蔽的房頂上張望勺阐。 院中可真熱鬧,春花似錦矛双、人聲如沸渊抽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽懒闷。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間愤估,已是汗流浹背帮辟。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留玩焰,地道東北人由驹。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像昔园,于是被迫代替她去往敵國和親荔棉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

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