Is is possible to make a method execute only once? - java

I have a for loop and structure like this:
for(....)
....
....
if(isTrue)
... do something..
.. method to be executed once (doTrick) is declared outside for loop.
....endif
endfor
public void doTrick()
...
...
..end
Is it possible for a method in for loop to be executed only once?

Sure!..
if(!alreadyExecuted) {
doTrick();
alreadyExecuted = true;
}

Your can use AtomicBoolean to make sure the task is only called the first time:
import java.util.concurrent.atomic.AtomicBoolean;
public class Once {
private final AtomicBoolean done = new AtomicBoolean();
public void run(Runnable task) {
if (done.get()) return;
if (done.compareAndSet(false, true)) {
task.run();
}
}
}
Usage:
Once once = new Once();
once.run(new Runnable() {
#Override
public void run() {
foo();
}
});
// or java 8
once.run(() -> foo());

if you use kotlin, you can do this:
val execOnce by lazy {
print("hello, world")
}

In Java 8, you can effectively do this using automatic memoization as described here: Do it in Java 8: Automatic memoization
I'll admit that memoization could be considered overkill for a "run once" scenario, but it is a rather clean alternative to some described in previous answers.
For instance:
public void doSomething() { ... }
Map<Boolean, Boolean> cache = new ConcurrentHashMap<>();
public void doSomethingOnce() {
cache.computeIfAbsent(true, x -> {
doSomething();
return true;
});
}

You can avoid the if() by using this trick:
private Runnable once;
private final static Runnable NOP = new Runnable () {
public void run () {
// Do nothing
}
}
public void method () {
once = new Runnable () {
public void run () {
doTrick();
once = NOP;
}
}
for (...) {
...
once.run();
...
}
}

Another overkill solution:
Depending on what you want to do, it might be possible to use a static initialization block.
public class YourKlass{
public void yourMethod(){
DoTrick trick;
for( int i = 0; condition; i++){
// ... (1)
trick = new DoTrick(); // or any kind of accessing DoTrick
// ... (2)
}
}
}
public class DoTrick{
static{
// Whatever should be executed only once
}
}
Simple solution:
Or, instead you just want to execute the first part outside of the loop:
int i = 0;
if( condition ){
// ... (1)
// do trick
// ... (2)
}
for(i = 1; condition; i++){
// ... (1)
// ... (2)
}

perhaps the break keyword is what you need? After running you method call break; I am sorry its not 100% clear what you mean from your question.
Have a look here from the sun docs

my sample from my app:
boolean alreadyExecuted = false;
then :
private void startMyService() {
if(!alreadyExecuted) {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 4 seconds
context.startService(new Intent(context, myService.class));
alreadyExecuted = true;
}
}, 4000);
}

I want to do something slight more complex. In a multi-threaded environment ensure that the methods is run only one (which is solved by Hover Ruan's answer above), but also block any thread so none return until the method is done.
My solution is to use a Semaphore to do the blocking.
public class Once implements Runnable {
private static Semaphore signal = new Semaphore(1,true);
private static boolean done=false;
#Override
public void run() {
if(done)
return;
signal.acquireUninterruptibly();
if(done) {
signal.release();
return;
}
doTrick(); // actually run the task
done=true;
signal.release();
return;
}
static int result; // Result of running the task
static int count; // number of times its been called
/**
* The task we only want to run once
*/
public void doTrick() {
++count;
Random rnd = new Random();
for(int i=0;i<10000;++i) {
result += rnd.nextInt(100);
}
try {
Thread.sleep(1000); // just to ensure all thread start
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(10);
for(int i=0;i<5;++i) { // multiple instances
final Once task = new Once();
for(int j=0;j<5;++j) { // multiple calls of same instance
executor.submit(() -> {
task.run();
System.out.println("Result "+Once.result+" count "+Once.count);
} );
}
}
executor.shutdown();
try {
executor.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
The program should pause, waiting until the first thread has finished, then all other threads should finish printing the result stored from the first run.

or by using Shared Preferences:
sharedpref = PreferenceManager.getDefaultSharedPreferences(this);
isFirstRun = sharedpref.getBoolean("FIRSTRUN", true);
if (isFirstRun)
{
// Do your unique code magic here
SharedPreferences.Editor editor = wmbPreference.edit();
editor.putBoolean("FIRSTRUN", false);
editor.commit();
}else{
//this will repeat everytime
}

Here is an example way. Just use "new Getlineonce();"
class Getlineonce {
static int[] linesRun = new int[0];
public Getlineonce() {
boolean lineRan = false;
int line = Thread.currentThread().getStackTrace()[2].getLineNumber();
for(int i = 0; i < linesRun.length; i++) {
if(line == linesRun[i]) {
lineRan = true; //Dont print
}
}
if(!lineRan) {
add(line);
System.out.println(line + " ran!");
}
}
public void add(int line) {
int newSize = linesRun.length+1;
int[] linesRunBuff = new int[newSize];
for(int i = 0; i < newSize-1; i++) {
linesRunBuff[i] = linesRun[i];
}
linesRunBuff[newSize-1] = line;
linesRun = linesRunBuff;
}
}

For me, the perfect solution was...
public class MainActivity extends BaseActivity {
private static boolean splash = false;
if (!splash){
runSplash();
}
private void runSplash(){
MainActivity.splash = true;
}
}
Defined my variable as private static and use the access via class.variable in my function :)

Try this. First, this will be called only once when the app is being installed for the first time on the user's device. Use SharedPreferences this will help us to remember that this method has already been called so it will not re-call it again when the app is killed or even if the device was turned off. (But keep in mind that when the user un-stall and then re-install the app the method will be called again)
public class MainActivity extends AppCompatActivity {
private static boolean alreadyExecuted = false; // Use private static boolean
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); // Use SharedPreferences
if (!prefs.getBoolean("onlyonce", false)) {
startMyService(); // Method to be called only once as long as the app wont be un-stalled but there is
// no problem if the app will be killed or the device being turned off.
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean("onlyonce", true);
editor.commit();
}
}
private void startMyService() {
if (!alreadyExecuted) {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
Intent intent = new Intent(MainActivity.this, SecondActivity2.class);
startActivity(intent);
alreadyExecuted = true;
}
}, 4000);
}
}
}

import java.util.ArrayList;
class RemoveDuplicate {
public static void main(String[] args) {
ArrayList<String> originalList = new ArrayList<String>();
originalList.add("foo");
originalList.add("bar");
originalList.add("bat");
originalList.add("baz");
originalList.add("bar");
originalList.add("bar");
String str="bar";
ArrayList<String> duplicateList = new ArrayList<String>();
// adding duplicates to duplicateList
for(String currentString : originalList) {
if(currentString.startsWith(str)) {
duplicateList.add(currentString);
}
}
// removing duplicates in duplicatesList
for(String removeString : duplicateList) {
originalList.remove(removeString);
}
// finally adding duplicateElement
originalList.add(str);
for(String currEntry : originalList) {
System.out.println(currEntry);
}
}
}

Related

Keep UI responsive while fetch data from internet alternative of Handler postDelayed

I have a ArrayList that I fetch from internet. Now I am performing the list retrieval using Handler postdelayed. Like below in the onCreate of the activity.
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
lis_dat.remove(lis_dat.size() - 1);
if (gri_not_lis) {
rv_3r_ada.notifyItemRemoved(lis_dat.size());
} else {
rv_3r_2c_ada.notifyItemRemoved(lis_dat.size());
}
List<String> lis_of_dat;
int cou = 0, pos = lis_dat.size();
String gen_sta, gen_end, gen_mon;
if (yea) {
String[] mon_sym = new DateFormatSymbols().getMonths();
lis_of_dat = Arrays.asList(mon_sym);
} else {
int how_man;
if (lis_is_new) {
how_man = 500;
} else {
how_man = 15;
}
if (day_in_bet(sta, end) <= how_man) {
gen_sta = sta;
gen_end = end;
} else {
gen_sta = sta;
Calendar cal = Calendar.getInstance();
cal.setTime(db_han.con_dat(sta));
cal.add(Calendar.DATE, how_man);
gen_end = USE_DAT.format(cal.getTime());
}
lis_of_dat = gen_dat_in_bet(gen_sta, gen_end);
}
for (String dat : lis_of_dat) {
if (yea) {
Date date = get_mon_dat(dat, YEA.format(db_han.con_dat(sta)));
gen_sta = get_mon_fir_or_las_dat(date, true);
gen_end = get_mon_fir_or_las_dat(date, false);
gen_mon = dat;
} else {
gen_sta = dat;
gen_end = null;
gen_mon = mon;
}
add_to_lis(gen_sta, gen_end, gen_mon, pos, gri_not_lis);
pos++;
}
pos_f[0] = pos;
cou_f[0] = cou;
is_loa = false;
}
}, 1000);
Now my question is this 1000 milliseconds here may vary in different devices. On whom the UI will stay frozen. So what's the alternative of this. Instead of waiting for 1 sec how can I wait till the UI loading is complete and then I do the retrieval?
I could use AsyncTask but then the task will stay running till the task is complete even if I go to another activity. but i don't need the task running after onPause is called. So how do I do it?
You can use AsyncTask which running in separate thread, your keeps UI responsive . You can cancel it in onPause of Activity by calling **asyncTaskRunner.cancel(true) **
private class AsyncTaskRunner extends AsyncTask<String, String, String> {
private String resp;
ProgressDialog progressDialog;
#Override
protected String doInBackground(String... params) {
return resp;
}
#Override
protected void onPostExecute(String result) {
}
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(String... text) {
}
}
You can save instance of handler and then remove all call backs in
onPause of the activity.
private Handler handler = new Handler();
handler.postDelayed(() -> {
// do you task here
},1000);
#Override
public void onPause() {
super.onPause();
handler.removeCallbacksAndMessages(null); // this is important
}
And if you want something which do not freeze UI without any delay,
there you go
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Executors.newSingleThreadExecutor().submit(() -> {
// Do you task here without any delay it will not freeze the UI
});
}
You can also try this,
class MyThread implements Runnable{
#Override
public void run() {
// Your Task Here, put your all calculations Here
}
}
and then in onCrate()
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(new MyThread());
and then in onPause() or onDestory()
executor.shutdownNow();// this will stop the thread instantly

Display values in ArrayList one by one in textView

I am trying to display values inside ArrayList on single line textView one by one after some interval. How to achieve this without blocking the main thread?
I have written code which is able to do this with Thread.sleep but, after a few seconds of running, activity is getting crashed. I have used For Loop & Thread.sleep to iterate every ArrayList value after some interval.
When activity crashes, I am getting IndexOutOfBondException after a few seconds of running.
public void errorRepeater() {
Thread t = new Thread() {
#Override
public void run() {
// !isInterrupted()
while (!isInterrupted()) {
for (xz = 0; xz < errorList.size(); xz++) {
try {
Thread.sleep(2000); //1000ms = 1 sec
runOnUiThread(new Runnable() {
#Override
public void run() {
String sErrorList = errorList.get(xz);
String sErrorListOkBox = errorListOkBox.get(xz);
Log.i("MQTT sErrorList", sErrorList);
TextView tvC1HPLP = findViewById(R.id.errormsg);
tvC1HPLP.setText(sErrorList);
TextView tvok = findViewById(R.id.ok);
tvok.setText(sErrorListOkBox);
rl.setBackgroundResource(R.drawable.errorred);
tvC1HPLP.setTextColor(Color.RED);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
t.start();
}
textView should display values inside ArrayList one by one without crashing activity.
Just for reference, you can try something like this.
// You can define those both textview globally.
TextView tvC1HPLP = findViewById(R.id.errormsg);
TextView tvok = findViewById(R.id.ok);
Handler mHandler = new Handler();
final Runnable runnable = new Runnable() {
int count = 0;
#Override
public void run() {
String sErrorList = errorList.get(count%errorList.size);
String sErrorListOkBox = errorListOkBox.get(count%errorListOkBox.size);
tvC1HPLP.setText(sErrorList);
tvok.setText(sErrorListOkBox);
rl.setBackgroundResource(R.drawable.errorred);
tvC1HPLP.setTextColor(Color.RED);
count++;
mHandler.postDelayed(this, 4000); // four second in ms
}
};
mHandler.postDelayed(runnable, 1000);

Android MIDI Threading InteruptedException - Aftertouch Messages

Trying to run MIDI on my Android app. I'm following the midisuite example to configure my app and it works fine with the exception of aftertouch. Whenever I try to trigger aftertouch, I run into a threading exception type
InteruptedException. How should I prevent this threading issue? My knowledge on multithreading isn't the best or else I would've figured this out already. All I can really tell right now is that the message is sending too fast and the thread hasn't woken up yet from its sleep call.
I followed the github repo with my code as follows:
MidiReceiver subclass:
#TargetApi(Build.VERSION_CODES.M)
public class MidiEngine extends MidiReceiver {
public AudioActivity activity;
private MidiEventScheduler eventScheduler;
private MidiFramer midiFramer;
private MidiReceiver midiReceiver = new MyReceiver();
private Thread mThread;
private boolean go;
private int mProgram;
public MidiEngine() {
this(new AudioActivity());
}
public MidiEngine(AudioActivity activity) {
this.activity = activity;
midiReceiver = new MyReceiver();
midiFramer = new MidiFramer(midiReceiver);
}
public AudioActivity getActivity() {
return this.activity;
}
/* This will be called when MIDI data arrives. */
#Override
public void onSend(byte[] data, int offset, int count, long timestamp)
throws IOException {
if (eventScheduler != null) {
if (!MidiConstants.isAllActiveSensing(data, offset, count)) {
eventScheduler.getReceiver().send(data, offset, count,
timestamp);
}
}
}
// Custom Listener to send to correct methods
private class MyReceiver extends MidiReceiver {
#Override
public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException {
byte command = (byte)(msg[0] & MidiConstants.STATUS_COMMAND_MASK);
int channel = (byte)(msg[0] & MidiConstants.STATUS_CHANNEL_MASK);
switch (command) {
case MidiConstants.STATUS_NOTE_ON:
activity.keyDown(i, msg[1], msg[2]);
break;
case MidiConstants.STATUS_NOTE_OFF:
activity.keyUp(channel, msg[1]);
break;
case MidiConstants.STATUS_POLYPHONIC_AFTERTOUCH:
activity.keyDown(channel, msg[1], msg[2]);
break;
case MidiConstants.STATUS_PITCH_BEND:
activity.pitchBendAction(channel, (msg[2] << 7) + msg[1]);
break;
case MidiConstants.STATUS_CONTROL_CHANGE:
activity.ccAction(channel, msg[1], msg[2]);
break;
case MidiConstants.STATUS_PROGRAM_CHANGE:
mProgram = msg[1];
break;
default:
break;
}
}
}
class MyRunnable implements Runnable {
#Override
public void run() {
do {
try {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
try {
processMidiEvents();
}
catch (Exception e) {
Log.e("Java", "SynthEngine background thread exception.", e);
}
}
});
Thread.sleep(100);
}
catch (InterruptedException e) {
Log.e("Java", "Threading exception", e);
}
}
while (go);
}
}
/**
* #throws IOException
*
*/
private void processMidiEvents() throws IOException {
long now = System.nanoTime();
MidiEventScheduler.MidiEvent event = (MidiEventScheduler.MidiEvent) eventScheduler.getNextEvent(now);
while (event != null) {
midiFramer.send(event.data, 0, event.count, event.getTimestamp());
eventScheduler.addEventToPool(event);
event = (MidiEventScheduler.MidiEvent) eventScheduler.getNextEvent(now);
}
}
public void start() {
stop();
go = true;
mThread = new Thread(new MyRunnable());
mThread.setPriority(6);
eventScheduler = new MidiEventScheduler();
mThread.start();
}
public void stop() {
go = false;
if (mThread != null) {
try {
mThread.interrupt();
mThread.join(500);
}
catch (Exception e) {
}
mThread = null;
eventScheduler = null;
}
}
}
Stack Trace Error (line 154 refers to the Thread.sleep part in my custom Runnable class):
Java: Threading exception
java.lang.InterruptedException
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:1031)
at java.lang.Thread.sleep(Thread.java:985)
at com.rfoo.midiapp.communication.MidiEngineInput$MyRunnable.run(MidiEngineInput.java:154)
at java.lang.Thread.run(Thread.java:818)
Thanks!
EDIT: Thread start
Midi Device Service subclass (thread will start whenever a device has connected or disconnected).
#TargetApi(Build.VERSION_CODES.M)
public class MidiSynthDeviceService extends MidiDeviceService {
private static final String TAG = "MidiSynthDeviceService";
private boolean midiStarted = false;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
AudioActivity.midiEngine.stop();
super.onDestroy();
}
#Override
// Declare the receivers associated with your input ports.
public MidiReceiver[] onGetInputPortReceivers() {
return new MidiReceiver[] { AudioActivity.midiEngine };
}
/**
* This will get called when clients connect or disconnect.
* You can use it to turn on your synth only when needed.
*/
#Override
public void onDeviceStatusChanged(MidiDeviceStatus status) {
if (status.isInputPortOpen(0) && !midiStarted) {
AudioActivity.midiEngine.start();
midiStarted = true;
} else if (!status.isInputPortOpen(0) && midiStarted){
AudioActivity.midiEngine.stop();
midiStarted = false;
}
}
}
Activity class:
public class AudioActivity extends AppCompatActivity {
private Thread thread;
public static MidiEngine midiEngine;
#Override
protected void onCreate(Bundle savedInstanceState) {
// Layout inits
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
// Setup MIDI:
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI)) {
Toast.makeText(this, "MIDI not supported!", Toast.LENGTH_LONG).show();
}
else {
midiEngine = new MidiEngine(this);
setupMidi();
}
// Setup audio thread:
if (thread == null) {
thread = new Thread() {
public void run() {
setPriority(Thread.MAX_PRIORITY);
// Runs an Open SL audio thread (C++)
// This generates a waveform.
// AudioEngine is a wrapper class connecting C++ to Java
AudioEngine.runProcess();
}
}
}
}
public void setupMidi() {
if (activity == null) activity = (AudioActivity) getContext();
mMidiManager = (MidiManager) activity.getSystemService(AudioActivity.MIDI_SERVICE);
if (mMidiManager == null) {
Toast.makeText(activity, "MidiManager is null!", Toast.LENGTH_LONG).show();
return;
}
// Get Device Info
MidiDeviceInfo deviceInfo = MidiTools.findDevice(mMidiManager, "RFOO", "AudioApp");
// MIDI Input
portIndex = 0;
inputPortSelector = new MidiOutputPortConnectionSelector(mMidiManager, activity, R.id
.inputListView, deviceInfo, portIndex);
inputPortSelector.setConnectedListener(new MyPortsConnectedListener());
midi_ch_input = 0;
midi_ch_output = 0;
}
// Bunch of UI code here....
}

Realm access from incorrect thread Android

I am dealing with this issue on Android :
Realm access from incorrect thread. Realm objects can only be accessed on the thread they where created.
I want to use Realm in my RemoteViewsFactory with
public class RemoteViewsX implements RemoteViewsFactory {
public RemoteViews getViewAt(int paramInt) {
if (this.results != null && this.results.size() != 0 && this.results.get(paramInt) != null) {
//FAILED HERE
}
}
...
This call failed! Why ?
I fetch my data like this in my class:
public void onDataSetChanged() {
Realm realm = Realm.getInstance(RemoteViewsX.this.ctx);
this.results = realm.where(Model.class).findAll();
}
I called my remoteFactory like this :
public class ScrollWidgetService extends RemoteViewsService {
#Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new RemoteViewsX (getApplicationContext());
}
}
Any idea ?
If the problem is caused by calling onDataSetChanged and getViewAt from another thread, you can force them to use the same thread, creating your own HandlerThread like this:
public class Lock {
private boolean isLocked;
public synchronized void lock() throws InterruptedException {
isLocked = true;
while (isLocked) {
wait();
}
}
public synchronized void unlock() {
isLocked = false;
notify();
}
}
public class MyHandlerThread extends HandlerThread {
private Handler mHandler;
public MyHandlerThread() {
super("MY_HANDLER_THREAD");
start();
mHandler = new Handler(getLooper());
}
public Handler getHandler() {
return mHandler;
}
}
public class RemoteViewsX implements RemoteViewsFactory {
private MyHandlerThread mHandlerThread;
...
}
public void onDataSetChanged() {
Lock lock = new Lock();
mHandlerThread.getHandler().post(new Runnable() {
#Override
public void run() {
Realm realm = Realm.getInstance(ctx);
results = realm.where(Model.class).findAll();
lock.unlock();
}
});
lock.lock();
}
public RemoteViews getViewAt(int paramInt) {
Lock lock = new Lock();
final RemoteViews[] result = {null};
mHandlerThread.getHandler().post(new Runnable() {
#Override
public void run() {
// You can safely access results here.
result[0] = new RemoteViews();
lock.unlock();
}
});
lock.lock();
return result[0];
}
I copied Lock class from this page: http://tutorials.jenkov.com/java-concurrency/locks.html
Do not forget to quit the handler thread when your tasks are done.
I am based on RxJava, so I do this in a realm. I clone each item because they are part of the main thread and that messes up when I am working in another thread such a widget home screen.
myRealm.where( Dog.class ).findAllAsync().subscribe( mainThreadDogs->{
thisThreadDogs.clear();
for( Dog dog: mainThreadDogs ){
thisThreadDogs.add( ModelUtil.cloneDog( dog ) );
}
});

Avoiding threads restarting when changing activity

Right now, when I change the activity, my thread seams to go to sleep or something. And when I come back to the main activity, there are two threads running, doing the same things. I'm not sure if this is the case, but it seems like it's something equal.
...
public class MainActivity extends Activity {
public static double cowCount = 195;
public static double income = 0.100;
static boolean twiceCow = false, Threadrunning = false;
...
public void inc() {
new Thread(new income()).start();
}
class income implements Runnable {
#Override
public void run() {
for (int i = 0; i < 20;) {
final int value = i;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
#Override
public void run() {
cowCount = cowCount + income;
refresh();
}
});
}
}
}
This is how my thread looks like.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new Handler();
checkThread();
}
private void checkThread() {
if (Threadrunning == false)
inc();
Threadrunning = true;
}
public void inc() {
new Thread(new income()).start();
}
...
public void refresh () {
TextView myTextView = (TextView)findViewById(R.id.myText);
myTextView.setText("You Have " + String.valueOf((nf.format(cowCount)) + " Cows!"));
}
I don't really understand what I've done wrong.
Please review this post: http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
Consider your activity re-start as the same thing as a config change.
This pattern, i.e. using a retained Fragment as a container for your thread, and proxying UI updates via callbacks to your activity, is a pattern that will work much better for you.
In your case you'd need only a single TaskCallback for your UI refresh(), e.g. onRefreshCowCount(int cows);

Categories