安卓各编译版本差异汇总(安卓10.0收集...)

安卓版本适配信息汇总,最后更新于2019年10月23日,持续收集中


  • 1:安卓4.4 API-19

    • 1.1:READ_EXTERNAL_STORAGE读取外置存储权限强制检查,此权限在低于4.4的版本默认获取。如果应用只在内部存储数据或者如下特定目录读取/写入文件,则不需要WRITE_EXTERNAL_STORAGE或者READ_EXTERNAL_STORAGE权限。如果app没有在别的地方读写存储但是minSdkVersion低于19,则可以在uses-permission标签内加入android:maxSdkVersion适配:<uses-permission ... android:maxSdkVersion="18"/>。支持的文件位置如下:

      Context API 文件指向位置 备注
      getExternalFilesDir(String) SD卡/Android/data/包名/String/ 非内置存储
      getExternalCacheDir() SD卡/Android/data/包名/cache/ 非内置存储
      getExternalMediaDirs() SD卡/Android/media/包名/ 非内置存储
      getDataDir() data/data/包名/ app内置存储根目录
      getDir(自定义dir,mode) data/data/包名/自定义dir/ 内置存储(api24开始mode缩小限定范围)
      getFilesDir() data/data/包名/files/ 内置存储
      getCacheDir() data/data/包名/cache/ 内置存储
      getPreferencesDir() data/data/包名/shared_prefs/ 内置存储
      getNoBackupFilesDir() data/data/包名/no_backup/ 内置存储(api21)
      getCodeCacheDir() data/data/包名/code_cache/ 内置存储(api21)
      getDatabasesDir() data/data/包名/databases/ 内置存储(api27)
    • 1.2:应用支持全屏模式,也就是常说的 沉浸式

    • 1.3:虚拟按键可隐藏,既然沉浸式底部导航栏跟着支持了,那就顺路把相关控制API也暴露给开发者

    • 1.4:为了加强WebView的功能,Google引入了Chromium内核。但仍然还是存在 编辑的bug


  • 2:安卓5.0 API-21

    • 2.1:ART androidRunTime取代 Dalvik 成为平台默认设置,ART采用预先编译技术,改进了垃圾回收机制与调试支持

    • 2.2:Material Design设计规范,编译版本提升至至少21时才能方便使用Appcompat-V7 RecyclerView-V7 CardView-V7等框架,在UI上突出的变化还有 SharedElementAnimation elevation等。

    • 2.3:提升用户隐私的安全性,弃用activityManager.getRecentTask()方法。对于向后兼容性,此方法仍会返回它的一小部分数据,包括应用自己的任务和可能的一些其他非敏感任务(如首页)。如果你的应用使用此方法检索它自己的任务,则改用getAppTasks检索信息

    • 2.4:禁用隐式意图启动服务,运行时会直接抛出IllegalArgumentException异常

      Intent intent = new Intent();过时禁用
      intent.setAction("com.example.myapplication");
      startService(intent);
      只能使用显式意图
      Intent intent = new Intent("com.example.myapplication");
      
    • 2.5:Notification模块不支持设置了rgb通道的smallIcon,此bug 在后续版本已经修复,但是为了系统兼容性,建议开发者在安卓5.0及以上设备上更换smallIcon以达到版本一致性

    • 2.6:WebView默认会阻止混合内容(https当中不能加载http资源),要允许请调用 WebSettings#setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW)
      默认阻止第三方 Cookie,要允许请调用CookieManager#setAcceptThirdPartyCookies(webView,true)

    • 2.7:现在可以智能地选择要绘制的 HTML 文档部分。这个新的默认行为有助于减少内存占用和提升性能。如果您要一次渲染整个文档,可通过调用 enableSlowWholeDocumentDraw() 停用此优化。

    • 2.8:自定义权限必须保证唯一,使用不同秘钥签名的应用不能使用相同的自定义权限。如果用户设备上存在一个应用使用了相同的自定义权限,那么新的应用将无法安装

    • 2.9:移除了对锁定屏幕小部件的支持

    • 2.10:新增Intent.resolveActivity(*)对发起的意图进行设备匹配


  • 3:安卓6.0 API-23

    • 3.1:移除了Apachehttp支持包,如有需要手动引入。

      android {
          useLibrary 'org.apache.http.legacy'
      }
      
    • 3.2:废弃:FloatMathNotification.setLastEventInfo()

    • 3.3:移除了对设备本地硬件标识符的编程访问权。WifiInfo.getMacAddress()方法和 BluetoothAdapter.getAddress() 方法现在会返回常量值 02:00:00:00:00:00。如果需要通过蓝牙和wifi扫描访问外部设备的硬件标识符,应用必须拥有 ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION 权限。

    • 3.4:音频管理器变更:不再支持通过 AudioManager 类直接设置音量或将特定音频流静音。setStreamSolo()方法弃用,改为requestAudioFocus() 方法。setStreamMute() 方法弃用,改为adjustStreamVolume() 并传入方向值 ADJUST_MUTE/ ADJUST_UNMUTE

    • 3.5:APK 验证更为严格。如果在清单中声明的文件在 APK 中并不存在,该 APK 将被视为已损坏。移除任何内容后必须重新签署 APK。

    • 3.6:运行时权限管理,谷歌借鉴了苹果 和国内厂商的优化经验,对应用的权限不再一刀切,而是把相关权限分成了三组

      权限名称 权限说明
      SYSTEM_ALERT_WINDOW 设置悬浮窗,TYPE_SYSTEM_ALERT已失效
      WRITE_SETTINGS 修改系统设置
      • 敏感权限 运行时申请,如果没有适配会直接抛出异常,许可状态会保存
      权限组 权限名称 介绍
      CALENDAR android.permission.READ_CALENDAR 读取系统日历
      android.permission.WRITE_CALENDAR 写入系统日历
      CAMERA android.permission.CAMERA 相机权限
      CONTACTS android.permission.READ_CONTACTS 读取联系人
      android.permission.WRITE_CONTACTS 写入联系人
      android.permission.GET_ACCOUNTS 读取账号
      LOCATION android.permission.ACCESS_FINE_LOCATION 获取精准位置
      android.permission.ACCESS_COARSE_LOCATION 获取大概位置
      MICROPHONE android.permission.RECORD_AUDIO 录音
      PHONE android.permission.READ_PHONE_STATE 获取手机信息
      android.permission.CALL_PHONE 打电话,不建议使用
      android.permission.READ_CALL_LOG 读取通话记录
      android.permission.WRITE_CALL_LOG 写入通话记录
      android.permission.PROCESS_OUTGOING_CALLS 监听、控制、取消呼出电话的权限
      android.permission.USE_SIP 使用sip(会话发起协议)网络电话
      android.permission.ADD_VOICEMAIL 添加系统中的语音邮件
      SENSORS android.permission.BODY_SENSOR 使用生命体征相关传感器
      SMS android.permission.SEND_SMS 发送短信
      android.permission.RECEIVE_SMS 接收短信
      android.permission.READ_SMS 读取短信
      android.permission.RECEIVE_WAP_PUSH 接收WapPush信息
      android.permission.RECEIVE_MMS 接收彩信
      android.permission.READ_CELL_BROADCASTS 不存在的权限
      STORAGE android.permission.READ_EXTERNAL_STORAGE 读取存储
      android.permission.WRITE_EXTERNAL_STORAGE 写入存储

      运行时权限共9组,同一组下如果有一个权限被允许,其它权限会自动被允许。
      开发时应减少申请权限的次数,以避免交互被打断。对于敏感权限可用intent来代替,让其它应用去处理

  • 4:安卓7.0 API-24

    • 4.1:应用分屏,支持java8。通过Context.getDir(name,mode)获取存储文件夹时不能再使用MODE_WORLD_READABLEMODE_WORLD_WRITEABLE,与此相似的还有SharedPreferenceDataBase。否则会抛出SecurityException

    • 4.2:Notification.Builder通知消息可以通过setShowWhen()设置定时显示

    • 4.3:删除了三个常用隐式广播 : CONNECTIVITY_ACTIONACTION_NEW_PICTUREACTION_NEW_VIDEO 。因为这些广播可能会一次唤醒多个应用的后台进程,消耗内存和电池

    • 4.4:优化了SurfaceView,使其在视频播放和3D方面表现更优于TextureView

    • 4.5:APK signature scheme v2,android 7.0 新引入应用签名方案,它能提供更快的应用安装时间和更多针对未授权 APK 文件更改的保护。

    • 4.6:StrictMode API政策禁止在您的应用外部公开file:// URI。如果启动一个包含文件 URI 的intent会抛出FileUriExposedException异常。受此影响的有拍照图片选择,下载apk安装等功能。要在应用间共享文件,您应发送一项content:// URI,并授予 URI 临时访问权限。步骤如下:

      • 4.6.1:在AndroidManifest.xml中添加<provider>标签
         <provider
              tools:replace="android:authorities"(如果使用的依赖module也存在provider则添加此tools)
              android:name="android.support.v4.content.FileProvider"
              android:authorities="替换为你的包名.fileProvider"
              android:grantUriPermissions="true"
              android:exported="false">
              <meta-data
                  android:name="android.support.FILE_PROVIDER_PATHS"
                  android:resource="@xml/file_provider_paths" />
          </provider>
        
      • 4.6.2:在res/xml文件夹里面创建xml文件file_provider_paths.xml,文件名要和上方的android:resource对应值保持一致
        各个元素的定义可参考android.support.v4.content.FileProvider
        <?xml version="1.0" encoding="utf-8"?>
        <paths xmlns:android="http://schemas.android.com/apk/res/android">
            <root-path name="root_path_name" path="test"/>"/" 
            <files-path path="file_path_name" name="images"/>Context.getFilesDir() 示例位置
            <cache-path name="cache_path_name" path="images" />Context.getCacheDir()
            <external-path name="external_path_name" path=" ." />Environment.getExternalStorageDirectory()
            <external-files-path name="external_file_path_name" path="images" />Context#getExternalFilesDir(String)
            <external-cache-path name="external_cache_path_name" path="images" />
            <external-path name="external_path_name_01" path="Android/data/com.papa.auction/"/>
            <external-path name="external_path_name_02"  path="."/>
        </paths>
        
        • 4.6.3:修改拍照intent示例:
        Intent intentCamera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        判断设备是否有应用可以处理打开相机intent
        if(intent.resolveActivity(context.getPackageManager()) != null){
            Uri cameraUri;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                cameraUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileProvider", file);
            }else {
                cameraUri = Uri.fromFile(file);
            }
            intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraUri);
            context.startActivityForResult(intent,requestCode);
        }
        
        • 4.6.4:修改安装APK intent示例:
        Intent installIntent = new Intent(Intent.ACTION_VIEW));
        installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  根据启动位置决定是否添加
        Uri apkFileUri;
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
           installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
           apkFileUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileProvider", apkFile);
        } else {
            apkFileUri = Uri.fromFile(apkFile);
        }
        installIntent.setDataAndType(apkFileUri, "application/vnd.android.package-archive");
        context.startActivity(installIntent);
        
    • 4.7:加密库 Crypto废弃 将密码作为随机数生成器的种子换算出密钥 key这种做法已经被认定为是不安全的。相关的 Crypto provider 和 SHA1PRNG 算法同时废弃掉了,并计划在后续的 SDK 中完全移除相关的库,如下是官方给出的解决方案:

        给出字符串的密码
       String password = "password";  
      
        密钥的比特位数,注意这里是比特位数
        AES 支持 128、192 和 256 比特长度的密钥
       int keyLength = 256; 
      
        盐值的字节数组长度,注意这里是字节数组的长度
        其长度值需要和最终输出的密钥字节数组长度一致
        由于这里密钥的长度是 256 比特,则最终密钥将以 256/8 = 32 位长度的字节数组存在
        所以盐值的字节数组长度也应该是 32
       int saltLength = 32;
       byte[] salt;
      
        先获取一个随机的盐值
        你需要将此次生成的盐值保存到磁盘上下次再从字符串换算密钥时传入
        如果盐值不一致将导致换算的密钥值不同
        保存密钥的逻辑官方并没写,需要自行实现
       SecureRandom random = new SecureRandom();  
       byte[] salt = new byte[saltLength];  
       random.nextBytes(salt);  
      
        将密码明文、盐值等使用新的方法换算密钥
       int iterationCount = 1000;
       KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt,iterationCount, keyLength);  
       SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");  
      
        到这里你就能拿到一个安全的密钥了
       byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();  
       SecretKey key = new SecretKeySpec(keyBytes, "AES");
      

  • 5:安卓8.0 API-26 (8.1 API-27)

    • 5.1:roundedIcon 自适应图标,在manifest文件中额外提供圆形应用icon
      <application
          android:name=".App"
          android:icon="@mipmap/ic_launcher"
          android:roundIcon="@mipmap/ic_launcher_round"
      ......
      
    • 5.2:PHONE权限组新增两个运行时权限
      • ANSWER_PHONE_CALLS:允许接听呼入电话。在应用中使用 acceptRingingCall()函数处理呼入电话。
      • READ_PHONE_NUMBERS:允许应用读取设备中存储的电话号码。
    • 5.3:安卓8.0中,Notification的通知渠道。用户可以根据渠道来屏蔽一些不想要的通知
      void setNotifyChannel(NotificationManager manager, NotificationCompat.Builder builder, String channeId, String channelName) {
          if (TextUtils.isEmpty(channeId)||TextUtils.isEmpty(channelName)){
              L.e("NotifyCompat:  ".concat("安卓8.0的通知兼容库中 channeId 与 channelName 不能为empty"));
          }
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
              //第三个参数设置通知的优先级别
              NotificationChannel channel = new NotificationChannel(channeId, channelName, NotificationManager.IMPORTANCE_DEFAULT);
              channel.canBypassDnd();//是否可以绕过请勿打扰模式
              channel.canShowBadge();//是否可以显示icon角标
              channel.enableLights(true);//是否显示通知闪灯
              channel.enableVibration(true);//收到小时时震动提示
              channel.setBypassDnd(true);//设置绕过免打扰
              channel.setLockscreenVisibility(NotificationCompat.VISIBILITY_SECRET);
              channel.setLightColor(Color.RED);//设置闪光灯颜色
              channel.getAudioAttributes();//获取设置铃声设置
              channel.setVibrationPattern(new long[]{100, 200, 100});//设置震动模式
              channel.shouldShowLights();//是否会闪光
              if (manager != null) {
                  manager.createNotificationChannel(channel);    //manager 与channel关联
              }
              if (builder != null) {
                  builder.setChannelId(channeId);//Notification 与channel关联
              }
          }
      }
      
    • 5.4:新增特殊权限:android.permission.REQUEST_INSTALL_PACKAGES当需要安装未知来源应用时使用,示例如下
      private void installAPK(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
          boolean hasInstallPermission = getPackageManager().canRequestPackageInstalls();
          if (hasInstallPermission) {
              安装应用
          } else {
              提示用户跳转至“安装未知应用”权限界面,引导用户开启权限
              Uri selfPackageUri = Uri.parse("package:" + this.getPackageName());
              Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, selfPackageUri);
              startActivityForResult(intent, REQUEST_CODE_UNKNOWN_APP);
          }
        }else {
          安装应用
        }
      }
      
    • 5.6:SharedPreferences不能使用MODE_WORLD_READABLE,请修改为MODE_PRIVATE
    • 5.6:新的广播接收器限制导致静态广播无法正常接收,应使用动态广播代替静态广播
    • 5.7:List.sort() 的实现不能调用 Collections.sort(),因为这会导致堆栈因无限递归而溢出
    • 5.8:申请了SYSTEM_ALERT_WINDOW权限的应用需要在其他应用和系统窗口上方显示提醒窗口时只能使用TYPE_APPLICATION_OVERLAY类型,之前的TYPE_PHONE TYPE_PRIORITY_PHONE TYPE_SYSTEM_ALERT TYPE_SYSTEM_OVERLAY TYPE_SYSTEM_ERROR不再生效。
    • 5.9:在 Android 8.0 之前,如果应用在运行时请求权限并且被授予该权限,系统会将同一权限组且在Manifest中注册的其他权限也一起授予应用。此行为在8.0被纠正:系统只会授予应用明确请求的权限。然而,一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准。
    • 5.10:Only fullscreen opaque activities can request orientation,在Android 8.0上非全屏透明页面不允许设置方向(8.1以上谷歌就修复去掉了这个限制)

      解决方案1:在manifest中去掉此Activity的screenOrientation属性
      解决方案2:在此Activity的style里设置<item name="android:windowIsTranslucent">false</item>
      解决方案3:使用Dialog/DialogFragment/Popwindow代替此Activity。

    更多细节参阅:
    https://developer.android.com/about/versions/oreo/android-8.0-changes#all-apps
    https://developer.android.com/about/versions/oreo/android-8.1


  • 6:安卓9.0 API-28

    • 6.1:限制明文流量的网络请求,非加密的流量请求会被系统禁止掉
      解决方案:在xml目录新建文件network_security_config.xml

      <?xml version="1.0" encoding="utf-8"?>
        <network-security-config>
        <base-config cleartextTrafficPermitted="true" />
      </network-security-config>
      

      在manifest清单文件application节点配置android:networkSecurityConfig="@xml/network_security_config"

    • 6.2:使用apacheHttp支持包时出现classNotFound 错误,从 Android 9 开始,Apache HTTP的支持内容库已从 bootclasspath 中移除,且默认情况下应用无法使用它。请在Manifest文件的application节点中加入如下设置:<uses-library android:name="org.apache.http.legacy" android:required="false" />

    • 6.3:java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed
      安卓9.0对canvas#clipPath()中的Region.Op做了限制只能使用INTERSECT或者DIFFERENCE。

      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
       canvas.clipPath(mPath);
      } else {
       canvas.clipPath(mPath, Region.Op.REPLACE);
      }
      
    • 6.4:弃用 Bouncy Castle 加密。Android 9 弃用了几个来自 Bouncy Castle 提供程序中的加密技术,代之以由 Conscrypt 提供程序提供的加密技术。调用请求 Bouncy Castle 提供程序的 getInstance() 时,会生成 NoSuchAlgorithmException 错误。要解决这些错误,请不要在 getInstance() 中指定提供程序(也就是请求默认实现)。

    • 6.5:前台服务权限。要使用前台服务的应用必须注册普通权限 FOREGROUND_SERVICE。在未获得此权限的情况下启动前台服务将会引发 SecurityException。

    • 6.6:移除对 Build.serial 的直接访问。使用Build.getSerial() 函数来替代 Build.serial获取标识符。应用必须请求READ_PHONE_STATE 权限。

    • 6.7:Detected problems with API 弹窗的解决:https://blog.csdn.net/codekxx/article/details/86507470#comments

    • 更多细节参阅:https://developer.android.com/about/versions/pie


  • 7:安卓10.0 API-29

    • 7.1:TelephonyManager#getDeviceId(),需要申请READ_PRIVILEGED_PHONE_STATE权限,此权限只开发给系统app,按照谷歌的建议如果不是做framework层app开发就不要使用此api,普通app此处需要大量适配,因为三方支付类统计类支持包都会调用此api,如果不能全部更新就不要把target提升到29,否则会直接抛出SecurityException:
      getDeviceId: The user 10104 does not meet the requirements to access device identifiers
      at android.os.Parcel.createException(Parcel.java:2071)
      at android.os.Parcel.readException(Parcel.java:2039)
      at android.os.Parcel.readException(Parcel.java:1987)
      at com.android.internal.telephony.ITelephony$Stub$Proxy.getDeviceId(ITelephony.java:10389)
      

      在安卓9.0时此方法已经提示deprecated,建议开发者用getImei()返回GSM网络的IMEI,或者getMeid()返回CDMA网络的MEID。

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

推荐阅读更多精彩内容