How to use Saved State module for ViewModel in Background Thread - java

How to use Saved State module for ViewModel in Background Thread
For MutableLiveData we have the option to use setvalue and postvalue , where Postvalue can be used in background thread.
How ever How can we use BACKGROUND THREAD FOR Saved State module for ViewModel
here Is the code I am trying
public class CommonViewModel extends ViewModel {
private SavedStateHandle mState;
public CommonViewModel(SavedStateHandle savedStateHandle) {
mState = savedStateHandle;
}
private static final String NAME_KEY = "name";
private Executor mExecutor = Executors.newSingleThreadExecutor();
public LiveData<ArrayList<CommonOwn>> getCart() {
if (mState.getLiveData(NAME_KEY) == null) {
initCart();
}
return mState.getLiveData(NAME_KEY);
}
public void initCart() {
mState.set(NAME_KEY, new ArrayList<CommonOwn>());
}
public void addItemToCart(CommonOwn commonOwn) {
if (getCart().getValue() == null) {
initCart();
}
ArrayList<CommonOwn> cartItemList = new ArrayList<CommonOwn>(getCart().getValue());
if (cartItemList.contains(commonOwn)) {
int a = cartItemList.indexOf(commonOwn);
cartItemList.remove(a);
} else {
cartItemList.add(commonOwn);
}
// mState.set(NAME_KEY, cartItemList);
mExecutor.execute(new Runnable() {
#Override
public void run() {
mState.set(NAME_KEY, cartItemList);
}
});
}
}
when using background thread The following error occurs
java.lang.IllegalStateException: Cannot invoke setValue on a background thread
at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:487)
at androidx.lifecycle.LiveData.setValue(LiveData.java:306)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
at androidx.lifecycle.SavedStateHandle$SavingStateLiveData.setValue(SavedStateHandle.java:367)
at androidx.lifecycle.SavedStateHandle.set(SavedStateHandle.java:256)
at com.example.CommonViewModel$1.run(CommonViewModel.java:63)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
how can we solve this issue.

The following line can never be null:
mState.getLiveData(NAME_KEY) == null
Hope this illustrates the solution. You just rely on the MutableLiveData from SavedStateHandle:
public class CommonViewModel extends ViewModel {
private MutableLiveData<ArrayList<CommonOwn>> cart;
public CommonViewModel(SavedStateHandle savedStateHandle) {
cart = savedStateHandle.getLiveData(NAME_KEY);
}
private static final String NAME_KEY = "name";
private Executor mExecutor = Executors.newSingleThreadExecutor();
public MutableLiveData<ArrayList<CommonOwn>> getCart() {
return cart;
}
public void addItemToCart(CommonOwn commonOwn) {
ArrayList<CommonOwn> cartItemList;
if(cart.getValue() == null) {
cartItemList = new ArrayList<CommonOwn>();
} else {
cartItemList = cart.getValue();
}
if (cartItemList.contains(commonOwn)) {
int a = cartItemList.indexOf(commonOwn);
cartItemList.remove(a);
} else {
cartItemList.add(commonOwn);
}
// mState.set(NAME_KEY, cartItemList);
mExecutor.execute(new Runnable() {
#Override
public void run() {
cart.postValue(cartItemList);
}
});
}
}

Related

Android application works with debugger but not in release mode

I have been trying to implement a debouncer for my application, I am trying to reduce requests to the server using this debouncer, eventually, I managed to implement the debouncer but, it seems to work only when I am using the debugger to debug the app.
This is how I implemented the debugger
public class NewDebouncedRunnable implements Runnable {
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private final Runnable operation;
private final long delayMillis;
private ScheduledFuture<?> scheduledFuture;
private long lastRunTime = -1;
private boolean isQueued = false;
private Context context;
public NewDebouncedRunnable(Context context,Runnable operation, String name, long delayMillis) {
this.operation = operation;
this.delayMillis = delayMillis;
this.context = context;
}
public void synchronized run() {
if(lastRunTime==-1){
Toasty.success(context,"LastRunTime reset",0).show();
}
long currentTime = getCurrentTimeMillis();
System.out.println(currentTime-lastRunTime);
if (shouldRunNow(currentTime)) {
// we've never called this before, call it now
System.out.println("Registered");
lastRunTime = currentTime;
operation.run();
} else {
System.out.println("Queued");
if(scheduledFuture!=null){
scheduledFuture.cancel(true);
}
schedule(this::scheduledRun, delayMillis);
}
}
private void scheduledRun() {
Log.d("SCHEDULED RUN","running scheduled task");
lastRunTime = getCurrentTimeMillis();
isQueued = false;
operation.run();
}
private boolean shouldRunNow(long currentTime) {
return currentTime-lastRunTime > delayMillis;
}
private void schedule(Runnable call, long delayMillis) {
scheduledFuture = scheduler.schedule(call, delayMillis, TimeUnit.MILLISECONDS);
}
long getCurrentTimeMillis() {
return System.currentTimeMillis();
}
}
This is my Debounced Runnable instance
NewDebouncedRunnable increaseOrderItemDebounce = new NewDebouncedRunnable(context,new Runnable() {
#Override
public void run() {
try{
Log.d("DEBOUNCE","Debounced run");
HelperClass.getInstance().updateOrderItem(context, item, item.getQuantity(), new HelperClass.ResponseListener<ResponseAddUpdateToCart>() {
#Override
public void onSuccess(ResponseAddUpdateToCart response) {
Toasty.success(context,"Cart Updated", Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(String error) {
}
});
}catch(Exception ex){
ex.printStackTrace();
}
}
},"",20000);
This is how I set my onClickListener for the button
holder.btnPlus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
item.setQuantity(item.getQuantity()+1);
increaseOrderItemDebounce.run();
}
});
I should also note that the button is part of a recycler view, after making a successful request to the server, the adapter is notified that items have changed using adapter.notifyItemRangeChanged()like this
if (orderListAdapter != null && orderlistRview.getLayoutManager() != null) {
Parcelable recyclerViewState = orderlistRview.getLayoutManager().onSaveInstanceState();
orderlistRview.getLayoutManager().onRestoreInstanceState(recyclerViewState);
orderListAdapter.notifyItemRangeChanged(0,orderListAdapter.getItemCount());
}
The main issue I am facing here is that every time I click the button in regular mode the lastRunTime variable is reset, resulting in continuous requests to the server but when I am running the app using a debugger and setting breakpoints, it is working as it is intended to.
try this in Gradle.build
buildTypes {
release {
**debuggable true**
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

After calling the notify method the thread is still waiting

I have several classes:
WorkerQueue - queue.
WorkerRunnable - runnable which contain worker thread method with loop and its stop flag.
SomeClass - just class for initalize and management thread stuff.
I need to stop thread in destroy method. For it I create endTask method but it doesn't work thread still waiting.
If I replace called endTask method to mWorkerQueue.add(...), it's working fine and thread stop waiting.
class WorkerQueue {
private Queue<BasePrintTask> mTaskQueue = new ArrayDeque<>();
private Object obj = new Object();
public BasePrintTask get() {
while (mTaskQueue.isEmpty())
{
try {
synchronized (obj) {
obj.wait();
}
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}
}
return mTaskQueue.remove();
}
public void add(BasePrintTask task) {
mTaskQueue.add(task);
synchronized (obj) {
obj.notify();
}
}
public void endTask() {
synchronized (obj) {
obj.notify();
}
}
}
public class WorkerRunnable implements Runnable
{
private boolean mFlag = false;
private WorkerQueue mWorkerQueue = null;
public WorkerRunnable(WorkerQueue workerQueue)
{
this.mPrinterManager = printerManager;
this.mWorkerQueue = workerQueue;
}
public void endThread() { mFlag = false; }
public void startThread() { mFlag = true; }
#Override
public void run() {
while(mFlag)
{
try
{
BasePrintTask task = mWorkerQueue.get();
}
catch (NoSuchElementException el)
{
}
}
}
}
public class SomeClass
{
private final Context mContext;
private WorkerQueue mWorkerQueue = new WorkerQueue();
private Thread mWorkerThread = null;
private WorkerRunnable mWorkerRunnable = null;
public SomeClass(final Context context)
{
this.mContext = context;
}
public void init()
{
mWorkerRunnable = new WorkerRunnable(mWorkerQueue);
}
public int prepare() {
mWorkerRunnable.startThread();
mWorkerThread = new Thread(mWorkerRunnable);
mWorkerThread.start();
return 0;
}
public int destroy() {
mWorkerRunnable.endThread();
mWorkerQueue.endTask();
try {
if(mWorkerThread.isAlive()) {
mWorkerThread.join();
}
}
catch (InterruptedException e) {
}
return 0;
}

How to replace AsyncTask onProgressUpdate() using Runnable callbacks

I'm trying to replace deprecated AsyncTask without using Kotlin Coroutines or others libraries, so I have
MyTask objects with following structure
public abstract class MyTask<R> implements MyCallable<R> {
#Override
public void setUiForLoading() {
//runs on ui
}
#Override
public void setDataAfterLoading(R result) {
//runs on ui
}
#Override
public R call() throws Exception {
//runs in background
return null;
}
}
MyCallable is just a simple interface
public interface MyCallable<R> extends Callable<R>{
void setDataAfterLoading(R result);
void setUiForLoading();
}
And use this MyTaskRunner to execute them
public class MyTaskRunner {
private final Handler handler = new Handler(Looper.getMainLooper());
private final Executor executor = Executors.newCachedThreadPool();
public <R> void executeAsync(MyCallable<R> callable) {
try {
callable.setUiForLoading();
executor.execute(new RunnableTask<R>(handler, callable));
} catch (Exception e) {
}
}
public static class RunnableTask<R> implements Runnable{
private final Handler handler;
private final MyCallable<R> callable;
public RunnableTask(Handler handler, MyCallable<R> callable) {
this.handler = handler;
this.callable = callable;
}
#Override
public void run() {
try {
final R result = callable.call();
handler.post(new RunnableTaskForHandler(callable, result));
} catch (Exception e) {
}
}
}
public static class RunnableTaskForHandler<R> implements Runnable{
private MyCallable<R> callable;
private R result;
public RunnableTaskForHandler(MyCallable<R> callable, R result) {
this.callable = callable;
this.result = result;
}
#Override
public void run() {
callable.setDataAfterLoading(result);
}
}
}
it works, but I cannot figure how I could replicate correctly the behaviour of publishProgress() and onProgressUpdate() of AsyncTask useful to show actual progress rather that just indeterminate
I cannot give the same code as yours but hopefully you get the idea.
Everything is self explain in code itself.
import android.app.*;
import android.graphics.*;
import android.os.*;
import android.widget.*;
import java.lang.ref.*;
public class MainActivity extends Activity
{
private static final class HeavyJob implements Runnable
{
private final WeakReference<Handler> handler;
private final Thread thread;
private boolean isAlive;
private boolean state;
private int progress;
public final HeavyJob(final Handler handler)
{
this.handler = new WeakReference<Handler>(handler);
thread = new Thread(this);
isAlive = true;
thread.setPriority(Thread.NORM_PRIORITY);
thread.start();
}
#Override
public final void run()
{
while(isAlive) {
try {
synchronized(this) {
while(!state) this.wait();
}
Thread.sleep(200L); //Let say this a heavy job which takes 200 m/s each round.
progress += 10;
final Handler hanRef = handler.get();
if(hanRef == null) {
isAlive = false;
handler.clear();
break;
}
final Message msg = Message.obtain();
msg.what = 0;
msg.arg1 = progress;
hanRef.sendMessageAtTime(msg, SystemClock.uptimeMillis()); //Update its progress each round.
} catch(final InterruptedException e) {}
}
//Finished ???
final Handler hanRef = handler.get();
if(hanRef != null) {
final Message msg = Message.obtain();
msg.what = 1;
msg.arg1 = progress; //Make your progress is 100% completed and updated.
//msg.obj = bitmap;
hanRef.sendMessageAtTime(msg, SystemClock.uptimeMillis());
}
}
public final synchronized void resume()
{
if(isAlive) {
state = true;
this.notify();
}
}
public final void suspend()
{
state = false;
thread.interrupt();
}
public final void stop()
{
isAlive = false; // In case interrupt() does nothing (Thread was not in sleep nor wait mode).
thread.interrupt();
handler.clear();
}
}
private static final class UIHandler extends Handler
{
private final WeakReference<MainActivity> activity;
public final UIHandler(final MainActivity activity)
{
super(Looper.getMainLooper());
this.activity = new WeakReference<MainActivity>(activity);
}
#Override
public final void handleMessage(final Message msg)
{
final MainActivity referent = activity.get();
if(referent != null) {
switch(msg.what) {
case 0: referent.onProgress(msg.arg1); break;
case 1: referent.onPostExecute(msg.arg1, (Bitmap)msg.obj); break;
}
}
}
}
private ProgressBar pb;
private ImageView iv;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
pb = findViewById(R.id.pb);
iv = findViewById(R.id.next);
UIHandler handler = new UIHandler(this);
//Initilize the object but will not run yet.
HeavyJob hj = new HeavyJob(handler);
//Run the job
hj.resume();
//Pause the job
hj.suspend();
//Resume the job
hj.resume();
//Stop the job
hj.stop();
//Multiple jobs
for(int i=0; i<10; i++) {
new HeavyJob(handler);
}
}
public final void onProgress(final int progress) {
pb.setProgress(progress);
}
public final void onPostExecute(final int progress, Bitmap bitmap)
{
pb.setProgress(progress);
if(bitmap != null) iv.setImageBitmap(bitmap);
}
}
The best I founded to do it is:
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MainActivity extends AppCompatActivity {
Button btn_start;
TextView text;
ProgressBar progressBar1, progressBar2;
int num = 0;
ExecutorService service;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = findViewById(R.id.textHello);
btn_start = findViewById(R.id.btn_start);
progressBar1 = findViewById(R.id.progressbar1);
progressBar2 = findViewById(R.id.progressBar2);
btn_start.setOnClickListener(v -> toDo());
}
private void toDo() {
service = Executors.newSingleThreadExecutor();
service.execute(() -> {
runOnUiThread(() -> {
// onPreExecute method of AsyncTask
progressBar1.setVisibility(View.VISIBLE);
progressBar2.setVisibility(View.VISIBLE);
});
// doInBackground of AsyncTask
for (int i = 1; i <= 10000; i++) {
num = i;
runOnUiThread(() -> {
// onProgressUpdate method of AsyncTask
progressUpdate(num);
});
}
runOnUiThread(() -> {
// onPostExecute method of AsyncTask
progressBar1.setVisibility(View.GONE);
progressBar2.setVisibility(View.GONE);
});
});
}
public void progressUpdate(Integer i) {
text.setText(String.valueOf(i));
progressBar2.setProgress(i);
}
}

Android ChangeOverTime method freezing at runtime (but not consistently)

So I am running into a problem with a function I made to slowly change the value of a monitored variable over time that is causing all the java logic to lock up. It doesn't seem to be producing an error or make the application crash so it must be getting stuck in the while loop or something but the logging isn't firing while it is locked so I am just very confused. If anyone can help me figure out how to diagnose what is causing the freezing that would be very much appreciated
EDIT: Turns out the problem was updating the UI from another thread, I manged to get it to crash and got the error and used a CountDownTimer instead of the background thread and now it is working fine. For those curious checkout my GitHub for this project.
Function in question:
public static void changeOverTime(final MonitoredVariable<Integer> tVar, final int tTo, final long tTime, final long tUpdateFreq) {
if (tTime < tUpdateFreq) { Log.e(TAG, "Time must be greater then update freq."); }
if (tVar == null) { Log.e(TAG, "Container cannot be null."); }
else {
final Thread tBackgroundThread = new Thread(new Runnable() {
#Override
public void run() {
float tSteps = tTime / tUpdateFreq; // 2000/100 = 20
float tInterval = (tTo - tVar.get()) / tSteps; // 67-175 = -108/20 = -5.4
float tVal = tVar.get(); //175
while (Math.round(tVal) != tTo) { //67(After 20 Times) != 67 -> FALSE
Debug.Log(TAG, "EQ: " + Math.round(tVal) + "?=" + tTo);
tVal += tInterval; // -5.4 * 20(Times) = -108+175 = 67
tryToSleep(tUpdateFreq); // 100ms * 20(Times) = 2000ms total
tVar.set(Math.round(tVal));
}
}
});
tBackgroundThread.start();
}
}
Supporting Function:
private static void tryToSleep(long tTime) {
try { sleep(tTime); }
catch (InterruptedException e) { e.printStackTrace(); }
}
Monitored Variable Class:
public class MonitoredVariable<Prototype> {
protected Prototype mData;
protected ChangeListener mListener;
public MonitoredVariable(Prototype tData) {
this(tData, null);
}
public MonitoredVariable(Prototype tData, ChangeListener tListener) {
if (tListener != null) setListener(tListener);
mData = tData;
}
public Prototype get() {
return mData;
}
public void set(Prototype tData) {
if (mData != tData) {
mData = tData;
notifyChange();
}
}
public void setListener(ChangeListener tListener) {
mListener = tListener;
}
public ChangeListener getListener() {
return mListener;
}
public void notifyChange() {
if (mListener != null) mListener.onChange();
}
public interface ChangeListener {
void onChange();
}
}
Usage:
public static void init() {
MonitoredVariable.ChangeListener tUpdateBackground = new MonitoredVariable.ChangeListener() {
#Override
public void onChange() { updateBackgroud();
}
};
mTop = new MonitoredVariable[]{
new MonitoredVariable<>(0, tUpdateBackground),
new MonitoredVariable<>(0, tUpdateBackground),
new MonitoredVariable<>(0, tUpdateBackground)
};
mBottom = new MonitoredVariable[]{
new MonitoredVariable<>(0, tUpdateBackground),
new MonitoredVariable<>(0, tUpdateBackground),
new MonitoredVariable<>(0, tUpdateBackground)
};
mAnimationLoop = new Handler();
mAnimation = new Runnable() {
#Override
public void run() {
Debug.Log(TAG, "RUNNING ANIMATION");
final Random RNG = new Random();
for (MonitoredVariable<Integer>[] tBackground: new MonitoredVariable[][] {mTop, mBottom}) {
for (MonitoredVariable<Integer> tColor : tBackground) {
int tRandomColor = RNG.nextInt(255);
//tColor.set(tRandomColor);
Shift.changeOverTime(tColor, tRandomColor, 2000, 100);
}
}
if(mAnimate.get()) {
mAnimationLoop.postDelayed(mAnimation, 10000);
}
}
};
mAnimate = new MonitoredVariable<>(false, new MonitoredVariable.ChangeListener() {
#Override
public void onChange() {
if (mAnimate.get()) mAnimationLoop.postDelayed(mAnimation, 0);
else mAnimationLoop.removeCallbacks(mAnimation);
}
});
}
public static void setBackground(final Activity tActivity){
final View tActivityBackground = tActivity.findViewById(R.id.background);
mListener = new ChangeListener() {
#Override
public void onChange() { tActivityBackground.setBackground(mBackground); }
};
notifyChange();
}
private static void updateBackgroud() {
int tTop = Color.argb(255, mTop[0].get(), mTop[1].get(), mTop[2].get());
int tBottom = Color.argb(255, mBottom[0].get(), mBottom[1].get(), mBottom[2].get());
int[] colors = {tTop, tBottom};
mBackground = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, colors);
mBackground.setCornerRadius(0f);
notifyChange();
}
public static void animateBackground(boolean tAnimate) {
mAnimate.set(tAnimate);
}
public static void notifyChange() {
if (mListener != null) mListener.onChange();
}
public interface ChangeListener {
void onChange();
}

Java Observer Pattern finding sender?

observer.update();
Above method makes the visibility of observers "GONE", but i want to change all other observers except sender observer. How can i control this ?
My all actions are observer and register themself in their constructor like below,
public class ParentAction extends AbstractAction implements IActionObserver{
private ArrayList<IAction> lSubItems;
private View subView;
public ParentAction( String ItemText,int drawable,ArrayList<IAction> SubItems) {
super(ItemText,drawable);
lSubItems = SubItems;
ActionHolder.getInstance().registerObserver(this);
}
#Override
public void update() {
getSubView().setVisibility(View.GONE);
} ...
ActionHolder
public class ActionHolder implements IActionSubject {
private static ActionHolder uniqueActionHolder;
private ArrayList observers;
private ActionHolder() {
observers = new ArrayList();
}
public static synchronized ActionHolder getInstance() {
if (uniqueActionHolder == null) {
uniqueActionHolder = new ActionHolder();
}
return uniqueActionHolder;
}
public void registerObserver(IActionObserver o) {
observers.add(o);
}
public void removeObserver(IActionObserver o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(i);
}
}
public void notifyObserver() {
for (int i = 0; i < observers.size(); i++) {
IActionObserver observer = (IActionObserver) observers.get(i);
observer.update();
}
}
public void actionClicked(View view) {
notifyObserver();
}
}
Is this your own implementation of the observer pattern? If so, you can modify the notify method, for instance:
public void notifyObserver(IAction sender) {
for (int i = 0; i < observers.size(); i++) {
IActionObserver observer = (IActionObserver) observers.get(i);
if (observer != sender)
observer.update();
}
}
and call this as
ActionHolder.getInstance().notifyObserver(this);
Alternatively, you could add a flag in your action class:
private bool sender = false;
set the flag before notifying:
sender = true;
ActionHolder.getInstance().notifyObserver();
and use this flag in the update:
#Override
public void update() {
if (!sender) {
getSubView().setVisibility(View.GONE);
}
sender = false;
}
You raising event in actionClicked method and then notifying all observers. Just pass a reference to your sender observer to skip its refreshing later.
If i got your code correctly, you can achieve that by controlling sender with your view
public void actionClicked(View view) {
notifyObserver(view);
}
public void notifyObserver(View view) {
for (int i = 0; i < observers.size(); i++) {
IActionObserver observer = (IActionObserver) observers.get(i);
observer.update(view);
}
}
And update method skips current view
#Override
public void update(View view) {
if (!getSubView().equals(view)) {
getSubView().setVisibility(View.GONE);
}
}

Categories

Resources