使用 ViewModel 和 LiveData 多次改造执行 API
Posted
技术标签:
【中文标题】使用 ViewModel 和 LiveData 多次改造执行 API【英文标题】:Retrofit execute API multiple times using ViewModel and LiveData 【发布时间】:2021-02-23 15:25:13 【问题描述】:所以当我执行操作单击以请求 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时,endpoint也被执行了多次。
我认为观察者不断更新数据,但我不知道这怎么可能
另一个类使其更清晰
intent启动时多次调用的类
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)
【问题讨论】:
【参考方案1】:我解决了这个问题,所以我将 oncreateView 中的所有代码移动到 onviewcreated 并将 viewModelRegisterMerchant.getDataStatusMerchant(auth!!.token!!) 移动到单击的项目之外。
【讨论】:
以上是关于使用 ViewModel 和 LiveData 多次改造执行 API的主要内容,如果未能解决你的问题,请参考以下文章
JetpackLiveData 架构组件 ( LiveData 简介 | LiveData 使用方法 | ViewModel + LiveData 示例 )
JetpackLiveData 架构组件 ( LiveData 简介 | LiveData 使用方法 | ViewModel + LiveData 示例 )