11 Android特色開發---基于位置的服務

第 11 章

11.1 基于位置的服務簡介

雙槍女.jpg

基于位置的服務(Location Based Service)簡稱LBS,主要的工作原理就是利用無線電通訊網絡或GPS等定位方式來確定出移動設備所在的位置。

基于位置的服務所圍繞的核心就是要先確定出用戶所在的位置。通常有兩種技術方式可以實現:一種是通過GPS定位,一種是通過網絡定位。GPS定位的工作原理是基于手機內置的GPS硬件直接和衛星交互來獲取當前的經緯度信息,這種定位方式精確度非常高,但缺點是只能在室外使用,室內基本無法接收到衛星的信號。網絡定位的工作原理是根據手機當前網絡附近的三個基站進行測速,以此計算出手機和每個基站之間的距離,再通過三角定位確定出一個大致的位置,這種定位方式精確度一般,但優點是在室內室外都可以使用。

11.2 申請API Key

要想在自己的應用程序里使用百度的LBS功能,首先需要申請一個API Key.

訪問[]http://lbsyun.baidu.com/apiconsole/key
這個地址,點擊創建應用就可以去申請API Key了,應用名稱可以隨便填,應用類型選擇Android SDK,啟用服務保持默認即可。

image

這個發布版SHA1和開發版SHA1又是什么東西呢?這是我們申請API Key 所必須填寫的一個字段,它指的是打包程序時所用簽名文件的SHA1指紋,可以通過Android Studio查看到。

打開Android Studio中的任意一個項目,點擊右側工具欄的Gradle---項目名---:app---Tasks---android

image

這里展示了一個Android Studio項目中所有內置的Gradle Tasks,其中signingReport這個Task就可以用來查看簽名文件信息。雙擊signingReport。

image

其中,E2:FB:B5:1F:CA:38:8D:40:EB:47:30:E0:28:0D:DB:C3:5A:9D:05:B6就是我們所需的SHA1指紋了,另外需要注意,目前我們使用的是debug.keystore文件所生成的指紋,這是Android自動生成的一個用于測試的簽名文件。而當你的應用程序發布時還需要創建一個正式的簽名文件,如果要得到他的指紋,可以在cmd中輸入如下命令:

keytool -list -v -keystore <簽名文件路徑>

現在得到的這個SHA1指紋實際上是一個開發版的SHA1指紋,不過因為暫時我們還沒有一個發布版的SHA1指紋,因此這兩個值都填成一樣就可以了。然后輸入包名后,提交就可以得到API Key了。

image

jPeDkML7MGusQ7CUUaZhR9YETfA8Ux42就是申請到的API Key,有了它就可以進行后續的LBS開發工作了。

11.3 使用百度地圖

11.3.1 準備LBS SDK

在開始編碼之前,我們還需要先將百度LBS開放平臺的SDK準備好。地址[]http://lbsyun.baidu.com/sdk/download?selected=mapsdk_basicmap,mapsdk_searchfunction,mapsdk_lbscloudsearch,mapsdk_calculationtool,mapsdk_radar

本章中我們會用到基礎地圖和定位功能這兩個SDK,將它們勾選上。

image

下載完成后對該壓縮包解壓,其中會有libs目錄,這里面的內容就是我們所需要的一切了。

image

libs目錄下的內容又分為兩部分,BaiduLBS_Android.jar這個文件是Java層要使用到的,其他子目錄下的so文件是Native層要用到的。so文件是用C/C++語言進行編寫,然后再用NDK編譯出來的。

首先觀察一下當前的項目結構,你會發現app模塊下面有一個libs目錄,這里就是用來存放所有的Jar文件的,我們將BaiduLBS_Android.Jar復制到這里。

image

接下來展開src/main目錄,右擊該目錄-->New-->Directory,再創建一個名為jniLibs的目錄,這里就是專門用來存放so文件的,然后把壓縮包里面的其他目錄直接復制到這里。

image

雖然所有新創建的項目中,app/build.gradle文件都會默認配置一下這段聲明:

dependencies 
{
    compile fileTree(dir: 'libs', include: ['*.jar'])
    ......
}

這表示將libs目錄下所有以.jar結尾的文件添加到當前項目的引用中。但是由于我們是直接將Jar包復制到libs目錄下的,并沒有修改gradle文件,因此不會彈出我們平時熟悉的Sync Now提示。這個時候必須手動點擊一下Android Studio頂部工具欄中的Sync按鈕,不然項目將無法引用到Jar包中提供的任何接口。

點擊Sync按鈕之后,libs目錄下的jar文件就會多出一個向右的箭頭,這就表示項目已經能引用到這些Jar包了。

11.3.2 確定自己位置的經緯度

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

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.GET_TASKS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <meta-data
            android:name="com.baidu.lbsapi.API_KEY"
            android:value="jPeDkML7MGusQ7CUUaZhR9YETfA8Ux42"/>

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

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <service android:name="com.baidu.location.f"
                 android:enabled="true"
                 android:process=":remote">

        </service>
    </application>

</manifest>

這里首先添加了很多行權限申明,每一個權限都是百度LBS SDK內部要用到的。然后在<application>標簽的內部添加了一個<meta-data>標簽,這個標簽的android:name部分是固定的,必須填com.baidu.lbsapi.API_KEY,android:value部分則應該填入我們上一節申請到的API Key。最后再注冊一個LBS SDK中的服務,不用對這個服務的名字感到疑惑,因為百度LBS SDK中的代碼都是混淆過的。

public class MainActivity extends AppCompatActivity
{

    public LocationClient mLocationClient;

    private TextView positionText;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        mLocationClient = new LocationClient(getApplicationContext());
        mLocationClient.registerLocationListener(new MyLocationListener());
        setContentView(R.layout.activity_main);
        positionText = (TextView) findViewById(R.id.position_text_view);
        List<String> permissionList = new ArrayList<>();
        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission
                .ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)
        {
            permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
        }
        if (ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission
                .READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED)
        {
            permissionList.add(Manifest.permission.READ_PHONE_STATE);
        }
        if (ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission
                .WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
        {
            permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }
        if (!permissionList.isEmpty())
        {
            String [] permissions = permissionList.toArray(new String[permissionList.size()]);
            ActivityCompat.requestPermissions(MainActivity.this,permissions,1);
        }
        else
        {
            requestLocation();
        }
    }

    private void requestLocation()
    {
        initLocation();
        mLocationClient.start();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String[] permissions,
                                           @NonNull int[] grantResults)
    {
        switch (requestCode)
        {
            case 1:
                if (grantResults.length > 0)
                {
                    for (int result : grantResults)
                    {
                        if (result != PackageManager.PERMISSION_GRANTED)
                        {
                            Toast.makeText(MainActivity.this, "必須同意所有權限才能使用本程序",
                                    Toast.LENGTH_SHORT).show();
                            finish();
                            return;
                        }
                    }
                    requestLocation();
                }
                else
                {
                    Toast.makeText(MainActivity.this, "發生未知錯誤", Toast.LENGTH_SHORT).show();
                }
                break;
            default:
                break;
        }
    }

    public class MyLocationListener implements BDLocationListener
    {

        @Override
        public void onReceiveLocation(BDLocation bdLocation)
        {
            StringBuilder currentPosition = new StringBuilder();
            currentPosition.append("維度:").append(bdLocation.getLatitude())
                    .append("\n");
            currentPosition.append("經線:").append(bdLocation.getLongitude())
                    .append("\n");
            currentPosition.append("定位方式: ");
            if (bdLocation.getLocType() == BDLocation.TypeGpsLocation)
            {
                currentPosition.append("GPS");
            }
            else if (bdLocation.getLocType() == BDLocation.TypeNetWorkLocation)
            {
                currentPosition.append("網絡");
            }
            positionText.setText(currentPosition);
        }

        @Override
        public void onConnectHotSpotMessage(String s, int i)
        {

        }
    }

    private void initLocation()
    {
        LocationClientOption option = new LocationClientOption();
        option.setScanSpan(5000);
        mLocationClient.setLocOption(option);
    }

    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        mLocationClient.stop();
    }
}

在onCreate()方法中,我們首先創建了一個LocationClient的實例,LocationClient的構建函數接收一個Context參數,這里調用getApplicationContext()方法來獲取一個全局的Context參數并傳入。然后調用LocationClient的registerLocationListener()方法來注冊一個定位監聽器,當獲取到位置信息的時候,就會回調這個定位監聽器。

由于我們在AndroidManifest.xml中申明了很多權限,其中ACCESS_COARSE_LOCATION,ACCESS_FINE_LOCATION,READ_PHONE_STATE,WRITE_EXTERNAL_STORAGE這四個權限是需要進行運行時權限處理的,不過由于ACCESS_COARSE_LOCATION和ACCESS_FINE_LOCATION都屬于通一個權限組,因此兩者只需要申請其一就可以了。那么怎樣才能在運行時一次性申請三個權限呢?這里我們使用了一種新的用法,首先創建一個空的List集合,然后依次判斷這三個權限有沒有被授權,如果沒被授權就添加到List集合中,最后將List轉換成數組,再調用ActivityCompat.requestPermissions()方法一次性申請。

onRequestPermissionsResult()方法中對權限申請結果的邏輯處理也和之前有所不同,這次我們通過一個循環將申請的每個權限都進行了判斷,如果有任何一個權限被拒絕,那么就直接調用finish()方法關閉當前程序,只有當所有權限都被用戶同意了,才會調用requestLocation()方法開始地理位置定位。

requestLocation()方法中的代碼比較簡單,只是調用了一下LocationClient的start()方法就能開始定位了。定位的結果會回調到我們前面我們注冊的監聽器中,也就是MyLocationListener。觀察一下MyLocationListener的onReceiveLocation()方法中,在這里我們通過BDLocation的getLatitude()方法獲取當前位置的維度,通過getLongitude()方法獲取當前位置的精度,通過getLocType()方法獲取當前的定位方式,最終將結果組裝成一個字符串,顯示到TextView上面。

在initLocation()方法中我們創建了一個LocationClientOption對象,然后調用它的setScanSpan()方法來設置更新的間隔。這里傳入了5000,表示每5秒鐘會更新一下當前的位置。

最后要記得,在活動被銷毀的時候一定要調用LocationClient的stop()方法來停止定位不然程序會持續在后臺不停地進行定位,從而嚴重消耗手機的電量。

11.3.4 選擇定位模式

GPS定位功能必須要由用戶主動去啟用才行,不然任何應用程序都無法使用GPS獲取到手機當前的位置信息。

 private void initLocation()
    {
        LocationClientOption option = new LocationClientOption();
        option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors);
        mLocationClient.setLocOption(option);
    }

我們在initLocation()方法中對百度LBS SDK的定位模式進行指定,一共有三種模式可選:Hight_Accuracy,Battery_Saving和Device_Sennors。

Hight_Accuracy:表示高精確度模式,會在GPS信號正常的情況下優先使用GPS定位,在無法接收GPS信號的時候使用網絡定位。

Battery_Saving:表示節電模式,只會使用網絡進行定位,。

Device_Sennors:表示傳感器模式,只會使用GPS定位,其中Hight_Accuracy是默認的模式,也就是說,我們即使不修改任何代碼,只要拿到手機走到室外去,讓手機可以接收到GPS信號,就會自動切換到GPS定位模式了。

11.3.4 看得懂的位置信息

private void initLocation()
    {
        LocationClientOption option = new LocationClientOption();
        //option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors);
        option.setScanSpan(5000);
        option.setIsNeedAddress(true);
        mLocationClient.setLocOption(option);
    }

首先在initLocation()方法中,我們調用了LocationClientOption的setIsNeedAddress()方法,并傳入true,這就表示我們需要獲取當前位置詳細的地址信息。

currentPosition.append("國家: ").append(bdLocation.getCountry())
                    .append("\n");
            currentPosition.append("省: ").append(bdLocation.getProvince())
                    .append("\n");
            currentPosition.append("市: ").append(bdLocation.getCity())
                    .append("\n");
            currentPosition.append("區: ").append(bdLocation.getDistrict())
                    .append("\n");
            currentPosition.append("街道: ").append(bdLocation.getStreet())
                    .append("\n");

在MyLocationListener的onReceiveLocation()方法就可以獲取到各種豐富的地址信息了。調用getCountry()方法可以得到當前所造國家。調用getProvince()方法可以得到當前所在省份,以此類推。另外還有一點需要注意,由于獲取地址信息一定需要用到網絡,因此即使我們將定位模式指定成了Device_Sensors,也會自動開啟網絡定位功能。

11.4 使用百度地圖

11.4.1 讓地圖顯示出來

<com.baidu.mapapi.map.MapView
        android:id="@+id/bmapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </com.baidu.mapapi.map.MapView>

MapView是由百度提供的自定義控件,所以在使用它的時候需要將完整的包名加上。

public class MainActivity extends AppCompatActivity
{

  
    private MapView mapView;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        ````
        SDKInitializer.initialize(getApplicationContext());
        setContentView(R.layout.activity_main);
        mapView = (MapView) findViewById(R.id.bmapView);
        ````
    }

    ```

    @Override
    protected void onResume()
    {
        super.onResume();
        mapView.onResume();
    }

    @Override
    protected void onPause()
    {
        super.onPause();
        mapView.onPause();
    }

    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        mLocationClient.stop();
        mapView.onDestroy();
    }
}

首先需要調用SDKInitializer的initialize()方法來進行初始化操作,initialize()方法接收一個Context參數,這里我們調用getApplicationContext()方法來獲取一個全局的Context參數并傳入。注意初始化操作一定要在setContentView()方法前調用,不然的話就會出錯。接下來我們調用findViewById()方法獲取到了MapView的實例,這個實例在后面的功能當中還會用到。

另外還需要重寫onResume(),onPause()和onDestroy()這三個方法,在這里對MapView進行管理,以保證資源能夠及時的得到釋放。

11.4.2 移動到我的位置

百度LBS SDK的API中提供了一個BaiduMap類,它是地圖的總控制器,調用MapView的getMap()方法就能獲取到BaiduMap的實例。

BaiduMap baiduMap = mapView.getMap();

有了BaiduMap后,我們就能對地圖進行各種各樣的操作了,比如設置地圖的縮放級別以及將地圖移動到某一個經緯度上。

百度地圖將縮放級別的取值范圍限定在3~19之間,其中小數點位的值也是可以取得,值越大,地圖顯示的信息就越精細。

MapStatusUpdate update = MapStatusUpdateFactory.zoomTo(12.5f);
baiduMap.animateMapStatus(update);

其中MapStatusUpdateFactory的zoomTo()方法接收一個float型的參數就是用于設置縮放級別的,這里我們傳入12.5f。zoomTo()方法返回一個MapStatusUpdate對象,我們把這個對象傳入BaiduMap的animateMapStatus()方法即可完成縮放功能。

讓地圖移動到某一經緯度上,這就需要借助LatLng類了,其實LatLng并沒有什么太多的用法,主要就是用于存放經緯度值得,它的構造方法接收兩個參數,第一個參數是緯度值,第二個參數是經度值。之后調用MapStatusUpdate的newLatLng()方法將LatLng對象傳入,newLatLng()方法返回的也是一個MapStatusUpdate對象,我們再把這個對象傳入BaiduMap的animateMapStatus()方法當中,就可以將地圖移動到指定的經緯度上了。

LatLng ll new LatLng(39.915,116.4.4);
MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll);
baiduMap.animateMapStatus(update);
public class MainActivity extends AppCompatActivity
{

    private LocationClient mLocationClient;

    private MapView mapView;

    private BaiduMap baidumap;

    private boolean isFirstLocate = true;

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        mLocationClient = new LocationClient(getApplicationContext());
        mLocationClient.registerLocationListener(new MyLocationListener());
        SDKInitializer.initialize(getApplicationContext());
        setContentView(R.layout.activity_main);

        mapView = (MapView) findViewById(R.id.bmapView);
        baidumap = mapView.getMap();

    }

    private void requestLocation()
    {
        //initLocation();
        mLocationClient.start();

    }
    private void initLocation()
    {
        LocationClientOption option = new LocationClientOption();
        //option.setScanSpan(5000);
        //option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors);
        mLocationClient.setLocOption(option);
    }


    private void navigateTo(BDLocation location)
    {
        if (isFirstLocate)
        {
            LatLng ll = new LatLng(location.getLatitude(),location.getLongitude());
            Log.d(TAG, "navigateTo: "+location.getLatitude()+"w"+location.getLongitude());
            MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll);
            baidumap.animateMapStatus(update);
            update = MapStatusUpdateFactory.zoomTo(16f);
            baidumap.animateMapStatus(update);
            isFirstLocate = false;
        }
    }

   
    public class MyLocationListener implements BDLocationListener
    {

        @Override
        public void onReceiveLocation(BDLocation bdLocation)
        {


            if (bdLocation.getLocType() == BDLocation.TypeGpsLocation
                    || bdLocation.getLocType() == BDLocation.TypeNetWorkLocation)
            {
                Log.d(TAG, "requestLocation: "+"3333333333333332");
                navigateTo(bdLocation);
            }
        }

        @Override
        public void onConnectHotSpotMessage(String s, int i)
        {

        }

    }

    @Override
    protected void onResume()
    {
        super.onResume();
        mapView.onResume();
    }

    @Override
    protected void onPause()
    {
        super.onPause();
        mapView.onPause();
    }

    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        mLocationClient.stop();
        mapView.onDestroy();
    }
}

我們主要是新加了一個navigateTo()方法。這個方法中的代碼也很好理解,先是將BDLocation對象中的地理位置信息取出并封裝到LatLng對象中,然后調用MapStatusUpdateFactory的newLatLng()方法并將LatLng對象傳入,接著將返回的MapStatusUpdate對象作為參數傳入到BaiduMap的animateStatus()方法中,我們將縮放級別設置成了16,另外還有一點需要注意,上訴代碼當中我們使用了一個isFirstLocate變量,這個變量的作用是為了防止多次調用animateMapStatus()方法,因為將地圖一定到我們當前的位置只需要在程序第一次定位的時候調用一次就可以了。

11.4.3 讓"我"顯示在地圖上

百度LBS SDK當中提供了一個MyLocationData.Builder類,這個類是用來封裝設備當前所在位置的,我們只需將經緯度信息傳入到這個類的相應方法當中就可以了。

MyLocationData.Builder locationBuilder = new MyLocationData.Builder();
locationBuilder.latitude(39.915);
locationBuilder.longitude(116.404);

MyLocationData.Builder類還提供了一個builder()方法,當我們把要封裝的信息都設置完成之后,只需要調用它的build()方法,就會生成一個MyLocationData的實例,然后再將這個實例傳入到BaiduMap的setMyLocationData()方法當中,就可以讓設備當前的位置顯示在地圖上。

MyLocationData location locationData = locationBuilder.build();
baiduMap.setMyLocationData(locationData);
mapView = (MapView) findViewById(R.id.bmapView);
        baidumap = mapView.getMap();
        baidumap.setMyLocationEnabled(true);

        
        private void navigateTo(BDLocation location)
    {
        if (isFirstLocate)
        {
            LatLng ll = new LatLng(location.getLatitude(),location.getLongitude());
            Log.d(TAG, "navigateTo: "+location.getLatitude()+"w"+location.getLongitude());
            MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll);
            baidumap.animateMapStatus(update);
            update = MapStatusUpdateFactory.zoomTo(16f);
            baidumap.animateMapStatus(update);
            isFirstLocate = false;
        }
        MyLocationData.Builder locationBuilder = new MyLocationData.Builder();
        locationBuilder.latitude(location.getLatitude());
        locationBuilder.longitude(location.getLongitude());
        MyLocationData locationData = locationBuilder.build();
        baidumap.setMyLocationData(locationData);
    }

    
     @Override
    protected void onDestroy()
    {
        super.onDestroy();
        mLocationClient.stop();
        mapView.onDestroy();
        baidumap.setMyLocationEnabled(false);
    }

在navigateTo()方法中,我們添加了MyLocationData的構建邏輯,將Location中包含的經度和緯度分別封裝到了MyLocationData.Builder當中,最后把MyLocationData設置到BaiduMap的setMyLocationData()方法當中。注意這段邏輯必須寫在isFirstLocate這個if條件語句的外面,因為讓地圖移動到我們當前的位置只需要在第一次定位的時候調用,但是設備在地圖上顯示的位置卻應該是隨著設備的移動而實時改變的。

另外,根據百度地圖的限制,如果我們想要實現這一功能,一定要事先調用BaiduMap的setMyLocationEnabled()方法將此功能開啟,否則設備的位置將無法在地圖上顯示。而在程序退出的時候,也要記得將此功能給關閉掉。

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

推薦閱讀更多精彩內容