android - 使用 ViewModel 和 LiveData 多次改造执行 API
问题描述
因此,当我执行操作单击以请求 API GET 时遇到问题,我使用 MVVM 和 LiveData 组合从 API 获取值多次命中端点。下面的代码
ApiService.kt
class ApiService {
private var retrofit : Retrofit? = null
private val okHttpBuilder = OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
.connectTimeout(120, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS)
.writeTimeout(120, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
.build()
fun <S> createService(serviceClass: Class<S>?): S {
if(retrofit == null){
retrofit = Retrofit.Builder().addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.client(okHttpBuilder)
.build()
}
return retrofit!!.create(serviceClass!!)
}
val serviceGuestMerchants : GuestMerchantsService by lazy{
createService(GuestMerchantsService::class.java)
}
}
界面
GuestMerchantService.kt
interface GuestMerchantsService {
@GET(ConstantGuestMerchants.BASE_URL_GUEST_MERCHANT)
suspend fun getListMerchant(@Query("page") page :Int?, @Query("order-direction") orderDirection :
String) : ResponseListMerchant
@GET(ConstantGuestMerchants.BASE_URL_GUEST_MERCHANT_DETAIL)
suspend fun getPreviewMerchant(@Path("id") id:Int) : ResponsePreviewMerchant
@GET(ConstantGuestMerchants.BASE_URL_GUEST_MERCHANT_PRODUCT_LIST)
suspend fun getListProductByMerchant(
@Path("id") id : Int,
@Query("page") page : Int?) : ResponseProductByMerchant
@GET(ConstantGuestMerchants.BASE_URL_GUEST_MERCHANT_STATUS)
suspend fun getStatusMerchant(@Header("Authorization") authorization : String) :
ResponseGetStatusMerchant
@Multipart
@POST(ConstantGuestMerchants.BASE_URL_GUEST_MERCHANT_REQUEST_MERCHANT)
fun registerMerchant(@Header("Authorization") authorization: String,
@Part fotoKtp : MultipartBody.Part, @Part fotoPemilik : MultipartBody.Part,
@Part fotoToko : MultipartBody.Part,
@Part namaToko : MultipartBody.Part,@Part noHp : MultipartBody.Part ,
@Part alamat : MultipartBody.Part, @Part noKtp : MultipartBody.Part) : Call<ResponseRegisterMerchant>
@DELETE(ConstantGuestMerchants.BASE_URL_GUEST_MERCHANT)
fun deleteRequestMerchant(@Header("Authorization") authorization : String) :
Call<ResponseDeleteReqMerchant>
}
视图模型
ProfileViewModel.kt
class ProfileViewModel : ViewModel(){
private val _profile = MutableLiveData<Profile>()
val profile : LiveData<Profile>
get() = _profile
private val _status = MutableLiveData<ApiStatus>()
val status : LiveData<ApiStatus>
get() = _status
private suspend fun getProfileUser(token : String){
try {
_status.postValue(ApiStatus.LOADING)
val apiService = ApiService().serviceProfileUser
_profile.postValue(apiService.getProfile(token).data)
_status.postValue(ApiStatus.SUCCESS)
}catch (e : Exception){
Log.d("REQ_PROF_USR_FAIL", e.localizedMessage!!)
_status.postValue(ApiStatus.FAILED)
}
}
fun getDataProfileUser(token : String){
viewModelScope.launch {
getProfileUser(token)
}
}
}
执行操作以从 ViewModel 调用函数的片段
CustomerProfileFragment.kt
class CustomerProfileFragment : Fragment() {
private lateinit var adapter: AdapterUtil<ProfileMenuItem>
private lateinit var binding: FragmentCustomerProfileBinding
private lateinit var cacheUtil: CacheUtil
private var auth : Login? = null
private val viewModel : ProfileViewModel by lazy {
ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(ProfileViewModel::class.java)
}
private val viewModelRegisterMerchant: RegisterMerchantViewModel by lazy {
ViewModelProvider(
this,
ViewModelProvider.NewInstanceFactory()
).get(RegisterMerchantViewModel::class.java)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
//binding init
binding = FragmentCustomerProfileBinding.inflate(inflater, container, false)
// Setup Cache
cacheUtil = CacheUtil()
cacheUtil.start(context as Activity, ConstantAuth.PREFERENCES)
// Setup Title
(activity as AppCompatActivity?)!!.setSupportActionBar(binding.toolbar)
binding.toolbar.title = getString(R.string.akun_saya)
//After Login
if (getAuth(cacheUtil).token!!.isNotEmpty()) {
auth = getAuth(cacheUtil)
Log.d("TOKEN NYA TOKEN", "${auth!!.token} TOKEN NYA TOKEN PROFILE")
binding.llLogin.visibility=View.GONE
setupProfileMenu()
binding.tvRegistrasi.setOnClickListener {
startActivity(Intent(context, RegisterUserActivity::class.java))
}
//login
binding.tvLogin.setOnClickListener {
startActivity(Intent(context, LoginActivity::class.java))
}
//Logout
binding.tvLogout.setOnClickListener {
this.cacheUtil.clear()
startActivity(Intent(requireContext(), MainActivity::class.java))
requireActivity().finish()
}
binding.imFotoProfil.setOnClickListener {
ImagePicker.create(this)
.single()
.start()
}
//edit photo profile
}else{
binding.tvNamaAkun.text = ""
binding.tvEmailAkun.text = ""
Glide.with(requireContext()).load(R.drawable.ic_baseline_account_circle_24).into(binding.imFotoProfil)
}
return binding.root
}
private fun setupProfileMenu(){
//Setup Profil Menu
binding.rvIconmenu.layoutManager = LinearLayoutManager(context)
adapter =
AdapterUtil(R.layout.item_list_menu_akun,
listOf(
ProfileMenuItem(
"Saldo Saya",
R.drawable.ic_monetization
),
ProfileMenuItem(
"Pusat Bantuan",
R.drawable.ic_help_outline
),
ProfileMenuItem(
"Chat dengan Leh-Oleh",
R.drawable.ic_chat
),
ProfileMenuItem(
"Beri Kami Nilai",
R.drawable.ic_star_border
),
ProfileMenuItem(
"Toko Saya",
R.drawable.ic_store
)
), { position, itemView, item ->
itemView.tv_menu!!.text = item.label
itemView.im_akun_icon!!.setImageResource(item.icon)
itemView.im_chevron_right.setImageResource(R.drawable.ic_chevron_right)
}, { position, item ->
when (position) {
0 -> startActivity(
Intent(
context,
CustomerSaldoSayaActivity::class.java
)
)
1 -> startActivity(
Intent(
context,
BantuanActivity::class.java
)
)
4 -> {
viewModelRegisterMerchant.getDataStatusMerchant(auth!!.token!!)
viewModelRegisterMerchant.statusMerchant.observe(
viewLifecycleOwner,
Observer {
if (it.isVisible!!.isNotEmpty()) {
if (it.isVisible == "1") {
//findNavController().navigate(R.id.action_navigation_register_toko_to_navigation_toko_saya)
startActivity(Intent(requireContext(), MerchantTokoSayaActivity::class.java))
}
} else {
Log.d("DATA_STATUS", "BELUM LOGIN")
}
})
}
}
// 2 -> startActivity(Intent(context, ProductListActivity::class.java))
// 3 -> startActivity(Intent(context, ProductListActivity::class.java))
})
binding.rvIconmenu.adapter = adapter
}
override fun onResume() {
viewModel.getDataProfileUser(auth!!.token!!)
super.onResume()
}
}
请注意,当 viewModelRegisterMerchant 被调用并观察时,intent 正在执行多次,当我看到 Logcat 时,端点也被执行了多次。
我认为观察者不断更新数据,但我不知道这怎么可能
另一个类使它更清楚
启动意图时多次调用的类
MerchantTokoSaya.kt
class MerchantTokoSayaActivity : AppCompatActivity() {
private lateinit var binding : ActivityMerchantTokoSayaBinding
private lateinit var auth: Login
private val viewModel : ProfileTokoViewModel by lazy {
ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(ProfileTokoViewModel::class.java)
}
private lateinit var cacheUtil: CacheUtil
private lateinit var adapter: AdapterUtil<ProfileMenuItem>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMerchantTokoSayaBinding.inflate(layoutInflater)
setContentView(binding.root)
cacheUtil = CacheUtil()
cacheUtil.start(this, ConstantAuth.PREFERENCES)
if (getAuth(cacheUtil).token!!.isNotEmpty()) {
auth = getAuth(cacheUtil)
Log.d("TOKEN NYA TOKEN", "${auth.token} TOKEN NYA TOKEN")
getDataProfileToko()
binding.tvUbahAkun.setOnClickListener {
val intent = Intent(this, RegisterMerchantFragment::class.java)
intent.putExtra(ConstantProfileMerchant.DATA_EDIT_PROFILE_TOKO, ConstantProfileMerchant.ACTION_EDIT_PROFILE_TOKO)
startActivity(intent)
finish()
}
title = "Toko Saya"
//Setup Profil Menu
binding.rvIconmenu.layoutManager = LinearLayoutManager(this)
adapter =
AdapterUtil(R.layout.item_list_menu_akun,
listOf(
ProfileMenuItem(
"Kelola Barang",
R.drawable.ic_card_giftcard
),
ProfileMenuItem(
"Kelola Pesanan",
R.drawable.ic_assignment
),
ProfileMenuItem(
"Pesan Masuk",
R.drawable.ic_chat
),
ProfileMenuItem(
"Keuangan",
R.drawable.ic_monetization
)
), { position, itemView, item ->
itemView.tv_menu!!.text = item.label
itemView.im_akun_icon!!.setImageResource(item.icon)
itemView.im_chevron_right.setImageResource(R.drawable.ic_chevron_right)
}, { position, item ->
when (position) {
0 -> startActivity(
Intent(
this,
MerchantKelolaBarangActivity::class.java
)
)
1 -> startActivity(
Intent(
this,
KelolaPesananActivity::class.java
)
)
// 2 -> startActivity(Intent(context, ProductListActivity::class.java))
// 3 -> startActivity(Intent(context, ProductListActivity::class.java))
}
})
binding.rvIconmenu.adapter = adapter
//init profil picture
binding.imFotoProfil.setImageResource(R.drawable.ic_home_black_24dp)
}else {
startActivity(Intent(this, LoginActivity::class.java))
}
}
private fun getDataProfileToko(){
viewModel.getDataProfileToko(auth.token!!)
viewModel.toko.observe(this, Observer {
binding.tvNamaAkun.text = it.marketName
Glide.with(this).load(it.authorUri).circleCrop().into(binding.imFotoProfil)
Glide.with(this).load(it.marketUri).into(binding.imageViewHeader)
})
}
}
用于保存共享首选项的 util 类
CacheUtil.kt
class CacheUtil {
private var sharePref: SharedPreferences? = null
fun start(activity: Activity, PREFS: String) {
sharePref = activity.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
}
fun destroy() { this.sharePref = null }
fun <T> set(PREFS: String, value: T) {
this.sharePref?.let {
with(it.edit()) {
putString(PREFS, Gson().toJson(value))
Log.d("CACHE UTIL", PREFS)
apply()
}
}
}
fun clear() {
this.sharePref?.edit()?.clear()?.apply()
}
fun get(PREFS: String): String? {
if (sharePref != null) return sharePref!!.getString(PREFS, null)
return null
}
}
RegisterMerchantViewModel.kt
class RegisterMerchantViewModel : ViewModel(){
private val _statusMerchant = MutableLiveData<DataGetStatusMerchant>()
val statusMerchant : LiveData<DataGetStatusMerchant>
get() = _statusMerchant
private val _status = MutableLiveData<ApiStatus>()
val status : LiveData<ApiStatus>
get() = _status
private val apiService = ApiService().serviceGuestMerchants
fun getDataStatusMerchant(token: String) {
viewModelScope.launch {
getStatusMerchant(token)
}
}
private suspend fun getStatusMerchant(token: String) {
try {
_status.postValue(ApiStatus.LOADING)
_statusMerchant.postValue(apiService.getStatusMerchant(token).data)
_status.postValue(ApiStatus.SUCCESS)
} catch (e: Exception) {
Log.d("ERROR_REQ_STATUS", e.localizedMessage!!)
_status.postValue(ApiStatus.FAILED)
}
}
}
解决方案
我解决了这个问题,所以我将 oncreateView 中的所有代码移动到 onviewcreated 并将 viewModelRegisterMerchant.getDataStatusMerchant(auth!!.token!!) 移动到单击的项目之外。
推荐阅读
- php - 如何使用带有访问令牌的 php 将文件上传到谷歌驱动器
- html - 当我为移动设备调整页脚大小时,页脚和页面底部之间有一个空白区域
- database - 我的信息没有发送到我的数据库
- html - Safari 中的三角形按钮颜色问题(纯 CSS)
- javascript - 在类组件中使用 React 钩子
- ruby-on-rails - 由 config.action_cable.url 定义的 URL 在被 Vue 前端使用时生成 404
- vim - 执行“参数”\r(回车)不起作用
- jquery - jQuery 检查 CSS 类的高度是否小于 20%
- graphql - 如何从后端消息队列应用程序触发 GraphQL 订阅
- java - 使用 jGit 将文件推送到远程存储库