Can I notify a BehaviorProcessor from inside a RxJava stream? - multithreading

I would like to get your feedback on the code below.
I'm wondering if it's safe to call currentSession.onNext(result.session)
from inside the SessionManager.signIn stream.
My first intuition is to say NO because of multithreading and synchronization issues, meaning, based on this code, I could be calling currentSession.onNext(result.session) from different threads.
Here is the code, please let me know what you think! Thanks
SessionManager which is a singleton
#Singleton
class SessionManager #Inject constructor(
private val sessionService: SessionService,
){
val currentSession = BehaviorProcessor.create<Session>()
fun signIn(login: String, password: String): Single<Boolean> =
sessionService.signIn(login, password)
.doOnNext(result ->
if (session is Success) {
currentSession.onNext(result.session)
}
).map { result ->
when (result) {
is Success -> true
else -> false
}
}
.subscribeOn(Schedulers.io())
}
HomeView which is a random View subscribing to the SessionManager's signIn stream
class HomeView(val context: Context) : View(context) {
#Inject
lateinit var sessionManager: SessionManager
private val disposables = CompositeDisposable()
override fun onAttachedToWindow() {
super.onAttachedToWindow()
disposables.add(sessionManager.signIn("username", "password")
.distinctUntilChanged()
.observeOn(AndroidSchedulers.mainThread())
.subscribe { result ->
textView.text = if (result) "Success" else "Fail"
})
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
disposables.clear()
}
}
A random View observing the currentSession from SessionManager
class RandomView(val context: Context) : View(context) {
#Inject
lateinit var sessionManager: SessionManager
private val disposables = CompositeDisposable()
override fun onAttachedToWindow() {
super.onAttachedToWindow()
disposables.add(sessionManager.currentSession
.distinctUntilChanged()
.observeOn(AndroidSchedulers.mainThread())
.subscribe { session -> userTextView.text = session.userName })
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
disposables.clear()
}
}

The documentation of BehaviorProcessor says:
Calling onNext(Object), offer(Object), onError(Throwable) and onComplete() is required to be serialized (called from the same thread or called non-overlappingly from different threads through external means of serialization). The FlowableProcessor.toSerialized() method available to all FlowableProcessors provides such serialization and also protects against reentrance (i.e., when a downstream Subscriber consuming this processor also wants to call onNext(Object) on this processor recursively).
So if you define it like this:
val currentSession = BehaviorProcessor.create<Session>().toSerialized()
then you can safely call onNext from any thread, it will not cause any synchronisation problems.
Notes:
I agree that the update of the processor should be in a doOnNext instead of the map.
I think it would be better to use a Completable instead of a Single<Boolean>, and use Rx errors to indicate what prevented signing in. You should also define the error handlers in the subscribe methods.

Related

Kotlin: Live data does not change Fragment UI when data changes

I am struggling to use Live data on an MVVM pattern. The app is supposed to:
Fetch data from an API (which it does correctly)
Store that data in the Live data object from the ViewModel
Then the fragment calls the Observer method to fill the recyclerView.
The problem comes in point 3, it does nothing, and I cannot find the solution.
Here is the relevant code. (If I'm missing something, I will try to answer as quickly as possible)
Main Activity:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val viewModel: SharedViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// Custom button to fetch data from api and log the Live Data value.
binding.refreshFab.setOnClickListener {
viewModel.fetchPlayerData()
Log.d("gabs", "${viewModel.livePlayerlist.value}")
}
}
}
ViewModel:
class SharedViewModel(app: Application): AndroidViewModel(app) {
// val playerDao = LaRojaDB.getDatabase(app).playerDao()
lateinit var playerList: Players
val livePlayerlist: MutableLiveData<MutableList<Players.PlayersItem>> by lazy {
MutableLiveData<MutableList<Players.PlayersItem>>()
}
fun fetchPlayerData() {
CoroutineScope(Dispatchers.IO).launch {
val response = MyService.getLaRojaService().getAllPlayers()
withContext(Dispatchers.Main) {
if (response.isSuccessful) {
val body = response.body()
if(!body.isNullOrEmpty()){
playerList = body
val playerArrayList = mutableListOf<Players.PlayersItem>()
playerList.forEach {
playerArrayList.add(it)
}
livePlayerlist.value = playerList
}
}
}
}
}
}
The fragment that displays the recycler view: (Fragment is already showing, I set up a textView as a title to make sure since I'm new using fragments as well.)
class PlayerListFragment : Fragment() {
private var _binding: FragmentPlayerListBinding? = null
private val binding get() = _binding!!
private val model: SharedViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentPlayerListBinding.inflate(inflater, container, false)
binding.rvPlayerList.layoutManager = LinearLayoutManager(activity)
----> // This is the observer that does not update the UI** <----
model.livePlayerlist.observe( viewLifecycleOwner, {
binding.rvPlayerList.adapter = PlayerAdapter(it)
})
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_player_list, container, false)
}
}
Thank you all in advance, hope I can finally learn what is causing the issue!
I think you don't need to switch Coroutine contexts. A few changes I'd expect if I were reviewing this code:
This should all be in the same IO context. You then postValue to your liveData.
fun fetchPlayerData() {
viewModelScope.launch(Dispatchers.IO) {
val xx = api.fetch()
...
_playerState.postValue(xx) //see below
}
}
Additionally, it's preferred not to expose mutable state, so your ViewModel should not expose the MutableLiveData (which shouldn't really be lazy). But it's also better to encapsulate the state in a sealed class:
//delete this
val livePlayerlist: MutableLiveData<MutableList<Players.PlayersItem>> by lazy {
MutableLiveData<MutableList<Players.PlayersItem>>()
}
Should be: (names are just pseudo code, I have no idea what this code is about)
sealed class PlayerDataState {
data class ListAvailable(data: List<Players.PlayersItem>>): PlayerDataState
object Loading(): PlayerDataState
}
And your new LiveData:
private val _playerState = MutableLiveData<PlayerDataState>()
val playerState: LiveData<PlayerDataState>() get() = _playerState
Finally when observing from the UI, you just...
model.playerState.observe(viewLifecycleOwner, {
when (it) {
is Loading -> ...
is ListAvailable -> binding.rvPlayerList.adapter = PlayerAdapter(it.data)
}
}

Room cannot verify the data integrity and Crashes on Empty Data

I am working on Android Application in Which I am getting specific Data from Room Database by specific path in the Storage. My App Got Crashes as It does not have Any Data in the Storage and the Logcat gives me this..
java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
at androidx.room.RoomOpenHelper.checkIdentity(RoomOpenHelper.java:154)
at androidx.room.RoomOpenHelper.onOpen(RoomOpenHelper.java:135)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onOpen(FrameworkSQLiteOpenHelper.java:195)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:428)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:317)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:145)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:106)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:476)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:281)
at com.maximus.technologies.views.activities.scanneddatabase.TodoDaoScanned_Impl.getAllScan(TodoDaoScanned_Impl.java:152)
at com.maximus.technologies.views.fragments.scanhistorypackage.QRRetrievingScanClassPresenter$getAllDatFromDatabase$1.invokeSuspend(QRRetrievingScanClassPresenter.kt:29)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
The Above Error or crash Only occurs as the app dont have any data in Storage. But as I put a Data the Crash Problem Get Resolved.
I am not able to Understand what the Problem actually is...
Here is My Room Database Class..
#Database(
entities = [TodoEntity::class,TodoEntityScanned::class],
version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun TodoDao(): TodoDao
abstract fun TodoDaoScanned(): TodoDaoScanned
object DatabaseBuilder {
private var INSTANCE: AppDatabase? = null
fun getInstance(context: Context): AppDatabase {
if (INSTANCE == null) {
synchronized(AppDatabase::class) {
INSTANCE = buildRoomDB(context)
}
}
return INSTANCE!!
}
private fun buildRoomDB(context: Context) =
Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"mindorks-example-coroutines"
).build()
}
}
Room Database Retrieving Interface where app Crashes on getall()
override fun getAllDatFromDatabase(appDatabasescanned: AppDatabase) {
var list = listOf<TodoEntityScanned>()
try {
GlobalScope.launch(Dispatchers.Default) {
list = appDatabasescanned.TodoDaoScanned().getAllScan()
Log.d("hello","hello")
mView.showAllData(list)
}
}
catch (e:Exception){
Log.d("get hello",e.toString())
}
}
The getAll lies in Dao Class
interface TodoDao {
#Query("SELECT * FROM tablefilepaths")
fun getAll(): List<TodoEntity>
#Query("SELECT * FROM tablefilepaths WHERE imagespath LIKE :title")
fun findByTitle(title: String): TodoEntity
#Insert
fun insertpaths(todo: TodoEntity)
#Delete
fun deletepaths(todo: TodoEntity)
#Query("DELETE FROM tablefilepaths WHERE id = :noteId")
fun deleteNoteById(noteId: Int)
#Update
fun updateTodo(vararg todos: TodoEntity)}
Here is My Fragment Class Where I am Setting data in RecyclerView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerviewcreatehistory?.layoutManager = LinearLayoutManager(context)
recyclerviewcreatehistory?.setHasFixedSize(true)
filefetch()
customAdaptercreatehistory = CustomAdapterCreateHistory(this.context ?: return, charItemcreate!!,this)
recyclerviewcreatehistory?.adapter = customAdaptercreatehistory
}
fun filefetch() {
val noteDatabase: AppDatabase = AppDatabase.DatabaseBuilder.getInstance(requireContext())
retrivingpresenter = QRRetrievingClassPresenter(this)
retrivingpresenter!!.getAllDatFromDatabase(noteDatabase)
}
override fun showAllData(note_list: List<TodoEntity>) {
if (note_list is ArrayList<*>) {
val arraylist = note_list as ArrayList<TodoEntity>
charItemcreate=arraylist
}
if (charItemcreate.isEmpty()){
}else{
customAdaptercreatehistory?.updateUsers(note_list as ArrayList<TodoEntity>)
customAdaptercreatehistory?.notifyDataSetChanged()
// Log.d("hello", note_list[0].imagesPathData)
}
}
You have to do some checks in your getAllDatFromDatabase() inside your coroutine. I guess list variable equals null or something like that. You should check if there are any files and if not you need to put there something else.

How can I keep my current architecture of the back end while separating my local and remote data sources?

So long story short, trying to utilize googles good practices. I made a mistake of going dagger 2 for the project Di. And now I am stuck. I followed along the Sunflower architecture
I am trying to separate local data(ld) source and remote data(rd) source while if possible keeping the architecture I have. I currently have 2 data sources ld and rd that implement datasource interface. And my repository only recognises functions from that interface. In the googles example they share the same functions across both data sources, while my project diverges from that and they should have different functions. (Ld should insert quotes into DB while remote should just fetch quotes)
I've tried making a new interface that implements the datasource but is specific to local data source, and that didn't work, keep getting a missing binding error from dagger.
Github project
interface QuoteDataSource { //data source interface for both data sources
suspend fun getQuotes(): Result<List<Quote>>
}
class DefaultQuoteRepository #Inject constructor(
#ApplicationModule.QuoteRemoteDataSource private val quoteRemoteDataSource: QuoteDataSource,
#ApplicationModule.QuoteLocalDataSource private val quoteLocalDataSource: QuoteDataSource,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) : BaseRepository(), QuoteRepository {
#JvmStatic
#Singleton
#QuoteRemoteDataSource
#Provides
fun provideQuoteRemoteDataSource(
jsonNetworkService: JsonNetworkService
): QuoteDataSource {
return QuoteRemoteDataSource(jsonNetworkService.apiService)
}
#JvmStatic
#Singleton
#QuoteLocalDataSource
#Provides
fun provideQuoteLocalDataSource(
database: LocalQuoteDataBase,
ioDispatcher: CoroutineDispatcher
): QuoteDataSource {
return QuoteLocalDataSource(
database.quotesDao(), ioDispatcher
)
}
class QuoteLocalDataSource constructor( // for fetching locally stored quotes, might save favorite quootes or save the json quotes
private val quotesDao: QuotesDao,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) : QuoteDataSource {
override suspend fun getQuotes(): Result<List<Quote>> = withContext(ioDispatcher) {
return#withContext try {
Success(quotesDao.getQuotes())
} catch (e: Exception) {
Result.Error(IOException("Unable to fetch quotes!"))
}
}
class QuoteRemoteDataSource #Inject constructor(
private val apiService: QuoteApi
) : QuoteDataSource {
override suspend fun getQuotes(): Result<List<Quote>> {
val response = apiService.getQuotes().await()
try {
if (response.isSuccessful)
if(!response.body().isNullOrEmpty())
return Result.Success(response.body()) as Result<List<Quote>>
return Result.Error(IOException("Error occurred during fetching quotes!"))
} catch (e: Exception) {
Result.Error(IOException("Unable to fetch quotes!"))
}
return Result.Error(IOException("Unable to fetch quotes!"))
}
I am somehow trying to have localdata source have its own functions that are recognised by the default quote repository. As if i just add functions to the localds they won't be available to the repository.

Is private method in spring service implement class thread safe

I got a service in an project using Spring framework.
public class MyServiceImpl implements IMyService {
public MyObject foo(SomeObject obj) {
MyObject myobj = this.mapToMyObject(obj);
myobj.setLastUpdatedDate(new Date());
return myobj;
}
private MyObject mapToMyObject(SomeObject obj){
MyObject myojb = new MyObject();
ConvertUtils.register(new MyNullConvertor(), String.class);
ConvertUtils.register(new StringConvertorForDateType(), Date.class);
BeanUtils.copyProperties(myojb , obj);
ConvertUtils.deregister(Date.class);
return myojb;
}
}
Then I got a class to call foo() in multi-thread;
There goes the problem. In some of the threads, I got error when calling
BeanUtils.copyProperties(myojb , obj);
saying Cannot invoke com.my.MyObject.setStartDate - java.lang.ClassCastException#2da93171
obviously, this is caused by ConvertUtils.deregister(Date.class) which is supposed to be called after BeanUtils.copyProperties(myojb , obj);.
It looks like one of the threads deregistered the Date class out while another thread was just about to call BeanUtils.copyProperties(myojb , obj);.
So My question is how do I make the private method mapToMyObject() thread safe?
Or simply make the BeanUtils thread safe when it's used in a private method.
And will the problem still be there if I keep the code this way but instead I call this foo() method in sevlet? If many sevlets call at the same time, would this be a multi-thread case as well?
Edit: Removed synchronized keyword since it is not neccessary, see comments below.
Instead of using the static methods in the BeanUtils class, use a private BeanUtilsBean instance (http://commons.apache.org/proper/commons-beanutils/apidocs/org/apache/commons/beanutils/BeanUtilsBean.html). This way, you don't need to register/deregister your converters each time the method is called.
public class MyServiceImpl implements IMyService {
private final BeanUtilsBean beanUtilsBean = createBeanUtilsBean();
private static BeanUtilsBean createBeanUtilsBean() {
ConvertUtilsBean convertUtilsBean = new ConvertUtils();
convertUtilsBean.register(new MyNullConvertor(), String.class);
convertUtilsBean.register(new StringConvertorForDateType(), Date.class);
BeanUtilsBean beanUtilsBean = new BeanUtilsBean(convertUtilsBean);
return beanUtilsBean;
}
public MyObject foo(SomeObject obj) {
MyObject myobj = this.mapToMyObject(obj);
myobj.setLastUpdatedDate(new Date());
return myobj;
}
private MyObject mapToMyObject(SomeObject obj){
MyObject myojb = new MyObject();
beanUtilsBean.copyProperties(myojb , obj);
return myojb;
}
}
add a synchonized block to the sensitive portion of your code or synchronize the method:
http://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html

How does one extend MEF to create objects based on a factory type provided as an attribute?

Consider the following existing classes which uses MEF to compose Consumer.
public interface IProducer
{
void Produce();
}
[Export(typeof(IProducer))]
public class Producer : IProducer
{
public Producer()
{
// perform some initialization
}
public void Produce()
{
// produce something
}
}
public class Consumer
{
[Import]
public IProducer Producer
{
get;
set;
}
[ImportingConstructor]
public Consumer(IProducer producer)
{
Producer = producer;
}
public void DoSomething()
{
// do something
Producer.Produce();
}
}
However, the creation of Producer has become complex enough that it can no longer be done within the constructor and the default behavior no longer suffices.
I'd like to introduce a factory and register it using a custom FactoryAttribute on the producer itself. This is what I have in mind:
[Export(typeof(IProducer))]
[Factory(typeof(ProducerFactory))]
public class Producer : IProducer
{
public Producer()
{
// perform some initialization
}
public void Produce()
{
// produce something
}
}
[Export]
public class ProducerFactory
{
public Producer Create()
{
// Perform complex initialization
return new Producer();
}
}
public class FactoryAttribute : Attribute
{
public Type ObjectType
{
get;
private set;
}
public FactoryAttribute(Type objectType)
{
ObjectType = objectType;
}
}
If I had to write the "new" code myself, it may very well look as follows. It would use the factory attribute, if it exists, to create a part, or default to the MEF to create it.
public object Create(Type partType, CompositionContainer container)
{
var attribute = (FactoryAttribute)partType.GetCustomAttributes(typeof (FactoryAttribute), true).FirstOrDefault();
if (attribute == null)
{
var result = container.GetExports(partType, null, null).First();
return result.Value;
}
else
{
var factoryExport = container.GetExports(attribute.ObjectType, null, null).First();
var factory = factoryExport.Value;
var method = factory.GetType().GetMethod("Create");
var result = method.Invoke(factory, new object[0]);
container.ComposeParts(result);
return result;
}
}
There are a number of articles how to implement a ExportProvider, including:
MEF + Object Factories using Export Provider
Dynamic Instantiation
However, the examples are not ideal when
The application has no dependencies or knowledge of Producer, only IProducer. It would not be able to register the factory when the CompositionContainer is created.
Producer is reused by several applications and a developer may mistakenly forget to register the factory when the CompositionContainer is created.
There are a large number of types that require custom factories and it may pose a maintenance nightmare to remember to register factories when the CompositionContainer is created.
I started to create a ExportProvider (assuming this would provide the means to implement construction using factory).
public class FactoryExportProvider : ExportProvider
{
protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition,
AtomicComposition atomicComposition)
{
// What to do here?
}
}
However, I'm having trouble understanding how to tell MEF to use the factory objects defined in the FactoryAttribute, and use the default creation mechanism if no such attribute exists.
What is the correct manner to implement this? I'm using MEF 2 Preview 5 and .NET 4.
You can make use of a property export:
public class ProducerExporter
{
[Export]
public IProducer MyProducer
{
get
{
var producer = new Producer();
// complex initialization here
return producer;
}
}
}
Note that the term factory isn't really appropriate for your example, I would reserve that term for the case where the importer wants to create instances at will, possibly by providing one or more parameters. That could be done with a method export:
public class ProducerFactory
{
[Export(typeof(Func<Type1,Type2,IProducer>)]
public IProducer CreateProducer(Type1 arg1, Type2 arg2)
{
return new Producer(arg1, arg2);
}
}
On the import side, you would then import a Func<Type1,Type2,IProducer> that you can invoke at will to create new instances.

Resources