项目档案

在线版本的目录赢博体育程序

单击上面的按钮下载下一个示例项目的代码。这个项目是Directory赢博体育程序的在线版本。这个版本的赢博体育程序与一个存储赢博体育使用该赢博体育程序的用户的赢博体育目录条目的web服务通信。

我已经设置了一个运行在https://cmsc106.net/directory上的后端Directory赢博体育程序来满足这个赢博体育程序的需求。

更新界面

我们将再次使用Retrofit来管理与后端服务器的通信。使用Retrofit的第一步是为我们计划进行的每个不同的服务器交互设置一个带有方法的接口。下面是这个赢博体育程序的界面:

进口retrofit2.http。GET import retrofit2.http。POST导入retrofit2.http。删除导入retrofit2.http。正文导入retrofit2.http。查询导入retrofit2.http。路径界面DirectoryApiService {@POST(“用户”)暂停有趣分类列出(@Body用户:用户):字符串@ get(“用户”)暂停有趣登录(@Query(“用户”)用户:字符串,@Query(“密码”)密码:字符串):字符串@ get(“人/{关键}”)暂停有趣getPeople (@ Path(“关键”)键:字符串):列表<人> @POST(“人/{关键}”)暂停有趣postPerson (@ Path(“关键”)键:字符串,@Body人:人):人@DELETE(“人/{关键}/ {id}”)暂停有趣removePerson (@ Path(“关键”)键:字符串,@ Path (" id ") id: Int):字符串}

在天气赢博体育示例中,我们看到了如何使用Retrofit设置GET请求的示例。这个赢博体育程序将提供更广泛的请求,包括POST和DELETE请求。Retrofit为这些类型的请求提供了适当的注释。

POST请求包括向服务器发送数据对象。为了处理这个问题,我在POST请求中添加了带有@Body注释的参数:这些参数成为POST请求的主体。

这里的另一个新特性是使用路径参数,路径参数是插入到URL文本中的变量。例如,在获取特定用户的目录的GET请求中,用户由插入到people/之后的URL中的键值标识。当我们调用处理GET请求的方法时,我们必须为该键提供一个值:我们通过将键传递给带有@Path注释的参数来实现这一点。

网关类

同样,这个示例将使用视图模型类作为后端服务的网关。下面是该类的代码:

导入androidx.lifecycle.ViewModel导入androidx.lifecycle.viewModelScope导入kotlinx.coroutines.Dispatchers导入kotlinx.coroutines.launch导入kotlinx.coroutines.withContext导入retrofit2。导入retrofit2.converter.gson。GsonConverterFactory类DirectoryModel:视图模型(){私人lateinit var restInterface: DirectoryApiService val关键= mutableStateOf(" ")瓦尔人= mutableStateOf <列表> <人? > (null) init {val改造:改造= Retrofit.Builder () .addConverterFactory (GsonConverterFactory.create ()) .baseUrl(“https://cmsc106.net/directory/”).build () restInterface = retrofit.create (DirectoryApiService:: class.java)}乐趣分类列出(名称:字符串,密码:字符串,调用onSuccess:()->Unit) {val newUser = User(用户名,密码)viewModelScope.launch(Dispatchers.IO) {try {val result = restInterface.newUser(newUser) withContext(Dispatchers.Main) {key. password = User(name,password)value = result if(result. isnotempty ()) onSuccess()}} catch (e:Exception) {e.p printstacktrace ()}}} fun login(name: String,password: String,onSuccess: ()->Unit) {viewModelScope.launch(Dispatchers.IO) {try {val result = restInterface.login(name,password) withContext(Dispatchers.Main) {key。value = result if(result. isnotempty ()) getPeople(onSuccess = onSuccess)}} catch (e:Exception) {e.p printstacktrace ()}}} fun getPeople(onSuccess: ()->Unit) {val keyStr = key。value viewModelScope.launch(Dispatchers.IO) {try {val result = restInterface.getPeople(keyStr) withContext(Dispatchers.Main) {people。value = result onSuccess()}} catch (e:Exception) {e.p printstacktrace ()}}} fun addPerson(person: person,onSuccess: ()->Unit) {val keyStr = key。value viewModelScope.launch(Dispatchers.IO) {try {restInterface.postPerson(keyStr,person) val result = restInterface.getPeople(keyStr) withContext(Dispatchers.Main) {people。value = result onSuccess()}} catch (e:Exception) {e.p printstacktrace ()}}} fun removePerson(id: Int) {val keyStr = key。value viewModelScope.launch(Dispatchers.IO) {try {restInterface.removePerson(keyStr,id) val result = restInterface.getPeople(keyStr) withContext(Dispatchers.Main) {people。value = result}} catch (e:Exception) {e. printstacktrace ()}}}}

该类存储从服务器下载的Person对象列表,并为我们计划进行的每个不同的服务器交互提供适当的方法。

这个版本的赢博体育程序需要指出的一个重要方面是,我们现在处理的是一个多用户赢博体育程序。后端服务可以为大量不同的用户存储目录。用户将不得不通过登录到他们的帐户开始:响应成功的登录请求后端将向我们发送一个密钥,用户将使用在随后的请求中识别自己。网关类将该键存储在一个成员变量中,以便我们在每个后续请求时都可以访问它。

登录

由于用户需要登录才能发出请求,因此赢博体育程序需要确保用户在执行其他操作之前登录。为了支持这一需求,我在赢博体育程序中添加了第三个屏幕,提示用户登录或创建新帐户。这个屏幕现在是赢博体育程序的初始屏幕。该屏幕上的login按钮包含调用视图模型的login()方法的代码,并传递给它一个成功回调函数,该函数将我们导航到显示用户目录数据的屏幕。