上传Android studio项目到Maven项目仓库

上传Android studio项目到Maven项目仓库

注:

1、因项目特殊性,部分截图中跟项目有关的字眼会打码,请见谅

2、本人使用的是mac,跟Windows有些不同,特别是文件地址的格式

在我们工作过程中,一定有不少同学自己写了一些框架性的东西,或者一些好用的工具、一些自定义的控件,总之就是能复用的代码。然而也有不少同学为了复用这些代码不得不复制粘贴到不同项目中,这样相同的功能出现了多份代码,在后期的维护过程中极为不便,修复其中一处bug将要改动几份代码。为了解决这些问题,我们不妨将这些代码部署到maven仓库,一处编写多处使用。

1、Maven是什么?

Maven这个单词来自犹太语,意为只是的积累,最初在Jakata Turbine项目中用来简化构建过程。当时有一些项目,仅有细微的差别,于是希望有一种标准化的方式构建项目,一个清晰的方式定义项目的组成,一个容易的方式发布项目信息,以及一种简单的方式在多个项目中共享jar包。Maven主要目标是让项目可重复使用、易维护、更容易理解… 说人话emm    Maven是一个项目管理工具,我们主要用于存放一些类库、插件等方便共享。

2、Maven仓库在哪里?

我的理解,Maven就像是svn和git,我们项目开发过程中,通常会使用这些版本控制工具托管代码,svn和git使得我们清楚的了解两个版本之间的差异。以git为例,我们项目根目录下通常会有个.git文件夹,这个文件夹就是git的本地仓库,如果我们需要远程开发,通常需要将代码推到远程仓库中,比如github、码云等等。版本控制工具作用重在区别版本之间的差异,而Maven作用重在共享依赖库。

Maven仓库和git一样,可以放在本地,可以放在服务器(私有或者公有)。下面,我们用一个项目演示一下三种Maven仓库的构建及使用方法。

3、本地仓库

有一个自定义的控件类库,需要在不同的项目中使用,我可以将类库拷贝到这些项目中,项目结构如下:

image-20210129100231014.png

然后在app/build.gradle中添加依赖implementation project(’:trackingsystemsdk’),这样就可以在项目中使用了。突然有一天我发现了一个bug,并把它修复了,但是只有一个项目中的类库修复,其他的项目中类库还是有bug,于是我不得不将lib代码复制到不同项目库中,这样还算是共享库吗?

为了解决这个问题,我们可以将这个类库发布到本地Maven仓库中,然后在不同的项目中依赖此类库即可。下面是发布本地仓库流程:

3.1 . uploadArchives

uploadArchives是一个发布类库到中央仓库的Task,我们需要为它指定本地仓库路径以及类库的一些信息,在类库模块的build.gradle中添加task。配置完成后点击Sync Now,然后在Gradle projects窗口中:项目lib->Tasks中会多出一个upload目录,里面就有一个名为uploadArchives的task,这个uploadArchives就是将类库发布到仓库的task。

image-20210129155246228.png

3.2 . 执行uploadArchives

执行uploadArchives有两种方式,一种是直接双击上面截图中的task;另一种是在Terminal中输入 gradlew uploadArchives 然后回车。执行完成后,在项目根目录下 多出一个repository目录,目录结构就是上面再build.gradle中的配置。其中项目lib-1.0.0.arr**就是我们需要的类库,到此为止就完成了本地Maven仓库的发布。

image-20210129155600204.png

3.3 . 使用本地仓库中的类库

在使用的时候,我们需要告诉gradle构建工具去哪里找到我们所需要的类库,在项目工程的build.gradle中,我们需要告诉gradle本地maven仓库的地址:

allprojects {
     repositories {
         google()
         jcenter()
         //本地Maven仓库地址
         maven { 
               url '..//repository' 
         }
     }
}

需要注意的是,在build.gradle中有两个地方可以配置repository(仓库)。buildscript里面配置的是gradle构建项目时需要的插件的maven仓库;而allprojects中是项目依赖库的仓库配置。如果将我们的仓库地址配置到buildscript中,将会导致依赖库找不到Failed to resolve: groupId:artifactId:version

然后在app模块build.gradle中添加依赖编译运行成功:

//groupId,artifactId,version都修改成提交到maven时的证实信息
implementation 'groupId:artifactId:version'

到此为止本地Maven仓库就介绍完毕。

4、局域网私有仓库

将类库发布到本地仓库后,我们可以方便的在多个项目中使用同一个类库,但是,日常工作中通常是一个团队在开发,我将仓库发布在本地,队友根本访问不了。如果将仓库搭建在公司内网服务器上,整个公司的成员就都能访问了,最常用的搭建Maven私有仓库的工具Nexus就可以为我们解决这个问题,按照步骤一步步来吧:

4.1 . 下载Nexus

这里就不做详细介绍,直接附上教程链接

下载并安装nexus教程:https://www.jianshu.com/p/9740778b154f

4.2 . 运行

Windows系统双击console-nexus.bat启动nexus,macOS在终端切换到nexus的bin目录下,执行 nexus/run,在浏览器中访问 http://127.0.0.1:8081/nexus/,如果出现下图所示则为启动成功,点击Repositories查看Maven仓库管理界面。

这里写图片描述

可以在conf\nexus.properties文件中修改nexus的端口号:

这里写图片描述

4.3 . 发布

首先我们要清楚,我们需要发布什么东西到maven仓库中。在Android Studio项目中依赖模块后,Android Studio会将被依赖的模块打包成.arr包,这个包就是被依赖的包,其中包含了编译后的java文件和一些资源文件。.arr.jar的性质是一样的,无非.arr多了一些资源文件,这是google为android设计的一种也有的lib格式,我们需要共享的正是这个.arr文件。

在需要发布的Modul下build.gradle中添加uploadArchives任务(nexus正常情况默认的用户名和密码分别是:adminadmin123):

/**发布到私有服务器maven仓库*/
apply plugin: 'maven'
​
// type显示指定任务类型或任务, 这里指定要执行Javadoc这个task,这个task在gradle中已经定义
task androidJavadocs(type: Javadoc) {
 // 设置源码所在的位置
 source = android.sourceSets.main.java.sourceFiles
}
​
// 生成javadoc.jar
task androidJavadocsJar(type: Jar) {
 // 指定文档名称
 classifier = 'javadoc'
 from androidJavadocs.destinationDir
}
​
// 生成sources.jar
task androidSourcesJar(type: Jar) {
 classifier = 'sources'
 from android.sourceSets.main.java.sourceFiles
}
​
// 产生相关配置文件的任务
artifacts {
 archives androidSourcesJar
 archives androidJavadocsJar
}
​
//上传到Maven仓库的task
uploadArchives {
   repositories {
   mavenDeployer {
     //指定maven仓库url
     repository(url: "http://localhost:8081/nexus/content/repositories/releases/") {
     //nexus登录默认用户名和密码
     authentication(userName: "admin", password: "admin123")
 }
     pom.groupId = "groupId 如com.android.sdk"// 唯一标识(通常为模块包名,也可以任意)
     pom.artifactId = "项目名称 如mysdk" // 项目名称(通常为类库模块名称,也可以任意)
     pom.version = "版本号" // 版本号
   }
 }
}

发现跟发布到本地maven仓库的配置差不多,就是一个uploadArchives任务,最大的区别就是指定的repository仓库地址不一样而已。接下来执行uploadArchives任务(双击 or gradlew uploadArchives),BUILD SUCCESSFUL后,我们使用浏览器看看仓库中发布成功的依赖包

image-20210201101807593.png

4.4 . 使用私有仓库中的依赖包

和使用本地仓库依赖一样,我们告诉gradle依赖包仓库的位置,在项目根目录下build.gradle中添加:

allprojects {
   repositories {
       jcenter()
       //本地仓库地址
       // maven { url '..//repository' }
       //私有服务器仓库地址
       maven { 
           url 'http://localhost:8081/nexus/content/repositories/releases/' 
       }
     }
}

然后在app模块build.gradle中添加依赖编译运行成功:

//groupId,artifactId,version都修改成提交到maven时的证实信息
implementation 'groupId:artifactId:version'

5、发布到JCenter

局域网私有仓库能解决团队内部资源共享,然而你觉得你写的框架或者类库很牛逼,不仅仅想让队友使用,还想让更多的开发者用到这么好的东西,这时候就需要将项目发布到中央仓库中,比如mavenCentraljcenter等。Android Studio最开始将 Maven Central作为默认仓库,但由于某些原因,从Android Studio 0.8开始将JCenter作为默认仓库。不管选用那个仓库,他们的作用和目的都是一样的,就像你想买手机,选择华为还是小米。接下来我们以Jcenter为例,看看怎么将项目发布到中央仓库中。

5.1 . 注册账号(注册企业账号审核流程要一个多月之久,建议使用个人用户注册)

首先你需要注册一个Bintray账号,注意此处需要点击Sign Up Here注册(或者直接访问https://bintray.com/signup/oss) 。绿色按钮是注册企业账号的,需要付费,有试用期,我们需要注册开发者账户,有很多文章没有标明这个问题。如果有人错误注册为企业账号,可从Edit Profile–>Delete Account删除后重新注册。

重要的事情说三遍:

个人用户注册地址:https://bintray.com/signup/oss

个人用户注册地址:https://bintray.com/signup/oss

个人用户注册地址:https://bintray.com/signup/oss

这里写图片描述

5.2 . 配置

网上有很多关于上传到JCenter的文章,如果根据这些文章一步步走下去或多或少都会出现一些意想不到的问题,可能某一个问题就会缠绕你一整天,包括你现在读到我的文章,我也不能确保你能顺利一次成功,遇到问题找到问题的根源才能解决问题。上传到JCenter需要使用一些插件(gradle中配置),一般有两种方式:

  • gradle-bintray-plugin

    这个插件的地址 https://github.com/bintray/gradle-bintray-plugin,使用此插件的人非常多,但是gradle配置内容较多,稍不小心就会出问题,这也就导致很多人根据别人的文章一步步走一般都不会成功了。

  • bintray-release

    地址https://github.com/novoda/bintray-release,此插件相比上面的就简单很多了,主要是配置内容少了很多,即使出了问题解决也相对容易。

这两个插件相关配置在各自插件的github上都有步骤,感兴趣的可以看看。既然两个插件的目标都是一致的,我们就选用简单点的试试吧。

Step 1

在项目的build.gradle中buildscript中添加如下脚本,使用最新版本号,在https://github.com/novoda/bintray-release查看:

buildscript {
   repositories {
   jcenter()
   }
   dependencies {
     //classpath 'com.novoda:bintray-release:<latest-version>'
     //目前最新的是0.9.2,这里用0.8.1,可自行选择
     classpath 'com.novoda:bintray-release:0.8.1'
   }
}

Step 2

在库模块(需要上传的模块)的build.gradle中添加:

//提交到bintray
apply plugin: 'com.novoda.bintray-release'
publish {
   userOrg = 'bintray账户下创建de组织id' //bintray账户下某个组织id
   groupId = 'groupId 如com.android.sdk' //maven仓库下库的包名,一般为模块包名
   artifactId = '项目名称 如mysdk' //项目名称
   publishVersion = '版本号' //版本号
   desc = '项目介绍,可以为空' //项目介绍,可以不写
   website = '项目介绍,可以为空' //项目主页,可以不写
}

注意:

  • groupIdartifactIdpublishVersion就是使用时依赖的组成;

  • userOrg不是用户名,网上很多文章直接写的用户名,是因为老版本,在bintray网站升级之后,增加了组织的概念,需要将仓库放到某个组织下,所以在新版本中这里要填写组织id,如果你的账户下没有组织,需要创建一个。可以点击账户下Create Organization–>Create new organization创建,也可以点击Manage Organizations–>New Organization–>Create new organization创建。创建完成后,将组织ID填写到userOrg中

image-20210201111607081.png

组织创建完成之后,我们还需要在组织下创建一个maven仓库,步骤如下:


image-20210201111841948.png

maven仓库创建完成之后,我们还需要在仓库下创建一个项目,步骤如下:


image-20210201112601304.png

创建完成之后,就可以继续下面的步骤了

Step 3

上传,首先我们需要获取API Key,Bintray网站点击右上角用户名–>Edit Your Profile -> API Key -->输入密码–>Submit–>Show。


image-20210201112154167.png

添加完成之后编译就通过了,但是上面仓库地址和依赖是从哪里来的呢?访问我们上传成功的项目页面,可以看到相关信息。如果我们将项目添加到JCenter中,以后依赖就不需要添加maven { url 'https://dl.bintray.com/xx/maven' }了,当然还是需要告诉gradle依赖库的仓库位置,只需要添加jcenter()就行了。

在Android Studio的Terminal面板中执行下面命令,其中BINTRAY_USERNAME替换为你的binatray用户名,BINTRAY_KEY替换为上面获取的API Key,-PdryRun=false会上传到仓库中,如果为true,只会执行gradle任务,但不会上传。替换完成后回车执行

//mac执行
./gradlew clean build bintrayUpload -PbintrayUser=BINTRAY_USERNAME -PbintrayKey=BINTRAY_KEY -PdryRun=false
    ​
//Windows执行
gradlew clean build bintrayUpload -PbintrayUser=BINTRAY_USERNAME -PbintrayKey=BINTRAY_KEY -PdryRun=false

Step 4

使用,我们需要告诉gradle依赖包仓库的位置,在项目根目录下·build.gradle·中添加:

allprojects {
     repositories {
       google()
       jcenter()
       //本地仓库地址
       // maven { url '..//repository' }
       //私有服务器仓库地址
       // maven { url 'http://localhost:8081/nexus/content/repositories/releases/' }
       //https://bintray.com仓库地址
       maven { url 'https://dl.bintray.com/oxo1/maven' }
     }
}

和然后在app模块build.gradle中添加依赖:

//groupId,artifactId,version都修改成提交到maven时的证实信息
implementation 'groupId:artifactId:version'

Step 5

添加到JCenter。点击“Add to JCenter”,填写项目介绍,点击Send发送,然后等待审核,审核成功之后会发送站内通知

6、发布到Maven中央仓库

6.1、通过SonaType把你的开源库提交到Mavne的中心库

  • 注册一个SonaType账号

  • 注册一个GitHub账号

  • 创建开源库

  • 新建一个类型为Community Support - Open Source Project Repository Hosting (OSSRH)的项目


    image-20210201115040494.png

创建完毕后就等待审核(晚上审核)。当状态变为“resolved”,然后你就可以使用Gradle上传项目了。或者就等着接收 sonatype 的反馈邮件,确认已经为你创建好了新项目。

6.2、上传项目到 Maven 中心仓库

示例项目是一个 Android 项目,所以通过在开源库的 Libray Module 下的 build.gradle 末尾添加如下配置:

//代码片有多种上传方式,其中android标签和dependencies标签都是常规代码,没有做修改,这里就忽略了
​
apply plugin: 'com.android.library'
​
// type显示指定任务类型或任务, 这里指定要执行Javadoc这个task,这个task在gradle中已经定义
task androidJavadocs(type: Javadoc) {
 // 设置源码所在的位置
 source = android.sourceSets.main.java.sourceFiles
}
​
// 生成javadoc.jar
task androidJavadocsJar(type: Jar) {
 // 指定文档名称
 classifier = 'javadoc'
 from androidJavadocs.destinationDir
}
​
// 生成sources.jar
task androidSourcesJar(type: Jar) {
 classifier = 'sources'
 from android.sourceSets.main.java.sourceFiles
}
​
// 产生相关配置文件的任务
artifacts {
 archives androidSourcesJar
 archives androidJavadocsJar
}
​
/*上传maven中央
apply plugin: 'maven'
apply plugin: 'signing'
​
// 进行数字签名
signing {
 required { gradle.taskGraph.hasTask("uploadArchives") }
 sign configurations.archives
}
uploadArchives {
    repositories {
       mavenDeployer {
       //不加会导致缺失.pom.asc文件(因为提交正式版需要审核,缺失.pom.asc文件回导致审核出错)
         beforeDeployment {
             MavenDeployment deployment -> signing.signPom(deployment)
         }
         //打包到正式maven中央仓库
             repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2") {
                 authentication(userName: "username", password: "password")
         }
​
         //打包到快照地址
         snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots") {
               authentication(userName: "username", password: "password")
         }
​
         //打包到本地地址
         //repository(url: uri('../repository'))
​
         pom.project {
             name 'name(跟artifactId一样就行了)'
             version '版本号'
             artifactId '项目名称 如mysdk'
             groupId 'groupId 如com.android.sdk'
             packaging 'aar'
             description '备注'
​
         developers {
             developer {
                 id 'username'
                 name 'username'
                 email 'email'
           }
         }
​
         scm {
             connection 'scm:git地址(注意要带上.git)'
             developerConnection 'scm:git地址(注意不要带上.git)'
             url 'git地址(注意要带上.git)'
         }
​
         licenses {
             license {
               name 'The Apache License, Version 2.0'
               url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
             }
         }
       }
     }
   }
}

在上面的代码中,大家请将根域中的 group , archiveBaseName , version 以及 uploadArchives#pom.project 中的相关描述信息替换成自己的就好了。这些信息将会在接下来上传的时候,自动打包成 Maven 项目,并封装相应信息。

6.3、配置上传时需要的签名以及密码等相关信息

首先我们需要创建一个 gpg 签名秘钥,并将公钥上传到指定服务器。对于在 MacOS 下,我们可以下载一个叫做GPGTools (http://www.gpgtools.org)的工具生成我们的签名文件。在 Windows 下,我们下载安装一个 Kleopatra(https://www.gpg4win.org/thanks-for-download.html)工具生成我们的签名文件。这两个工具使用基本一致,在安装完成之后,新建一个密钥对,并将本地公钥上传到指定服务器。

局域网私有仓库能解决团队内部资源共享,然而你觉得你写的框架或者类库很牛逼,不仅仅想让队友使用,还想让更多的开发者用到这么好的东西,这时候就需要将项目发布到中央仓库中,比如mavenCentraljcenter等。Android Studio最开始将 Maven Central作为默认仓库,但由于某些原因,从Android Studio 0.8开始将JCenter作为默认仓库。不管选用那个仓库,他们的作用和目的都是一样的,就像你想买手机,选择华为还是小米。接下来我们以Jcenter为例,看看怎么将项目发布到中央仓库中。

安装PGP教程:https://www.cnblogs.com/cocoajin/p/5969069.html

注:mac加密命令:

#生成密钥方式
#gpg --export-secret-keys -o secring.gpg

上传完成之后,就可以正式配置签名信息以及你的 Maven 账户信息了,我们在 Library Module 的根目录下新建一个 gradle.properties 文件,内容如下:

signing.keyId= 密钥的ID  (注意一下,密钥ID 是一个八个字节的字符串 Kleopatra工具需要悬浮在 密钥ID 一栏查看 )
signing.password= 密钥的密码 (生成证书时填写的密码)
signing.secretKeyRingFile=..\\secret.gpg(secret.gpg为私密证书。将导出的私密证书,放置在工程目录下)
ossrhUsername= sonatype 账号
ossrhPassword= sonatype 密码

配置成功之后,按照下图运行 uploadArchives 就可以开始上传了,这样就可以将你的文件上传到 Maven 中央库了

6.4、将上传的开源库发布出去

通过上面的步骤,我们只是把开源库放置在了一个私有的 Maven 仓库中,是不能被其他人所访问的。所以还需要我们自己将它发布出去。那么如何发布:

① 进入 Maven 项目控制台

进入以下地址:https://oss.sonatype.org/,并使用 sonatype 的账号密码在右上角进行登录。

② 查找你上传的项目

如果你登录成功,在左侧的导航栏,你会看到一个叫做 Staging Repositories 的选项,点击它,你会发现出现了很多列表选项,如下图。

根据列表名称,你会发现一个以你的 groud id 去掉点号后加上一个四位数字命名的Repository,选中它,你会发现上方的 Close 按钮亮起,当你确认上传无误之后,你可以点击 Close 按钮,关闭掉这个仓库,不允许再次上传。如果关闭成功,你点击刷新以后会发现,Release 按钮亮起,点击它即可发布。如果关闭失败,你可以看看下方的界面,查找一下失败原因,再次上传代码,关闭后发布。

上图就是我的解决过程,然后再 close ,再 release ,OK 这就将开源库发布出去了。

6.5、查找开源库

注意开源库发布出去之后,还是需要等待一段时间,才能在这里 https://search.maven.org/ 查找我们的开源库,以及查看如何依赖自己的开源库。我是下班第二天来看的,查收 email 也是有通知的。

以上,几种maven仓库的操作基本完成了,当然过程不可能这么顺利,很多人在这个过程中都会遇到不少问题,不同的人问题不一样,网上也有很多文章,这里感谢踩坑的前辈,我就不再赘述,这个流程是我披荆斩棘最后成功的流程,如果让我按照上面的流程再操作一次应该会一次成功,那有同学按照这个步骤出现问题,可以留言。

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

推荐阅读更多精彩内容