Android 定位(GPS为主) 笔记

一概览

0.概念
GPS是英文Global Positioning System(全球定位系统)的简称。
GNSS全球导航卫星系统(Global Navigation Satellite System),它是泛指所有的卫星导航系统,包括全球的、区域的和增强的。

1.定位方式
(1)GPS_PROVIDER:通过 GPS 来获取地理位置的经纬度信息;
优点:获取地理位置信息精确度高;
缺点:只能在户外使用,获取经纬度信息耗时,耗电;

(2)NETWORK_PROVIDER:通过移动网络的基站或者 Wi-Fi 来获取地理位置;
优点:只要有网络,就可以快速定位,室内室外都可;
缺点:精确度不高;

(3)PASSIVE_PROVIDER:被动接收更新地理位置信息,而不用自己请求地理位置信息。 PASSIVE_PROVIDER 返回的位置是通过其他 providers 产生的,可以查询 getProvider() 方法决定位置更新的由来,需要 ACCESS_FINE_LOCATION 权限,但是如果未启用 GPS,则此 provider 可能只返回粗略位置匹配;

2.版本差异与权限
权限
(1)ACCESS_FINE_LOCATION是精确位置,如果使用GPS_PROVIDER或者同时使用GPS_PROVIDER和NETWORK_PROVIDER,需声明该权限,它对于这两个provider都是有效的;
(2)ACCESS_COARSE_LOCATION是粗略位置,该权限只针对NETWORK_PROVIDER。
版本差异
Android 6.0 以上动态申请权限
Android 7.0 以上 可以获取GPS原始数据。

二、代码相关

2.0 权限检查

       if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
             Log.i(TAG, "startLocationClient checkSelfPermission return");
            return;
       }

GPS是否开启

   boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
     if (!gpsEnabled) {
        //检测gps 开启状态
          Log.i(TAG, "gpsEnabled  " + gpsEnabled);
          Intent settingsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
          settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
          startActivity(settingsIntent);
     }

2.1 搜星判断

GPS 首次通信非常耗时,所以很多时候可能请求不到坐标。可以侧面从有效卫星数量来判断是室内外。

  LocationManager  locationManager = (LocationManager) getSystemService(Context.
            LOCATION_SERVICE);
  locationManager.addGpsStatusListener(statusListener);


private static int STAR_MAX_SNR = 50;//搜星最大有效snr 上限
private static int STAR_MIN_SNR = 30;//最低有效snr 下限
private static int MIN_STAR_NUM = 4;//最低有效卫星数量
private ArrayList<GpsSatellite> numSatelliteList = new ArrayList();//有效搜星数量


private final GpsStatus.Listener statusListener = new GpsStatus.Listener() {
    public void onGpsStatusChanged(int event) {// GPS状态变化时的回调,获取当前状态

        if (ActivityCompat.checkSelfPermission(LocationServiceGPS.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(LocationServiceGPS.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        if (locationManager != null) {
            GpsStatus status = locationManager.getGpsStatus(null);
            // 获取卫星状态相关数据  判断 室内还是室外
            GetGPSStatus(event, status);
        }

    }
};


/**
 * @param event
 * @param status
 */

private void GetGPSStatus(final int event, final GpsStatus status) {

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

            if (status == null) {

            } else if (event == GpsStatus.GPS_EVENT_SATELLITE_STATUS) {
                //   获取最大的卫星数(这个只是一个预设值)
                int maxSatellites = status.getMaxSatellites();
                Iterator<GpsSatellite> it = status.getSatellites().iterator();
                numSatelliteList.clear();

                int count = 0;
                while (it.hasNext() && count <= maxSatellites) {
                    GpsSatellite s = it.next();
                    if (s.getSnr() >= STAR_MIN_SNR && s.getSnr() <= STAR_MAX_SNR) {//
                        //只有信躁比不为0的时候才算搜到了星 此处统计SNR在30-50间的卫星数, 重要: 需要清除星历 重测 冷启动
                        numSatelliteList.add(s);
                        //  SCLog.i(TAG, "s.getSnr()" + " snr--- " + s.getSnr());
                        count++;
                    }

                }
                if (numSatelliteList.size() >= MIN_STAR_NUM) {
                    Log.i(TAG, "updateGpsStatus----numSatelliteList.size() >= 4 numSatelliteList.size()= " + numSatelliteList.size());
                    //todo 此处判断搜星结果 为有效信号
           
                }
            } else if (event == GpsStatus.GPS_EVENT_STARTED) {
                Log.i(TAG, "updateGpsStatus----GPS_EVENT_STARTED=");
                //定位启动
            } else if (event == GpsStatus.GPS_EVENT_STOPPED) {
                //定位结束
                Log.i(TAG, "updateGpsStatus----GPS_EVENT_STOPPED=");
            }
        }
    });

}

2.2 坐标获取

变量

private LocationManager locationManager;
private String provider = LocationManager.GPS_PROVIDER;//定位提供者
private long minTime = 1000;//最小间隔时间

方法

locationManager.requestLocationUpdates(provider, minTime * 2, 5, locationListener2);

requestLocationUpdates方法四个参数分别为,位置提供者类型,最小间隔时间(毫秒),最小间隔距离(米),位置变化回调

 /**
 * 这个 只用来监听状态 定位请求交给 主动 realyRequest
 */
private final LocationListener locationListener2 = new LocationListener() {
    public void onLocationChanged(Location location) {
        //当坐标改变时触发此函数,如果Provider传进相同的坐标,它就不会被触发
        Log.i(TAG, "LocationListener  onLocationChanged");
        updateToNewLocation(location);
    }

    public void onProviderDisabled(String provider) {
        //Provider被disable时触发此函数,比如GPS被关闭
        Log.i(TAG, "LocationListener  onProviderDisabled");
    }

    public void onProviderEnabled(String provider) {
         // Provider被enable时触发此函数,比如GPS被打开
          Log.i(TAG, "LocationListener  onProviderEnabled");
    }

    public void onStatusChanged(String provider, int status, Bundle extras) {
        //  extras   provider的一些设置参数(如高精度、低功耗等)
        // Provider的转态在可用、暂时不可用和无服务三个状态直接切换时触发此函数
        switch (status) {
 //                Provider的转态
            case LocationProvider.AVAILABLE:
                Log.i(TAG, "LocationListener  onStatusChanged  LocationProvider.AVAILABLE");

                break;
            case LocationProvider.OUT_OF_SERVICE:
                Log.i(TAG, "LocationListener  onStatusChanged   .LocationProvider.OUT_OF_SERVICE");
                Pair state = new Pair(RESULT_STATE_GPS_ERR, "GPS OUT_OF_SERVICE");

                if (gpsCallBack != null) {
                    gpsCallBack.onReceiverStateInfo(state);
                }
                break;
            case LocationProvider.TEMPORARILY_UNAVAILABLE:
                Log.i(TAG, "LocationListener  onStatusChanged   .LocationProvider.TEMPORARILY_UNAVAILABLE");
                Pair state2 = new Pair(RESULT_STATE_GPS_ERR, "GPS 暂不可用");
                if (gpsCallBack != null) {
    //   gpsCallBack 是传递状态结果的接口
                    gpsCallBack.onReceiverStateInfo(state2);
                }
                break;
        }
    }
};

至于很多误导人的操作请求是

 Location newLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

这个是上次获取的已知坐标,可能是很久之前的其实没有意义。

可以从收到的坐标中获取精度最高的,同时做超时控制判断每隔一定时间获取精度最高点上报,最后移除listeners

/**
 * 移除监听
 */
private void removeListeners() {

    if (locationManager != null) {
        Log.i(TAG, "removeListeners----removeListeners");
        // 关闭程序时将监听器移除
        locationManager.removeUpdates(locationListener2);
        locationManager.removeGpsStatusListener(statusListener);
        locationManager = null;
    }
}

三、一些笔记

1.实测requestLocationUpdates要配合getLastKnownLocation一起使用,效果会更好。
2.GPS的精度问题

 location.hasAccuracy() 判断然后location.getAccuracy()获取。

官方API中提到


image.png

我理解是 以收到坐标信息为原点,以精度值为半径,真实位置在该圆内的概率是68%。

四、LocationManager

LocationManager 分析

除了上文的添加GpsStatusListener

    mLocationManager.addGpsStatusListener(statusListener)
    mLocationManager.addNmeaListener(nmeaListener);
    mLocationManager.registerGnssMeasurementsCallback(gnssMeasurementEventListener);
    mLocationManager.registerGnssStatusCallback(gnssStatucallback);
    mLocationManager.registerGnssNavigationMessageCallback(gnssNavigationMessageCallback);

1. addNmeaListener

    mLocationManager.addNmeaListener(nmeaListener);

    private final  GpsStatus.NmeaListener nmeaListener =new GpsStatus.NmeaListener() {
    @Override
    public void onNmeaReceived(long timestamp, String nmea) {

    }
};
  • NMEA 0183是用于与船用电子设备通信的标准,通过此方法获取GPS引擎接收NMEA数据。

2.registerGnssStatusCallback

GNSS 状态回调
mLocationManager.registerGnssStatusCallback(gnssStatucallback);

  private GnssStatus.Callback gnssStatucallback =new GnssStatus.Callback() {
    /**
     * Called when GNSS system has started.
     */
    public void onStarted() {}

    /**
     * Called when GNSS system has stopped.
     */
    public void onStopped() {}

    /**
     * Called when the GNSS system has received its first fix since starting.
     * @param ttffMillis the time from start to first fix in milliseconds.
     */
    public void onFirstFix(int ttffMillis) {}

    /**
     * Called periodically to report GNSS satellite status.
     * @param status the current status of all satellites.
     */
    public void onSatelliteStatusChanged(GnssStatus status) {}
};

英语也很简单,很直白就不翻译了。

3.registerGnssNavigationMessageCallback

Used for receiving GNSS satellite Navigation Messages from the GNSS engine.
用于从GNSS引擎接收GNSS卫星消息。(一般用不到)

  mLocationManager.registerGnssNavigationMessageCallback(gnssNavigationMessageCallback);

     private final GnssNavigationMessage.Callback gnssNavigationMessageCallback=new GnssNavigationMessage.Callback() {
    @Override
    public void onGnssNavigationMessageReceived(GnssNavigationMessage event) {  
   //返回最新收集的GNSS消息。
        super.onGnssNavigationMessageReceived(event);
    }

    @Override
    public void onStatusChanged(int status) {
       //返回GNSS导航系统的最新状态。
        super.onStatusChanged(status);
    }
};

4.registerGnssMeasurementsCallback

注册GPS测量回调。

   mLocationManager.registerGnssMeasurementsCallback(gnssMeasurementEventListener);
    private final GnssMeasurementsEvent.Callback gnssMeasurementEventListener =
        new GnssMeasurementsEvent.Callback() {
            @Override
            public void onGnssMeasurementsReceived(GnssMeasurementsEvent eventArgs) {
                super.onGnssMeasurementsReceived(eventArgs);
                //这里我们获取到了回调的测量数据容器:GnssMeasurementsEvent eventArgs
                  onGnssMeasurementsReceived(eventArgs);
         
            }

            @Override
            public void onStatusChanged(int status) {
                super.onStatusChanged(status);
            }
        };

五 GNSS

Android 7.0 以后官方推荐使用GNSS 。

1.GnssMeasurementsEvent

gnss 的测量数据mLocationManager.registerGnssMeasurementsCallback(gnssMeasurementEventListener)回调接受的数据
成员变量

private final GnssClock mClock;//时钟 
private final Collection<GnssMeasurement> mReadOnlyMeasurements;//测量数据

参见Android 获取GNSS原始数据

获取办法

   private void onGnssMeasurementsReceived(GnssMeasurementsEvent event) {
    StringBuilder builder=new StringBuilder("GNSS测量数据:\n\n");
    //这里的toStringClock和toStringMeasurement将写在下一步里
    builder.append(toStringClock(event.getClock()));//写入gnss时钟的数据
    builder.append("\n");

    for (GnssMeasurement measurement : event.getMeasurements()) {
        builder.append(toStringMeasurement(measurement));//写入gnss测量数据
        builder.append("\n");
    }
    
  }

  private String toStringClock(GnssClock gnssClock){
    //将GPS接收器时钟的值转换为字符串
    final String format = "   %-4s = %s\n";//定义数据显示格式,“%-4”表示左对齐、不足四位补足四位
    StringBuilder builder=new StringBuilder("GNSS时钟:\n");
    DecimalFormat numberFormat = new DecimalFormat("#0.000");//定义格式化数字


    if (gnssClock.hasLeapSecond()) {
        //如果闰秒存在则显示闰秒
        builder.append(String.format(format, "闰秒(LeapSecond)", gnssClock.getLeapSecond()));
    }
    builder.append(String.format(format, "硬件时钟(TimeNanos)", gnssClock.getTimeNanos()));//获取以毫秒为单位的GNSS接收器内部硬件时钟值
    if (gnssClock.hasTimeUncertaintyNanos()) {
        //获取硬件时钟的误差估计(不确定度)
        builder.append(String.format(format, "时钟误差估计(TimeUncertaintyNanos)", gnssClock.getTimeUncertaintyNanos()));
    }

    if (gnssClock.hasFullBiasNanos()) {
        //如果存在接收机本地时钟总偏差,则显示
        builder.append(String.format(format, "总时钟偏差(FullBiasNanos)", gnssClock.getFullBiasNanos()));
    }
    if (gnssClock.hasBiasNanos()) {
        //亚纳秒偏差
        builder.append(String.format(format, "亚偏差(BiasNanos)", gnssClock.getBiasNanos()));
    }
    if (gnssClock.hasBiasUncertaintyNanos()) {
        //FullBiasNanos和BiasNanos的误差估计
        builder.append(String.format(format, "时钟偏差估计(BiasUncertaintyNanos)", numberFormat.format(gnssClock.getBiasUncertaintyNanos())));
    }
    /**
     * 注意:以上五个数据用于计算GPS时钟
     * 具体计算方法为:local estimate of GPS time = TimeNanos - (FullBiasNanos + BiasNanos)
     *     世界标准时:UtcTimeNanos = TimeNanos - (FullBiasNanos + BiasNanos) - LeapSecond * 1,000,000,000
     */
    if (gnssClock.hasDriftNanosPerSecond()) {
        //以每秒纳秒为单位获取时钟的漂移
        builder.append(String.format(format, "时钟漂移(DriftNanosPerSecond)", numberFormat.format(gnssClock.getDriftNanosPerSecond())));
    }
    if (gnssClock.hasDriftUncertaintyNanosPerSecond()) {
        //时钟偏差的估计
        builder.append(String.format(format, "时钟漂移估计(DriftUncertaintyNanosPerSecond)", numberFormat.format(gnssClock.getDriftUncertaintyNanosPerSecond())));
    }
    //获取硬件时钟不连续的计数,即:每当gnssclock中断时,该值+1
    builder.append(String.format(format, "中断计数(HardwareClockDiscontinuityCount)", gnssClock.getHardwareClockDiscontinuityCount()));
    return builder.toString();
}


private String toStringMeasurement(GnssMeasurement measurement){
    //将GNSS测量结果转换为字符串
    //定义显示格式
    final String format = "   %-4s = %s\n";
    StringBuilder builder = new StringBuilder("GNSS测量结果:\n");
    DecimalFormat numberFormat = new DecimalFormat("#0.000");
    DecimalFormat numberFormat1 = new DecimalFormat("#0.000E00");

    //获取卫星ID
    /**
     * 取决于卫星类型
     * GPS:1-32
     * SBAS:120-151、183-192
     * GLONASS:OSN或FCN + 100之一
     * 1-24作为轨道槽号(OSN)(首选,如果知道)
     * 93-106作为频道号(FCN)(-7至+6)加100。即将-7的FCN编码为93,0编码为100,+ 6编码为106
     * QZSS:193-200
     * 伽利略:1-36
     * 北斗:1-37
     */
    builder.append(String.format(format, "卫星ID", measurement.getSvid()));

    //获取卫星类型
    /**
     *  1:CONSTELLATION_GPS 使用GPS定位
     *  2:CONSTELLATION_SBAS 使用SBAS定位
     *  3:CONSTELLATION_GLONASS 使用格洛纳斯定位
     *  4:CONSTELLATION_QZSS 使用QZSS定位
     *  5:CONSTELLATION_BEIDOU 使用北斗定位 (^-^)!
     *  6:CONSTELLATION_GALILEO 使用伽利略定位
     *  7:CONSTELLATION_IRNSS 使用印度区域卫星定位
     */
    builder.append(String.format(format, "卫星类型", measurement.getConstellationType()));

    //获取进行测量的时间偏移量(以纳秒为单位)
    builder.append(String.format(format, "测量时间偏移量", measurement.getTimeOffsetNanos()));

    //获取每个卫星的同步状态
    //具体数值含义请查表
    builder.append(String.format(format, "同步状态", measurement.getState()));

    //获取时间戳的伪距速率,以m/s为单位
    builder.append(String.format(format, "伪距速率", numberFormat.format(measurement.getPseudorangeRateMetersPerSecond())));
    //获取伪距的速率不确定性(1-Sigma),以m/s为单位
    builder.append(String.format(format, "伪距速率不确定度", numberFormat.format(measurement.getPseudorangeRateUncertaintyMetersPerSecond())));
    //
    if (measurement.getAccumulatedDeltaRangeState() != 0) {
        // 获取“累积增量范围”状态
        // 返回:MULTIPATH_INDICATOR_UNKNOWN(指示器不可用)=0
        // notice 即:指示器可用时,收集数据
        builder.append(
                String.format(format, "累积增量范围状态", measurement.getAccumulatedDeltaRangeState()));

        //获取自上次重置通道以来的累积增量范围,以米为单位.
        //该值仅在上面的state值为“可用”时有效
        //notice 累积增量范围= -k * 载波相位(其中k为常数)
        builder.append(String.format(format, "累积增量范围", numberFormat.format(measurement.getAccumulatedDeltaRangeMeters())));

        //获取以米为单位的累积增量范围的不确定性(1-Sigma)
        builder.append(String.format(format, "累积增量范围不确定度", numberFormat1.format(measurement.getAccumulatedDeltaRangeUncertaintyMeters())));
    }

    if (measurement.hasCarrierFrequencyHz()) {
        //获取被跟踪信号的载波频率
        builder.append(String.format(format, "信号载波频率", measurement.getCarrierFrequencyHz()));
    }

    if (measurement.hasCarrierCycles()) {
        //卫星和接收器之间的完整载波周期数
        builder.append(String.format(format, "载波周期数", measurement.getCarrierCycles()));
    }

    if (measurement.hasCarrierPhase()) {
        //获取接收器检测到的RF相位
        builder.append(String.format(format, "RF相位", measurement.getCarrierPhase()));
    }

    if (measurement.hasCarrierPhaseUncertainty()) {
        //误差估计
        builder.append(String.format(format, "RF相位不确定度", measurement.getCarrierPhaseUncertainty()));
    }

    //获取一个值,该值指示事件的“多路径”状态,返回0或1或2
    //MULTIPATH_INDICATOR_DETECTED = 1 测量显示有“多路径效应”迹象
    // MULTIPATH_INDICATOR_NOT_DETECTED = 2 测量结果显示没有“多路径效应”迹象
    builder.append(String.format(format, "多路经效应指示器", measurement.getMultipathIndicator()));

    //
    if (measurement.hasSnrInDb()) {
        //获取信噪比(SNR),以dB为单位
        builder.append(String.format(format, "信噪比", measurement.getSnrInDb()));
    }

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
        if (measurement.hasAutomaticGainControlLevelDb()) {
            //获取以dB为单位的自动增益控制级别
            builder.append(String.format(format, "自动增益控制级别", measurement.getAutomaticGainControlLevelDb()));
        }
        if (measurement.hasCarrierFrequencyHz()) {
            builder.append(String.format(format, "载波频率", measurement.getCarrierFrequencyHz()));
        }
    }
    return builder.toString();

}

2.GnssNavigationMessage

GNSS satellite Navigation Message.GNSS卫星导航消息。
其中event.getData()二进制数据可以按照ICD协议文件(GPS接口控制文件)逐子帧解码出卫星运行轨道参数、卫星 钟改正参数、电离层延迟改正参数等数据用于导航与定位。
绝大多数应用层开发无实际意义。

3.GnssStatus

mLocationManager.registerGnssStatusCallback(gnssStatucallback)回调中接收到的数据。
status.getSatelliteCount()来获取卫星数。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270