Gandalf StormCrow
Gandalf StormCrow

Reputation: 26202

Is is possible to make a method execute only once?

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?

Upvotes: 24

Views: 112782

Answers (14)

El-rains
El-rains

Reputation: 21

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);
        }
    }


}

Upvotes: 0

Apix_D
Apix_D

Reputation: 1101

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 :)

Upvotes: 0

Hover Ruan
Hover Ruan

Reputation: 3475

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());

Upvotes: 20

Paramjit Singh Rana
Paramjit Singh Rana

Reputation: 809

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 
}

Upvotes: 1

Jesse Guerrero
Jesse Guerrero

Reputation: 1

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;
    }

}

Upvotes: 0

Salix alba
Salix alba

Reputation: 7824

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.

Upvotes: 1

JIE WANG
JIE WANG

Reputation: 1934

if you use kotlin, you can do this:

val execOnce by lazy {
   print("hello, world")
}

Upvotes: 13

mkrufky
mkrufky

Reputation: 3388

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;
  });
}

Upvotes: 8

Thiago
Thiago

Reputation: 13302

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);
       }

Upvotes: 1

Ranga Reddy
Ranga Reddy

Reputation: 3066

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);
        }
    }
}

Upvotes: -1

Mike
Mike

Reputation: 71

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)
}

Upvotes: 6

Aaron Digulla
Aaron Digulla

Reputation: 328594

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();
        ...
    }
}

Upvotes: 6

Rosdi Kasim
Rosdi Kasim

Reputation: 25966

Sure!..

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

Upvotes: 67

Paul Whelan
Paul Whelan

Reputation: 16809

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

Upvotes: 1

Related Questions