当前位置: 首页 > wzjs >正文

长沙正规制作网站公司seo优化操作

长沙正规制作网站公司,seo优化操作,公众号开发者设置,网站转出文章目录 前言:1、添加依赖1.1 在settings.gradle.kts中添加1.2 在应用级的build.gradle.kts添加插件依赖1.3 在module级的build.gradle.kts添加依赖 2、实体类2.1 request2.2 reponse 3、网络请求3.1 ApiService3.2 NetworkModule3.3 拦截器 添加token3.4 Hilt 的 …

文章目录

  • 前言:
  • 1、添加依赖
    • 1.1 在settings.gradle.kts中添加
    • 1.2 在应用级的build.gradle.kts添加插件依赖
    • 1.3 在module级的build.gradle.kts添加依赖
  • 2、实体类
    • 2.1 request
    • 2.2 reponse
  • 3、网络请求
    • 3.1 ApiService
    • 3.2 NetworkModule
    • 3.3 拦截器 添加token
    • 3.4 Hilt 的 使用
  • 4、数据类
    • 4.1 服务器数据
      • 4.1.1 LoginModule
      • 4.1.2 Repository
    • 4.2 本地数据
      • 4.2.1 StorageModule
      • 4.2.2
  • 5、ViewModel访问接口
  • 6、compose UI调用
    • 6.1 CustomApplication
    • 6.2 MainActivity
  • 7、问题
    • 7.1 问题1 网络错误
      • 7.1.1 步骤 1:创建网络安全配置文件
      • 7.1.2 步骤2:

前言:

新开了一个项目之后,发现MVP框架的实现代码有点多了,就想说用MVVM框架进行实现,加上发现Hilt注解相对能够更好地解耦,学习了一下之后就想说需要应用到实际引用中,就写了个简单功能实现,虽然一个登录功能看着写的代码结构多了点,但是到后期功能不断增加之后就会发现,结构比较清晰,基本机构见图所示,使用MVVM框架实现登录效果,包括retrofit+ViewModel+Hilt注解+Compose的实现。
在这里插入图片描述

1、添加依赖

添加依赖需要在三个部分中进行添加

1.1 在settings.gradle.kts中添加

pluginManagement {repositories {google {content {includeGroupByRegex("com\\.android.*")includeGroupByRegex("com\\.google.*")includeGroupByRegex("androidx.*")}}google()mavenCentral()gradlePluginPortal()maven(url = uri("https://jitpack.io"))}
}
dependencyResolutionManagement {repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)repositories {google()mavenCentral()maven(url = uri("https://jitpack.io"))}
}

上述调用中,如果无法下载或者下载失败,可以使用了阿里云镜像库,原链接可写可不写,如下所示

    maven(url = uri("https://maven.aliyun.com/repository/google"))maven( url  = uri("https://maven.aliyun.com/repository/public"))maven(url = uri("https://maven.aliyun.com/nexus/content/repositories/central"))maven(url = uri("https://maven.aliyun.com/repository/gradle-plugin"))

1.2 在应用级的build.gradle.kts添加插件依赖

plugins {...id("com.google.devtools.ksp") version "2.1.10-1.0.29"id("com.google.dagger.hilt.android") version "2.56.2" apply false}

需要注意的是,KSP 版本的前一部分必须与 build 中使用的 Kotlin 版本一致,上述版本中知道kotlin的版本为2.1.10,从kapt迁移到ksp官方

1.3 在module级的build.gradle.kts添加依赖

重点写的是添加网络相关、Hilt和EncryptedSharedPreferences的依赖

plugins {
...
id("com.google.devtools.ksp") version "2.1.10-1.0.29"
id("com.google.dagger.hilt.android")
}
android{
...
buildFeatures {compose = true}...
}dependencies {
...//网络相关依赖implementation("com.google.code.gson:gson:2.10.1")implementation("com.squareup.retrofit2:retrofit:2.9.0")implementation("com.squareup.retrofit2:converter-gson:2.9.0")implementation("com.squareup.okhttp3:okhttp:4.12.0")implementation("com.squareup.okhttp3:logging-interceptor:4.10.0")//navigation页面跳转implementation("androidx.navigation:navigation-compose:2.9.0")//hilt注解implementation("com.google.dagger:hilt-android:2.56.2")ksp("com.google.dagger:hilt-android-compiler:2.56.2")implementation("androidx.hilt:hilt-navigation-compose:1.2.0")//EncryptedSharedPreferences本地持久化保存implementation("androidx.security:security-crypto:1.1.0-alpha06")
}

对于compose的依赖的导入一般新建项目的时候Build configuration language选择kotlin项目可以自动导入,
如果想要了解具体如何添加,可以参考 添加compose的依赖

2、实体类

2.1 request

请求接口数据的数实体类

data class LoginRequest(val username:String,val password:String)

2.2 reponse

接口响应的实体类

open class BaseResponse @JvmOverloads constructor(var code: Int = -1,var msg: String? = ""
)
data class LoginResponse(val token:String):BaseResponse()

数据类的形式主要看服务器的调用

3、网络请求

3.1 ApiService

import retrofit2.Responseimport retrofit2.http.Body
import retrofit2.http.POSTinterface  ApiService {@POST("/openLogin")suspend fun userLogin(@Body request:LoginRequest): Response<LoginResponse>}

3.2 NetworkModule

import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {private const val BASE_URL = "http://47.122.63.169:8070"@Provides@Singletonfun provideAuthInterceptor(tokenManager: LoginStorage): AuthInterceptor {return AuthInterceptor(tokenManager)}@Provides@Singletonfun provideOkHttpClient(authInterceptor: AuthInterceptor): OkHttpClient {return OkHttpClient.Builder().addInterceptor(authInterceptor) //添加拦截器.addInterceptor(HttpLoggingInterceptor().apply {level = HttpLoggingInterceptor.Level.BODY}).build()}@Provides@Singletonfun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {return Retrofit.Builder().baseUrl(BASE_URL).client(okHttpClient).addConverterFactory(GsonConverterFactory.create()).build()}@Provides@Singletonfun provideApiService(retrofit: Retrofit): ApiService {return retrofit.create(ApiService::class.java)}
}

3.3 拦截器 添加token

import okhttp3.Interceptor
import okhttp3.Response
import java.io.IOException
import javax.inject.Inject// AuthInterceptor.kt
class AuthInterceptor @Inject constructor(private val tokenManager: LoginStorage
) : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val request = chain.request()// 检查是否需要认证val requiresAuth = request.header("No-Auth") == nullif (requiresAuth) {val token = tokenManager.getToken() ?: throw AuthException("未登录")// 添加认证头val newRequest = request.newBuilder().addHeader("Authorization", "Bearer $token").build()return chain.proceed(newRequest)}return chain.proceed(request)}
}class AuthException(message: String) : IOException(message)

ps:如果token失效了,需要及时更新

3.4 Hilt 的 使用

@Singleton 是进程级别的
最大范围:​​ 在整个应用进程中唯一存在
​依赖管理:​​ 确保每次注入都是同一实例
Hilt注解的官方解释

使用hilt注解,需要注意的是,引用的包为import javax.inject.Singleton,而不是jakarta.inject.Singleton,不然会出现报错,​

scoped with @Singleton may not reference bindings with different scopes:public abstract static class SingletonC implements CustomApplication_GeneratedInjector

4、数据类

4.1 服务器数据

4.1.1 LoginModule

@Module
@InstallIn(SingletonComponent::class)
object LoginModule {@Provides@Singletonfun provideLoginRepository(authService: ApiService,authStorage: LoginStorage): LoginRepository {return LoginUserRepositoryImp(authService, authStorage)}}

4.1.2 Repository

访问登录接口

interface LoginRepository {suspend fun login(bean: UserBean): UiState<out LoginResponse>suspend fun isLoggedIn(): Booleansuspend fun logout()
}
import javax.inject.Injectclass LoginUserRepositoryImp @Inject constructor(private val apiService: ApiService,private val loginStorage: LoginStorage):LoginRepository {override suspend fun login(bean: UserBean): UiState<out LoginResponse> {return   try {val response = apiService.userLogin(LoginRequest(username = bean.userName,password = bean.password))Log.d("lucky", "login: code ${response.code()} \nbody ${response.body()} \n message${response.message()}")if (response.isSuccessful) {response.body()?.let {loginStorage.saveToken(it.token)UiState.Success(it)} ?: UiState.Error("Empty response body")} else {UiState.Error("Login failed: ${response.code()}")}} catch (e: Exception) {UiState.Error("Network error: ${e.message}")}}override suspend fun isLoggedIn(): Boolean {return loginStorage.getToken() != null}override suspend fun logout() {loginStorage.clearToken()}
}

4.2 本地数据

4.2.1 StorageModule

import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton@Module
@InstallIn(SingletonComponent::class)
object StorageModule {@Singleton@Providesfun provideLoginStorage( @ApplicationContext context: Context): LoginStorage {return SecureLoginStorageImp(context)}
}

4.2.2

保存token,可根据其获取登录状态,使用token进行实现

interface LoginStorage {suspend fun saveToken(token: String)suspend fun getToken(): String?suspend fun clearToken()
}
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Injectclass SecureLoginStorageImp @Inject constructor(@ApplicationContext context:Context) : LoginStorage{private val encryptedPreferences by lazy {val masterKey = MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build()EncryptedSharedPreferences.create(context,Constants.LOGIN_USER_PREFERENCE,masterKey,EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM)}private  val tokenKey = Constants.LOGIN_USER_TOKEN// companion object {}override suspend fun saveToken(token: String) {encryptedPreferences.edit {putString(tokenKey,token)}}override suspend fun getToken(): String? {return encryptedPreferences.getString(tokenKey,"")}override suspend fun clearToken() {encryptedPreferences.edit {remove(tokenKey)}}
}

5、ViewModel访问接口

import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.launch@HiltViewModel
class LoginViewModel @Inject constructor(private val loginRepository: LoginRepository
) : ViewModel() {private val _loginState = mutableStateOf<UiState<out LoginResponse>>(UiState.Idle)val loginState: MutableState<UiState<out LoginResponse>> get() = _loginStatevar userName by mutableStateOf("")private setvar password by mutableStateOf("")private setfun updateUsername(input: String) {userName = input}fun updatePassword(input: String) {password = input}fun login() {viewModelScope.launch {_loginState.value = UiState.Loading_loginState.value = loginRepository.login(UserBean(userName = userName,password = password))}}fun resetState() {_loginState.value = UiState.Idle}}

6、compose UI调用

6.1 CustomApplication

@HiltAndroidApp
class CustomApplication : Application() {override fun onCreate() {super.onCreate()}}

6.2 MainActivity

@AndroidEntryPoint
class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)enableEdgeToEdge()setContent {ChainOfCustodyTheme {Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->Column(modifier = Modifier.padding(innerPadding)) {Login()}}}}}}
@Composable
fun Login( viewModel: LoginViewModel = hiltViewModel()){// 登录成功处理val loginState = viewModel.loginState.valueval context = LocalContext.currentLaunchedEffect(loginState) {when (loginState) {is UiState.Success -> {viewModel.resetState()Handler(Looper.getMainLooper()).post {Toast.makeText(context,"登录成功111221212",Toast.LENGTH_LONG).show()}}is UiState.Error -> {// 显示错误提示Toast.makeText(context,loginState.message,Toast.LENGTH_LONG).show()}else -> {}}}Text(text = "登录",modifier = Modifier.clickable {viewModel.updatePassword("xxx123")viewModel.updateUsername("xxx")viewModel.login()})
}

7、问题

在Android9+的版本中,服务器的域名为http会出现的问题

7.1 问题1 网络错误

java.lang.Exception: Toast callstack! strTip=Network error: CLEARTEXT communication to 47.122.63.169 not permitted by network security policy

7.1.1 步骤 1:创建网络安全配置文件

在 res/xml 目录创建 network_security_config.xml
添加以下内容:

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

7.1.2 步骤2:

在AndroidManifest中:

  <uses-permission android:name="android.permission.INTERNET" /><application...android:networkSecurityConfig="@xml/network_security_config"
...>
</application>
http://www.dtcms.com/wzjs/31548.html

相关文章:

  • 平顶山做网站哪家好电脑培训班多少费用
  • 那个网站可以找人做设计师百度竞价排名背后的伦理问题
  • 做网站后台用什么语言好搜什么关键词能找到网站
  • 文旅网站界面设计西点培训
  • 百度商桥的代码放到网站里如何制作简单的网页链接
  • 男技师做spa的视频网站倒油汕头seo优化公司
  • 网站免费网站的方法无货源网店怎么开
  • 网站做盗版视频赚钱吗优化排名工具
  • 平台网址怎么查询厦门seo搜索引擎优化
  • 国外网站为什么不用备案百度竞价点击工具
  • 制作网页心得体会seo收录排名
  • 网络空间 网站 域名茂名seo快速排名外包
  • 池州城乡住房建设厅网站网站优化建议
  • 建立网站模板百度企业官网
  • 开发购物网站描述百度热点排行榜
  • 英文版网站制作社群营销的十大步骤
  • 加强网站信息怎么做怎么看app的下载网址
  • 长沙3合1网站建设价格谷歌搜索网页版入口
  • 西安演出公司网站建设苏州seo门户网
  • 湖南响应式网站建设哪家有网站服务器
  • 网站后台传图片传不了十大接单推广app平台
  • 手机网站在后台怎么做编辑网页开发需要学什么
  • 做信息网站怎么样怎么在网上做推广
  • 万网网站备案电商seo优化
  • 网站建设要学无锡网站优化公司
  • 宁波 手机网站建设网站的推广方式
  • 只做美食类目产品的网站百度贴吧怎么发广告
  • 使用编辑字母做免费网站竞价推广渠道
  • 无障碍环境建设 网站视频seo优化教程
  • 网站开发公司销售总监岗位要求黑帽seo论坛