2. 先從看得到的入手-探究活動

1.活動(Activity)

Activity是一種可以包含用戶界面的組件,主要用于和用戶進行交互。
手動創建活動,我們在創建的時候,選擇Add No Activity,這個時候app/src/main/java/com.example.activitytest目錄是空的。我們右鍵新建一個活動,但是不要勾選Generate Layout FileLauncher Activity.

image

Generate Layout File: 表示會自動為FirstActivity創建一個對應的布局文件
Launcher Activity: 表示會自動將FirstActivity設置為當前項目的主活動。
項目中的任何活動都應該重寫ActivityonCreate()方法。
Android Studio為我們提供了可視化布局編輯器,在窗口的最下方有兩個切換卡,左邊是Design,是當前的可視化布局編輯器,右邊是Text,通過XML文件的方式來編輯布局的。
setContentView()方法給當前的活動加載一個布局,在方法中我們一般會傳入一個布局文件的id.項目中添加的任何資源都會在R文件中生成一個相應的資源id

image

上圖是手動配置的主活動,其中android:label 指定活動中標題欄的內容,標題欄是顯示在活動最頂部的,需要注意的是,給主活動指定的label不僅會成為標題欄中的內容,還會成為啟動器(Launcher)中應用程序顯示的名稱。
如果你的應用程序中沒有聲明任何一個活動作為主活動,這個程序仍然是可以正常安裝的,只是你無法在啟動器中看到或者打開這個程序,這種程序一般都是作為第三方服務供其它應用在內部進行調用的,如支付寶快捷支付服務等。

2. Toast

image

在活動中,可以通過findViewById()方法獲取到在布局文件中定義的元素,findViewById()方法,返回的是一個View對象,我們需要向下轉型將他轉成Button對象,調用setOnClickListener()方法為按鈕注冊一個監聽器,點擊按鈕時就會執行監聽器中的onClick()方法。
通過靜態方法makeText()創建出一個Toast對象,然后調用show()方法將Toast顯示出來就可以了。makeText()方法需要傳入三個參數,第一個參數是Context對象,也就是Toast要求的上下文,第二個參數是Toast顯示的文本內容,第三個參數是Toast顯示的時長。有兩個內置常量可以選擇Toast.LENGTH_SHORTToast.LENGTH_LONG

3.在活動中使用Menu

image

其中<item>標簽就是用來創建具體的某一個菜單項,然后通過android:id給這個菜單項指定一個唯一的標識符,通過android:title給這個菜單項指定一個名稱。

@Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        getMenuInflater().inflate(R.menu.main,menu);
        return true;
    }

通過getMenuInflater()方法能夠得到MenuInflater對象,在調用它的inflater()方法,就可以給當前的活動創建菜單了,inflater()方法接受兩個參數,第一個參數用于指定我們通過哪一個資源文件來創建菜單,第二個參數用于指定我們的菜單項將添加到哪一個Menu對象中去。這里直接使用onCreateOptionMenu()方法中傳入的Menu參數,然后給這個方法換回true,表示允許創建的菜單顯示出來。如果返回false,創建的菜單將無法顯示。

菜單響應事件

 @Override
    public boolean onOptionsItemSelected(MenuItem item)
    {
        switch (item.getItemId())
        {
            case R.id.add_item:
                Toast toast1 = Toast.makeText(FirstActivity.this,"You clicked Add",
                        Toast.LENGTH_LONG);
                toast1.show();
                break;
            case R.id.remove_item:
                Toast toast2 = Toast.makeText(FirstActivity.this,"You clicked Remove",
                        Toast.LENGTH_LONG);
                toast2.show();
                break;
            default:
        }
        return true;
    }

調用item.getItemId() 來判斷我們點擊的是哪一個菜單項。

4.Intent的使用

IntentAndroid程序中各組件之間進行交互的一種重要方式,它不僅可以指明當前組件想要執行的動作,還可以在不同組件之間傳遞數據。Intent一般可被用于啟動活動,啟動服務以及發送廣播等場景。
Intent大致可以分為兩種:顯式Intent和隱式Intent.
顯式Intent

Intent intent = new Intent(FirstActivity.this;SecondActivity.class);
startActicity(intent);

Intent有多個構造函數的重載,其中一個是Intent(Context packageContext,Class<?> cls).這個構造函數接受兩個參數,第一個參數Context要求提供一個啟動活動的上下文,第二個參數Class則是指定想要啟動的目標活動,通過這個構造函數就可以構造出Intent的意圖.Activity類中提供了一個startActivity()方法,這個方法是專門用于啟動活動的,他接受一個Intent參數,這里我們將構建好的Intent傳入startActivity()方法就可以啟動目標活動了。
使用這種方式來啟動活動,Intent的意圖非常明顯,因此我們稱之為顯式Intent
隱式Intent
相比于顯式Intent,隱式Intent則含蓄了很多,他并不明確指出我們想要啟動哪一個活動,而是制定了一系列更為抽象的action和category等信息,然后交于系統去分析這個Intent,并幫我們找出合適的活動去啟動。

        <activity 
            android:name=".SecondActivity">
            <intent-filter>
                <action android:name="com.example.activitytest.ACTION_START"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

在<action>標簽中我們聲明了當前活動可以相應com.example.activitytest.ACTION_START這個action,而<category>標簽則包含了一些附加信息,更精確的指明了當前活動能夠響應的Intent中還可能帶有category.只有<action>和<category>中的內容同時能夠匹配上Intent中指定的action和category時,這個活動才能夠響應該Intent.

Intent intent = new Intent("com.example.activitytest.ACTION_START");
startActivity(intent);

我們使用了Intent的另一個構造函數,直接將action的字符串傳了進去,表明我們想要啟動能夠響應com.example.activitytest_ACTION_START這個action的活動,android.intent.category.DEFAULT是一種默認的category,在調用startActivity()方法的時候,會自動將這個category添加到Intent中。
每個Intent中只能指定一個action,但卻能指定多個category.

 Intent intent = new Intent("com.example.activitytest.ACTION_START");
 intent.addCategory("com.example.activitytest.MY_CATEGORY");
 startActivity(intent);

我們調用Intent中的addCategory()方法來添加一個category,我們指定了一個自定義的category,值為:com.example.activitytest.MY_CATEGORY.如果我們在運行的話會出現這樣的錯誤:

Process: com.example.activitytest, PID: 12083    
android.content.ActivityNotFoundException: No Activity found to handle  
Intent { act=com.example.activitytest.ACTION_START  cat=[com.example.activitytest.MY_CATEGORY] }  

因為并沒有活動去響應我們新增加的category.

<activity
            android:name=".SecondActivity">
            <intent-filter>
                <action android:name="com.example.activitytest.ACTION_START"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="com.example.activitytest.MY_CATEGORY"/>
            </intent-filter>
        </activity>

我們增加一個category就可以了。
更多隱式Intent的用法
使用隱式Intent,我們不僅可以啟動我們自己程序向內的活動,還可以啟動其他程序的活動,這使得Android多個應用程序之間的功能共享成為了可能。

 Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);

我們指定了Intent的action是Intent.ACTION_VIEW,這是一個Android系統內置的活動,其常量值為android.intent.action.View.然后通過Uri.parse()方法,將一個網址字符串解析成一個Uri對象,在調用Intent的setData()方法將這個Uri對象傳遞進去。setData()方法接受一個Uri對象,主要用于指定當前Intent正在操作的數據,而這些數據通常都是以字符串的形式傳入到Uri,parse()方法中解析產生的。
我們還可以在<intent-filter>標簽中再配置一個<data>標簽,用于更精確的指定當前活動能夠響應什么類型的數據。<data>標簽中主要可以配置一下內容。

  • android:scheme :用于指定數據的協議部分,如上面的http部分。
  • android:host :用于指定數據的主機名部分,如上面的www.baidu.com
  • android:port :用于指定數據的端口部分,一般緊隨在主機名之后
  • android:mineType:用于指定可以處理的數據類型,允許使用通配符的方式指定。
    只有<data>標簽中指定的內容和Intent中攜帶的Data完全一致時,當前活動才能夠響應該Intent.不過一般在<data>中都不會指定過多的內容。如上面的瀏覽器例子中,只需要指定android:scheme為http,就可以響應所有的http協議的Intent了。
     <activity
            android:name=".ThirdActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:scheme="http"/>
            </intent-filter>
        </activity>

我們添加一個活動,修改<intent-filter>的值,讓他也可以響應瀏覽器的Intent.
除了http協議外,我們還可以指定很多其他協議,比如geo表示顯示地理位置,tel表示撥打電話。

Intent intent = new Intent(Intent.ACTION_DIAL);
                 intent.setData(Uri.parse("tel:10086"));
                 startActivity(intent);

這是調用系統撥號界面
指定了Intent的action是Intent.ACTION_DIAL,這又是系統的內置動作。然后data部分指定了協議是tel,號碼是10086.

向下一個活動傳遞數據

Intent中提供了一系列putExtra()方法的重載。

String data = "hello SecondActivity";
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("extra_data",data);
startActivity(intent);

注意這里putExtra()方法接收兩個參數,第一個參數是鍵,用于后面從Intent中取值,第二個參數才是真正要傳遞的數據。

Intent intent = getIntent();
String data = intent.getStringExtra("extra_data");
Log.d(TAG, data);

通過getIntent()方法獲取到用于啟動SecondActivityIntent,然后調用getStringExtra()方法,傳入相應的鍵值,就可以得到傳遞的數據了。如果傳遞的是整形數據使用getIntExtra()方法,如果傳遞的是布爾型數據,則使用getBooleanExtra()方法,以此類推。

返回數據給上一個活動

Activity中還有一個startActivityForResult()方法也是用于啟動活動的,但這個方法期望在活動銷毀的時候能夠返回一個結果給上一個活動。
startActivityForResult()方法接收兩個參數,第一個參數還是Intent,第二個參數是請求碼,用于在之后的回調中判斷數據的來源。

 Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
 startActivityForResult(intent,1);  

使用startActivityForResult()來啟動活動。請求碼只要是一個唯一值就可以了。 ```

 Intent intent = new Intent();
 intent.putExtra("data_return","hello FirstActivity");
 setResult(RESULT_OK,intent);
 finish();

我們構建了一個Intent,只不過這個Intent僅僅是用于傳遞數據而已,他沒有指定任何的意圖,接著我們把要傳遞的數據放入Intent中,然后調用setResult()方法,這個方法非常重要,是專門用于向上一個活動返回數據的。setResult()方法接收兩個參數,第一個參數用于向上一個活動返回處理的結果,一般只使用RESULT_OKRESULT_CANCELED兩個值,第二個參數則把帶有數據的Intent傳遞回去。

SecondActivity銷毀后會回調上一個活動的onActivityResult()方法:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 
   switch (requestCode){
       case 1:
           if (resultCode == RESULT_OK) {
               String returnedData = data.getStringExtra("data_return");
               Log.d(TAG, "onActivityResult: "+returnedData);
           }
           break;
       default:
   }
    
}

onActivityResult()方法帶有三個參數,第一個參數requestCode,即我們啟動活動時的請求碼,第二個參數resultCode,即我們在返回數據時傳入的處理結果,第三個參數data,即攜帶者返回數據的Intent,由于在一個活動中有可能調用startActivityForResult()方法去啟動很多不同的活動,每一個活動返回的數據都會回調到onActivityResult()這個方法,因此我們首先要做的就是通過檢查requestCode的值來判斷數據來源,然后再通過resultCode的值來判斷處理結果是否成功.

如果用戶在SecondActivity中并不是通過點擊按鈕,而是通過按下Back鍵回到FirstActivity,我們可以通過在SecondActivity中重寫onBackPressed()方法。

@Override
public void onBackPressed() {
   Intent intent = new Intent();
   intent.putExtra("data_return","hello FirstActivity2");
   setResult(RESULT_OK,intent);
   finish();
}

5.活動的生命周期

Android是使用任務(Task)來管理活動的,一個任務就是一組存放在棧里的活動的集合,這個棧也被稱作返回棧(Back Stack).棧是一種后進先出的數據結構,在默認情況下,每當我我們啟動了一個新的活動,他會在返回棧中入棧,并處于棧頂的位置,而每當我們按下Back鍵或調用finish()方法去銷毀一個活動的時候,處于棧頂的活動會出棧,這時前一個入棧的活動就會重新處于棧頂的位置。系統總是會顯示處于棧頂的活動給用戶。
返回棧的工作示意圖:

image

5.1活動的狀態

每個活動在其生命周期中最多可能會有4種狀態。
1.運行狀態
當一個活動位于返回棧的棧頂時,這時活動就處于運行狀態。
2.暫停狀態
當一個活動不再處于棧頂位置,但仍然可見時,這是活動就進入暫停狀態。
3.停止狀態
當一個活動不在處于棧頂位置,并且完全不可見的時候,就進入停止狀態。
4.銷毀狀態
當一個活動從返回棧中移除后就變成了銷毀狀態。

5.2活動的生存期

Activity類中定義了7個回調方法,覆蓋了活動生命周期的每一個環節。
onCreate(): 在活動第一次被創建的時候調用,你應該在這個方法中完成活動的初始化操作,比如說加載布局,綁定事件等。
onStart(): 在活動由不可見變為可見的時候調用。
onResume(): 在活動準備好和用戶進行交互的時候調用,此時的活動一定位于返回棧的棧頂,并且處于運行狀態。
onPause(): 在系統準備去啟動或恢復另一個活動的時候調用,我們通常會在這個方法中將一些小號CPU的資源釋放掉,以及保存一些關鍵數據,但這個方法的執行速度一定要快,不然會影響到新的棧頂活動的使用。
onStop(): 在活動完全不可見的時候調用,他和onPause()方法的主要區別是如果啟動的新活動是一個對話框的活動,那么onPause()方法會得到執行,而onStop()方法并不會得到執行。
onDestory(): 在活動貝銷毀之前調用,之后活動的狀態將變為銷毀狀態。
onRestart(): 在活動有停止狀態變為運行狀態之前調用,也就是活動被重新啟動了。
完整生存期
活動在onCreate()方法和onDestory()方法之間所經歷的,就是完整生存期,一般情況下,一個活動會在onCreate()方法中完成各種初始化操作,而在onDestory()方法中完成釋放內存的操作。
可見生存期
活動在onStart()方法和onStop()方法之間所經歷的就是可見生存期。在onStart()方法中對資源進行加載,而在onStop()方法中對資源進行釋放,從而保證處于停止狀態的活動不會占用過多的內存。
前臺生存期
活動在onResume()方法和onPause()方法之間所經歷的就是前臺生存期。

活動的生命周期圖

image

 <activity 
            android:name=".DialogActivity"
            android:theme="@android:style/Theme.Dialog">
        </activity>

我們給他使用了一個android:theme屬性,這是用于給當前活動指定主題的,Android系統內置有很多的主題可以選擇,當然我們也可以定制自己的主題。而這里@android:style/Theme.Dialog,則毫無疑問是讓DialogActivity使用對話框式的主題。

當MainActivity第一次被創建時會依次執行onCreate(),onStart()和onResume()方法。
NormalActivity把MainActivity完全遮擋住,onPause()和onStop()方法會得到執行。
按下Back鍵,之前的MainActivity已經進入停止狀態,所以onRestart()方法會得到執行之后又會依次執行onStart()和onResume()方法。
點擊進入DialogActivity,只有onPause()方法得到了執行,onStop()方法并沒有執行,這是因為DialogActivity并沒有完全遮擋住MainActivity,此時MainActivity只是進入了暫停狀態,并沒有進入停止狀態。相應的,按下Back鍵返回MainActivity也應該只有onResume()方法會得到執行。
活動被回收了怎么辦?
Activity中還提供了一個onSaveInstanceState()回調方法,這個方法可以保證在活動被回收之前一定會被調用。因此我們可以通過這個方法來解決活動被回收時臨時數據得不到保存的問題。
onSaveInstanceState()方法會攜帶一個Bundle類型的參數,Bundle提供了一系列的方法用于保存數據,比如可以使用putString()方法保存字符串,使用putInt()方法保存整形數據。每個保存方法需要傳入兩個參數,第一個參數是鍵,用于后面從Bundle中取值,第二個參數是真正要保存的內容。

 @Override
    public void onSaveInstanceState(Bundle outState)
    {
        super.onSaveInstanceState(outState);
        String tempData = "Something you just typed";
        outState.putString("data_key",tempData);
    }

onCreate()方法其實也有一個Bundle類型的參數。這個參數在一般情況下都是null,但是如果在活動被系統回收之前有通過onSaveInstanceState()方法來保存數據的話,這個參數就會帶有之前所保存的全部數據。

 @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        if (savedInstanceState != null)
        {
            String tempData = savedInstanceState.getString("data_key");
        }
        
    }

取出值后再做相應的操作就可以了。
Intent還可以結合Bundle一起用于傳遞數據,首先可以把需要傳遞的數據都保存在Bundle對象中,然后再將Bundle對象存放在Intent里,到了目標活動之后先從Intent中取出Bundle,在從Bundle中一一取出。

6.活動的啟動模式

在實際項目中我們應該根據特定的需求為每個活動指定恰當的啟動模式。啟動模式一共有4種,分別是standard,singleTop,singleTask和singleInstance,可以在AndroidManifest.xml中通過給<activity>標簽指定android:launchMode屬性來選擇啟動模式。

6.1 standard

image

standard是活動默認的啟動模式,在不進行顯式指定的情況下,所有活動都會自動使用這種啟動模式。Android是使用返回棧來管理活動的在standard(即默認情況下),每當啟動一個新的活動,他就會在返回棧中入棧,并處于棧頂的位置,對于使用standard面膜是的活動,系統不會在乎這個活動是否已經在返回棧中存在,每次啟動都會創建該活動的一個新的實例。

在代碼中實踐:

Log.d(TAG, this.toString());
Intent intent = new Intent(FirstActivity.this,FirstActivity.class);
                startActivity(intent);

連續點擊兩次,可以看到打印出當前活動的實例。

01-06 21:47:26.409 18937-18937/com.example.activitytest D/FirstActivity: com.example.activitytest.FirstActivity@f7397be
01-06 21:47:32.614 18937-18937/com.example.activitytest D/FirstActivity: com.example.activitytest.FirstActivity@6b88a94

6.2 singleTop

image

使用singleTop模式,當活動的啟動模式指定為singleTop,在啟動活動時,如果發現返回棧的棧頂已經是該活動,則認為可以直接使用它,不會再創建新的活動實例。

android:name=".FirstActivity"
            android:launchMode="singleTop"
            android:label="This is FirstActivity">
01-06 22:12:07.813 15130-15130/? D/FirstActivity: com.example.activitytest.FirstActivity@f7c25bd

無論點擊多少次,都只會創建一次實例。

6.3 singleTask

image

使用singleTop模式可以很好地解決重復創建棧頂活動的問題,當活動的啟動模式指定為singleTask模式,每次啟動該活動時系統首先會在返回棧中檢查是否存在該活動的實例,如果發現已經存在則直接使用該實例,并把在這個活動之上的所有活動統統出棧,如果沒有發現就會創建一個新的活動實例。

6.4 singleInstance

image

指定為singleInstance模式的活動會啟用一個新的返回棧來管理這個活動(其實如果singleTask模式指定了不同的taskAffinity,也會啟動一個新的返回棧)。每個應用程序都會有自己的返回棧,同一個活動在不同的返回棧中入棧時必然是創建了新的實例,在singleInstance模式下,會有一個單獨的返回棧來管理這個活動不管是哪個應用程序來訪問這個活動,都共用的同一個返回棧,也就解決了共享活動實例的問題。

7.活動的最佳實踐

7.1 知曉當前是在哪一個活動?

新建一個類:BaseActivity

public class BaseActivity extends AppCompatActivity
{
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        Log.d("BaseActivity",getClass().getSimpleName());
    }
}

讓所有的Activity不在繼承AppCompatActivity,而是繼承BaseActivity,getClass().getSimpleName()可以得到當前實例的類名。

7.2隨時隨地退出程序

我們需要用一個專門的集合類對所有的活動進行管理就可以了。

public class ActivityCollector
{
     //添加活動
    public static List<Activity> activities = new ArrayList<>();
    
    public static void addActivity(Activity activity)
    {
        activities.add(activity);
    }
    
    //移除活動
    public static void removeActivity(Activity activity)
    {
        activities.remove(activity);
    }
    
    //全部銷毀活動
    public static void finishAll()
    {
        for (Activity activity : activities)
        {
            if (!activity.isFinishing())
            {
                activity.finish();
            }
        }
    }
}

通過一個List來暫存活動,然后提供了一個addActivity()方法用于向List中添加一個活動,提供了一個removeActivity()方法用于從List中移除活動,最后提供了一個finishAll()方法用于將List中存儲的活動全部銷毀掉.

public class BaseActivity extends AppCompatActivity
{
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        Log.d("BaseActivity",getClass().getSimpleName());
        
        ActivityCollector.addActivity(this);
    }

    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        ActivityCollector.removeActivity(this);
    }
}

在BaseActivity的onCreate()方法中調用了ActivityCollector的addActivity()方法,表明將當前正在創建的活動添加到活動管理器里,然后在BaseActivity中重寫onDestroy()方法,并調用了ActivityCollector的removeActivity()方法,表明將一個馬上要銷毀的活動從活動管理器里移除。

從此以后,不管你想在什么地方退出程序,只需要調用ActivityCollector.finishAll()方法就可以了。

public class ThirdActivity extends BaseActivity
{

    Button button;

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

        button = (Button) findViewById(R.id.button_3);
        initEvent();
    }

    public void initEvent()
    {
        button.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                ActivityCollector.finishAll();
                android.os.Process.killProcess(android.os.Process.myPid());
            }
        });
    }
}

你還可以在銷毀所有活動的代碼后面再加上殺掉當前進程的代碼,以保證程序完全退出,殺掉進程的代碼:

 android.os.Process.killProcess(android.os.Process.myPid());

killProcess()方法用于殺掉一個進程,它接受一個進程ID的參數,我們可以通過myPid()方法來獲得當前程序的進程ID,需要注意的是,killProcess()方法只能用于殺掉當前程序的進程,,我們不能使用這個方法去殺掉其他程序。

7.3 啟動活動的最佳寫法

在SecondActivity中寫入下面的方法:


    public static void actionStart(Context context,String data1,String data2)
    {
        Intent intent = new Intent(context,SecondActivity.class);
        intent.putExtra("param1",data1);
        intent.putExtra("param2",data2);
        context.startActivity(intent);
    }

在FirstActivity里面調用這個方法:

 SecondActivity.actionStart(FirstActivity.this,"data1","data2");

這樣寫的好處是一目了然:SecondActivity所需要的數據在方法參數中全部體現出來了,這樣即使不用閱讀SecondActivity中的代碼,不去詢問負責編寫SecondActivity的同事,你也可以非常清晰地知道啟動SecodeActivity需要傳遞哪些數據,另外這樣寫還簡化了啟動活動的代碼。0

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容