0

how to start method B()(main thread) inside onResume() after method A()(work thread) finished inside onStart()

I have loadCards() inside onStart(), and initViews() inside onResume().

Need to run initViews() only after loadCards() finished, loadCards() is a long-running operation, and have a callback().

Current problem is initViews() runs before loadCards() finished, so get a null pointer.

Would like to get help: how to run initViews()(inside onResume) only after loadCards()(inside onStart) has finished?

@Override
protected void onStart() {
    super.onStart();

    loadCards(new Callback(){
        success(List<Card> cardList){do something}
        fail(){do other thing}
    });
}

@Override
protected void onResume() {
    super.onResume();

    //my problem is here: 
    //how to know if loadCards() has finished? then run initViews.

    initViews();
}

public void loadCards(Callback callback) {
    Runnable runnable = () -> {

        //List<Card> cardList = get list from work thread;

        mAppExecutors.mainThread().execute(() -> {
            if (result == empty) {
                callback.fail();
            } else {
                callback.success(cardList);
            }
        });
    };

    mAppExecutors.diskIO().execute(runnable);
}

void initViews(){}

expected: after loadCards() has finished, run initViews().

actual: when loadCards() is still running, initViews() runs.

caibirdcnb
  • 275
  • 5
  • 18

2 Answers2

1

Just put your initViews(); inside success like this

@Override
protected void onStart() {
    super.onStart();

    loadCards(new Callback(){
        success(List<Card> cardList){initViews();}
        fail(){do other thing}
    });
}
Bach Vu
  • 2,298
  • 1
  • 15
  • 19
  • It is just an example, I have more methods besides initViews(), they need to run one by one after loadCards has finished, some of them require onResume condition. Use your answer, I need to put all methods inside success() callback. – caibirdcnb Feb 11 '19 at 04:46
  • Yes, this is actually the standard way of doing it, put everything in your success – Bach Vu Feb 11 '19 at 04:55
  • But what if initViews() does requires to run inside onResume(), then how to do? Thanks. – caibirdcnb Feb 11 '19 at 06:11
  • Then you need to show some default views and then when your `loadCards` finishes, you can update those views – Bach Vu Feb 11 '19 at 06:12
  • No,no, the question was: how to run a method both after loadCards() and inside onResume() ? (loadCards is inside onStart) Current I know only how to run a method either after loadCards() or inside onResume(). – caibirdcnb Feb 11 '19 at 06:48
  • You can't, it's the Android lifecycle flow, and you cannot modify that – Bach Vu Feb 11 '19 at 07:36
  • ok, let me modify the condition: if loadCards() running time is from whole onStart to partial onResume, and assume onResume exists for a long enough time, then how to run a method after loadCards has finished plus inside onResume? – caibirdcnb Feb 11 '19 at 16:04
  • I think you getting the wrong conception regarding the activity life cycle, please do more research on this and then you can figure it all out https://developer.android.com/guide/components/activities/activity-lifecycle – Bach Vu Feb 12 '19 at 03:13
  • I used Handler solve this: after loadCards() finished, send a message, and handleMessage inside onResume. – caibirdcnb Feb 12 '19 at 17:36
0

You can use https://github.com/EliyahuShwartz/ConditionalTasksRunner to run a specific task when specific condition is met ( e.g when the activity is resumed )

This code was designed for running view task from the another class( Usually presenter) without be ware to android lifecycle.

the basic implementation will be

   private val mResumePendingTasks: ConditionalTasksRunner = object : ConditionalTasksRunner() {
        override val isConditionMet = isResumed
    }

and then you will be able to post task from anywhere you want inside the decleration class.

mResumePendingTasks.runOnConditionMet(Runnable { onListLoadedSuccessfully() })

The source code of ConditionalTasksRunner is :

package conditionaltasksrunner.com

import android.os.Handler
import android.os.Looper
import java.util.*

/**
 * Run tasks when a specific condition is met [.isConditionMet]
 * this tasks will run on the [Handler] that create the constructor
 */
abstract class ConditionalTasksRunner {

    private var mPendingTask: MutableSet<Runnable>
    private var mHandler: Handler

    /**
     * @return true if condition is met
     */
    abstract val isConditionMet: Boolean

    protected constructor() {
        mPendingTask = LinkedHashSet()
        mHandler = Handler()
    }

    protected constructor(handler: Handler) {
        mPendingTask = LinkedHashSet()
        mHandler = handler
    }

    /**
     * Run the given task when condition is met.
     * if the condition is met already than the task will invoke immediately
     *
     * @param runnable task to run
     */
    @Synchronized
    fun runOnConditionMet(runnable: Runnable) {
        if (isConditionMet) {
            if (Looper.myLooper() == mHandler.looper)
                runnable.run()
            else
                mHandler.post { runOnConditionMet(runnable) }
        } else {
            mPendingTask.add(runnable)
        }
    }

    /**
     * Remove the task from the pending task queue
     *
     * @param runnable the task to remove
     * @return true if the task was removed successfully
     */
    @Synchronized
    fun remove(runnable: Runnable): Boolean {
        mHandler.removeCallbacks(runnable)
        return mPendingTask.remove(runnable)
    }

    /**
     * Should be called when the condition is met
     */
    @Synchronized
    fun onConditionMet() {
        if (Looper.myLooper() == mHandler.looper) {
            val iterator = mPendingTask.iterator()

            while (iterator.hasNext()) {
                iterator.next().run()
                iterator.remove()
            }
        } else {
            mHandler.post { this.onConditionMet() }
        }
    }

    /**
     * Clear all the pending tasks and remove them from the handler queue
     */
    @Synchronized
    fun clear() {
        for (runnable in mPendingTask)
            mHandler.removeCallbacks(runnable)

        mPendingTask.clear()
    }
}

Please look at the source code for complete sample.