Colin Gray
07/13/2021, 2:23 PMNikita Khlebushkin
07/13/2021, 11:29 PMinvalidate()
method of PagingSource, then it starts spamming load
method of PagingSource (and doesn't even wait until the job is finished, cancels it and starts anew). This is the stacktrace:
2021-07-14 02:23:46.189 15215-15254/com.xlebnick.kitties.debug D/OkHttp: --> GET <https://myapi.com/v1/images/search?page=0&limit=24&order=ASC>
2021-07-14 02:23:46.189 15215-15254/com.xlebnick.kitties.debug D/OkHttp: x-api-key: 9b7e282d-2a67-4c7b-a9fd-3f3e4056e949
2021-07-14 02:23:46.189 15215-15254/com.xlebnick.kitties.debug D/OkHttp: --> END GET
2021-07-14 02:23:46.189 15215-15254/com.xlebnick.kitties.debug D/OkHttp: <-- HTTP FAILED: java.io.IOException: Canceled
2021-07-14 02:23:46.189 15215-15215/com.xlebnick.kitties.debug W/System.err: kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutine{Cancelling}@6c3004
And this is my implementation:
class KittiesPagingSource(
private val repository: Repository,
var breedFilter: Breed? = null,
var likedKitties: List<Like> = listOf()
) : PagingSource<Int, Kitty>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Kitty> {
return try {
// Start refresh at page 1 if undefined.
val nextPage = params.key ?: 1
val filters = if (breedFilter != null) listOf(breedFilter!!) else null
var kitties: List<KittyRemoteModel> = listOf()
try {
kitties = repository.fetchKitties(nextPage, KITTIES_PAGE_SIZE, filters)
} catch (e: Throwable) {
e.printStackTrace()
}
val newKitties = kitties.map { kitty ->
kitty.asKitty(likedKitties.any { it.imageId == kitty.id })
}
LoadResult.Page(
data = newKitties,
prevKey = if (nextPage == 1) null else nextPage - 1,
nextKey = if (newKitties.isEmpty()) null else nextPage + 1
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
override fun getRefreshKey(state: PagingState<Int, Kitty>): Int? {
return state.anchorPosition?.let {
if (it < state.config.initialLoadSize) {
// if anchor position is less than initial loading count then download from the beginning
0
} else {
// otherwise load a page around anchorPosition using initialLoadSize
(it - state.config.initialLoadSize / 2).coerceAtLeast(0)
}
}
}
}
private val kittiesPagingFactory = InvalidatingPagingSourceFactory {
KittiesPagingSource(repository, breedFilter, likedKitties.value ?: listOf())
}
private val kittiesPager =
Pager(PagingConfig(pageSize = 6), pagingSourceFactory = kittiesPagingFactory)
val kitties: Flow<PagingData<Kitty>> = kittiesPager
.flow
.cachedIn(viewModelScope)
What am I doing wrong?San
07/14/2021, 12:46 AMARCHIT JAIN
07/14/2021, 5:01 AMHovhannes
07/14/2021, 6:47 AM{
"users": [
{
"id": "1",
"name": "Bill Roy",
},
{
"id": "2",
"name": "Ben Bush",
},
{
"id": "3",
"name": "Dan Fox",
},
]
}
User.kt
data class User(
@Json(name = "id")
val id: Int = 0,
@Json(name = "name")
val name: String = ""
)
data class UserResponse(
@Json(name = "users")
val userList: List<User>
)
MainViewModel.kt
class MainViewModel(
private val mainRepository: MainRepository,
private val networkHelper: NetworkHelper
) : ViewModel() {
private val _users = MutableLiveData<Resource<UserResponse>>()
val users: LiveData<Resource<UserResponse>>
get() = _users
init {
fetchUsers()
}
private fun fetchUsers() {
viewModelScope.launch {
_users.postValue(Resource.loading(null))
if (networkHelper.isNetworkConnected()) {
mainRepository.getUsers().let {
if (it.isSuccessful) {
_users.postValue(Resource.success(it.body()))
} else _users.postValue(Resource.error(it.errorBody().toString(), null))
}
} else _users.postValue(Resource.error("No internet connection", null))
}
}
}
MainAdapter.kt
class MainAdapter(
private val users: ArrayList<User>
) : RecyclerView.Adapter<MainAdapter.DataViewHolder>() {
class DataViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(user: User) {
itemView.textViewUserName.text = user.name
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
DataViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.item_layout, parent,
false
)
)
override fun getItemCount(): Int = users.size
override fun onBindViewHolder(holder: DataViewHolder, position: Int) =
holder.bind(users[position])
fun addData(list: User) {
users.addAll(listOf(list))
}
}
MainActivity.kt
private fun setupObserver() {
mainViewModel.users.observe(this, Observer {
when (it.status) {
Status.SUCCESS -> {
progressBar.visibility = View.GONE
it.data?.let { users -> renderList(users) }
recyclerView.visibility = View.VISIBLE
}........
}
private fun renderList(users: User) {
adapter.addData(users)
adapter.notifyDataSetChanged()
}
Colton Idle
07/15/2021, 12:17 AMhttps://youtu.be/09qjn706ITA▾
Rony Krell
07/15/2021, 1:37 AMnull
after the WebView is detached from the parent container. Destroying it later on results in a NPE
. Posted about it here. Have really no idea what it could be after perusing the release notes. Would appreciate any helpLawrence Shen
07/15/2021, 7:13 AMNoushad Chullian
07/15/2021, 7:27 AMSaidiali
07/15/2021, 10:00 AM<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="<http://schemas.android.com/apk/res/android>"
xmlns:tools="<http://schemas.android.com/tools>"
package="com.devhub.meet">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
android:theme="@style/Theme.Meet">
<activity
android:name=".CallActivity"
android:exported="false" />
<activity android:name=".MainActivity"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>Anamika Trivedi
07/15/2021, 1:59 PMAnton Afanasev
07/15/2021, 4:15 PMopen
from IO Dispatcher.
My code:
withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
context.assets.open(FILE_NAME).run {
this.readBytes().also { file = it }
this.close()
}
}
Any ideas, folks?lawlorslaw
07/15/2021, 6:00 PMHovhannes
07/16/2021, 8:52 AMfun reqBody(): RequestBody {
val clientIdentifier = binding.edittxtClientIdentifier.text.toString().trim()
val device = binding.edittxtDeviceType.text.toString().trim()
val lang = binding.edittxtLang.text.toString().trim()
val partn = binding.edittxtPartnerId.text.toString().trim()
val pass = binding.editTextTextPassword.text.toString().trim()
viewModel.login(
clientIdentifier,
Integer.parseInt(device),
lang,
Integer.parseInt(partn),
pass
)
val rootObject = JSONObject()
rootObject.put("ClientIdentifier", clientIdentifier)
rootObject.put("DeviceType", device)
rootObject.put("LanguageId", lang)
rootObject.put("PartnerId", partn)
rootObject.put("Password", pass)
encrypt(rootObject.toString(), publicKey)
val encrypted = encrypt(rootObject.toString(), publicKey)
return RequestBody.create(
"application/json; charset=utf-8".toMediaTypeOrNull(),
"{\r\n \"Data\":\"$encrypted\"\r\n}"
)
}
In another class
private fun <Request> getRetrofitClient(authenticator: Authenticator? = null): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor { chain ->
chain.proceed(chain.request().newBuilder().also {
it.addHeader("Accept", "application/json")
it.method("POST", LoginFragment().reqBody()) //Error shows on this
}.build())
}.also { client ->
authenticator?.let { client.authenticator(it) }
if (BuildConfig.DEBUG) {
val logging = HttpLoggingInterceptor()
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
client.addInterceptor(logging)
}
}.build()
}
brandonmcansh
07/16/2021, 2:44 PMalthaf
07/17/2021, 1:26 PMval diCoroutineModule = module {
single { <http://Dispatchers.IO|Dispatchers.IO> }
single { Dispatchers.Main }
single { Dispatchers.Default }
}
Something similar ins HILT
@InstallIn(SingletonComponent::class)
@Module
object CoroutinesModule {
@DefaultDispatcher
@Provides
fun providesDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default
@IoDispatcher
@Provides
fun providesIoDispatcher(): CoroutineDispatcher = <http://Dispatchers.IO|Dispatchers.IO>
@MainDispatcher
@Provides
fun providesMainDispatcher(): CoroutineDispatcher = Dispatchers.Main
@MainImmediateDispatcher
@Provides
fun providesMainImmediateDispatcher(): CoroutineDispatcher = Dispatchers.Main.immediate
}
Jason Inbody
07/17/2021, 9:51 PMKlaxon().parse<Map<String,Any>>(jsonString)!!
or
var map: Map<String, Any> = HashMap()
map = Gson().fromJson(jsonString, map.javaClass)
Waqas Tahir
07/18/2021, 2:08 PMalorma
07/19/2021, 11:44 AMHovhannes
07/19/2021, 12:35 PMclass LoginFragment : Fragment() {
private lateinit var binding: FragmentLoginBinding
private val viewModel: AuthViewModel by viewModels()
/* private val viewModel:AuthViewModel by lazy{
ViewModelProvider(this).get(AuthViewModel::class.java)
}*/
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentLoginBinding.inflate(inflater, container, false)
........
}
fun reqBody(): String {
Log.d("LogFrag", "reqBody's binding$binding")
val clientIdentifier = binding.edittxtClientIdentifier.text.toString().trim()
val pass = binding.editTextTextPassword.text.toString().trim()
viewModel.login(
clientIdentifier,
pass
)
val rootObject = JSONObject()
rootObject.put("ClientIdentifier", clientIdentifier)
rootObject.put("Password", pass)
encrypt(rootObject.toString(), publicKey)
val encrypted = encrypt(rootObject.toString(), publicKey)
return encrypted
}
fun <Request> getRetrofitClient(authenticator: Authenticator? = null): OkHttpClient {
val finalEncr=reqBody() //Error shows on this
val body = "{\r\n \"Data\":\"$finalEncr\"\r\n}"
.toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
return OkHttpClient.Builder()
.addInterceptor { chain ->
chain.proceed(chain.request().newBuilder().also {
it.addHeader("Accept", "application/json")
it.method("POST", body)
}.build())
}.also { client ->
authenticator?.let { client.authenticator(it) }
if (BuildConfig.DEBUG) {
val logging = HttpLoggingInterceptor()
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
client.addInterceptor(logging)
}
}.build()
}
atanasi charle
07/19/2021, 3:45 PMKetan Khunti
07/20/2021, 3:57 AMCiprian Grigor
07/20/2021, 11:22 AMval repoResult: Flow<PagingData<Repo>> = queryFlow.filterNotNull()
.flatMapLatest { query ->
repository.getSearchResultStream(query)
}
//.cachedIn(viewModelScope) //crash without
.combine(filterFlow) { page, filter ->
page.filter {
it.description?.contains(filter, true) ?: false
}
}
lifecycleScope.launch {
viewModel.repoResult.collectLatest {
adapter.submitData(it)
}
}
crash is java.lang.IllegalStateException: Attempt to collect twice from pageEventFlow, which is an illegal operation. Did you forget to call Flow<PagingData<*>>.cachedIn(coroutineScope)?
Hovhannes
07/20/2021, 2:11 PMlass LoginFragment : Fragment() {
private lateinit var binding: FragmentLoginBinding
private val viewModel: AuthViewModel by viewModels()
/* private val viewModel:AuthViewModel by lazy{
ViewModelProvider(this).get(AuthViewModel::class.java)
}*/
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentLoginBinding.inflate(inflater, container, false)
........
return binding.root
}
fun reqBody(): String {
Log.d("LogFrag", "reqBody's binding$binding")
val clientIdentifier = binding.edittxtClientIdentifier.text.toString().trim()
val pass = binding.editTextTextPassword.text.toString().trim()
viewModel.login(
clientIdentifier,
pass
)
val rootObject = JSONObject()
rootObject.put("ClientIdentifier", clientIdentifier)
rootObject.put("Password", pass)
encrypt(rootObject.toString(), publicKey)
val encrypted = encrypt(rootObject.toString(), publicKey)
return encrypted
}
fun <Request> getRetrofitClient(authenticator: Authenticator? = null): OkHttpClient {
val finalEncr=reqBody() //Error shows on this
val body = "{\r\n \"Data\":\"$finalEncr\"\r\n}"
.toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
return OkHttpClient.Builder()
.addInterceptor { chain ->
chain.proceed(chain.request().newBuilder().also {
it.addHeader("Accept", "application/json")
it.method("POST", body)
}.build())
}.also { client ->
authenticator?.let { client.authenticator(it) }
if (BuildConfig.DEBUG) {
val logging = HttpLoggingInterceptor()
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
client.addInterceptor(logging)
}
}.build()
}
class RemoteDataSource {
@RequiresApi(Build.VERSION_CODES.O)
fun <Api> buildApi(
api: Class<Api>,
context: Context
): Api {
val authenticator = TokenAuthenticator(context, buildTokenApi())
Log.d("RemoteDataSource", "buildApi")
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(LoginFragment().getRetrofitClient<Any>(authenticator))
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(api)
}
@RequiresApi(Build.VERSION_CODES.O)
fun buildTokenApi(): TokenRefreshApi {
Log.d("RemoteDataSource", "buildTokenApi")
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(LoginFragment().getRetrofitClient<Any>())
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(TokenRefreshApi::class.java)
}
}
Jon
07/20/2021, 7:24 PMfun getLiveData(): LiveData<Result?> { return mResult }
where mResult is just a simple POJO. I am confused a bit about how kotlin interprets the return type for this function. I would imagine this to be non-null but when I try to call it, it forces me to use the null-safe operator "?.". If I wanted this to be nullable, I would imagine the return type would be
LiveData<Result?>?
with the appended "?"Hovhannes
07/21/2021, 12:56 PMinterface AuthApi : BaseApi {
@FormUrlEncoded
@PUT("{PartnerId}/api/Main/LoginClient")
suspend fun login(
@Field("email") email: String,
@Path("PartnerId") partnerId:String,
): LoginResponse
}
interface UserApi : BaseApi{
@GET("{PartnerId}/api/Main/LoginClient")
suspend fun getUser(@Path("PartnerId")partnerId:Int): LoginResponse
}
class UserRepository @Inject constructor(
private val api: UserApi
) : BaseRepository(api) {
suspend fun getUser() = safeApiCall { api.getUser(0) } <---getUser()'s argument must be EditText's value
}
rajesh
07/22/2021, 6:07 AMAndré Thiele
07/22/2021, 11:43 AMPawan Gorai
07/22/2021, 5:14 PMLibor Bicanovsky
07/22/2021, 9:20 PMLibor Bicanovsky
07/22/2021, 9:20 PMChris Fillmore
07/23/2021, 6:06 PM