Here is an alternative solution for all callback methods, that may all be subject to the same undefined event order behavior with activity cycle. Unless you going to inspect all the android code for each call back you use to determine the origin trigger and who controlling the implementations and hope that the code base doesn't changed in the future, can one really state, that the event order between callsbacks and activity life cycle events could be guaranteed.
Right now these interactions of order can typically be referred to as undefined behavior, for development purposes.
So best would be to always correctly handle this undefined behavior, such that
it will never be a problem in the first place, by ensure the orders are defined behavior.
My Sony Xperia for instance, on sleep, cycles my current app, by destroying the app and then restarting it and puts it into the pause state, believe it or not.
How much event ordering behavior testing google provides in their SDK as special test build for host environment implements I don't know, but they definitely need to make an effort to ensure, behaviors of event orders are all locked down by being rather strict on the matter.
https://code.google.com/p/android/issues/detail?id=214171&sort=-opened&colspec=ID%20Status%20Priority%20Owner%20Summary%20Stars%20Reporter%20Opened
import android.util.Log;
import android.util.SparseArray;
/**
* Created by woliver on 2016/06/24.
*
* Android host environment, dictates an Activity Life Cycle for OnCreate, onStart, onResume, onPause, onStop, onDestory,
* where by we are require to release memory and handles for other applications to use.
* When resume we are required at times to rebind and activate these items with other objects.
* Typically these other objects provide callback methods from the host enviroment which provide
* an onCreated and onDestroy, in which we can only bind to this object from OnCreated and and loose
* out bind onDestory.
* These types of call back methods, shedual time to run is controller by our host enviroment
* and their are no guarantees to that the behaviour/order of execution of the Activity Life Cycle and these call back methods
* remains consistent.
* For the purpose of development the interactions and order of execution can technically be called undefined
* as it is up to the host implementation implementer, samsung, sony, htc.
*
* See following developer document: https://developer.android.com/reference/android/app/Activity.html
* Quote:
* If an activity is completely obscured by another activity, it is stopped. It still retains all state
* and member information, however, it is no longer visible to the user so its window is
* hidden and it will often be killed by the system when memory is needed elsewhere.
* EndQuato:
*
* If the activity is not hidden, then any callbacks that one would have expected to have been call by the host
* system, will not have been called, such as OnCreate and OnDestory methods interface SurfaceView callback.
* This means that you will have to stop the object that has been binded to SurfaceView such as a camera
* in pause and will never rebind the object as the OnCreate callback will never be called.
*
*/
public abstract class WaitAllActiveExecuter<Size>
{
private SparseArray<Boolean> mReferancesState = null;
// Use a dictionary and not just a counter, as hosted code
// environment implementer may make a mistake and then may double executes things.
private int mAllActiveCount = 0;
private String mContextStr;
public WaitAllActiveExecuter(String contextStr, int... identifiers)
{
mReferancesState = new SparseArray<Boolean>(identifiers.length);
mContextStr = contextStr;
for (int i = 0; i < identifiers.length; i++)
mReferancesState.put(identifiers[i], false);
}
public void ActiveState(int identifier)
{
Boolean state = mReferancesState.get(identifier);
if (state == null)
{
// Typically panic here referance was not registered here.
throw new IllegalStateException(mContextStr + "ActiveState: Identifier not found '" + identifier + "'");
}
else if(state == false){
mReferancesState.put(identifier, true);
mAllActiveCount++;
if (mAllActiveCount == mReferancesState.size())
RunActive();
}
else
{
Log.e(mContextStr, "ActivateState: called to many times for identifier '" + identifier + "'");
// Typically panic here and output a log message.
}
}
public void DeactiveState(int identifier)
{
Boolean state = mReferancesState.get(identifier);
if (state == null)
{
// Typically panic here referance was not registered here.
throw new IllegalStateException(mContextStr + "DeActiveState: Identifier not found '" + identifier + "'");
}
else if(state == true){
if (mAllActiveCount == mReferancesState.size())
RunDeActive();
mReferancesState.put(identifier, false);
mAllActiveCount--;
}
else
{
Log.e(mContextStr,"DeActiveState: State called to many times for identifier'" + identifier + "'");
// Typically panic here and output a log message.
}
}
private void RunActive()
{
Log.v(mContextStr, "Executing Activate");
ExecuterActive();
}
private void RunDeActive()
{
Log.v(mContextStr, "Executing DeActivate");
ExecuterDeActive();
}
abstract public void ExecuterActive();
abstract public void ExecuterDeActive();
}
Example of Implementation and use of class, which deals with or the undefined behaviour of android host enviroment
implementers.
private final int mBCTSV_SurfaceViewIdentifier = 1;
private final int mBCTSV_CameraIdentifier = 2;
private WaitAllActiveExecuter mBindCameraToSurfaceView =
new WaitAllActiveExecuter("BindCameraToSurfaceViewe", new int[]{mBCTSV_SurfaceViewIdentifier, mBCTSV_CameraIdentifier})
{
@Override
public void ExecuterActive() {
// Open a handle to the camera, if not open yet and the SurfaceView is already intialized.
if (mCamera == null)
{
mCamera = Camera.open(mCameraIDUsed);
if (mCamera == null)
throw new RuntimeException("Camera could not open");
// Look at reducing the calls in the following two methods, some this is unessary.
setDefaultCameraParameters(mCamera);
setPreviewSizesForCameraFromSurfaceHolder(getSurfaceHolderForCameraPreview());
}
// Bind the Camera to the SurfaceView.
try {
mCamera.startPreview();
mCamera.setPreviewDisplay(getSurfaceHolderForCameraPreview());
} catch (IOException e) {
e.printStackTrace();
ExecuterDeActive();
throw new RuntimeException("Camera preview could not be set");
}
}
@Override
public void ExecuterDeActive() {
if ( mCamera != null )
{
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
};
@Override
protected void onPause() {
mBindCameraToSurfaceView.DeactiveState(mBCTSV_CameraIdentifier);
Log.v(LOG_TAG, "Activity Paused - After Super");
}
@Override
public void onResume() {
mBindCameraToSurfaceView.ActiveState(mBCTSV_CameraIdentifier);
}
private class SurfaceHolderCallback implements SurfaceHolder.Callback
{
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Log.v(LOG_TAG, "Surface Changed");
}
public void surfaceCreated(SurfaceHolder surfaceHolder) {
Log.v(LOG_TAG, "Surface Created");
mBindCameraToSurfaceView.ActiveState(mBCTSV_SurfaceViewIdentifier);
}
public void surfaceDestroyed(SurfaceHolder arg0) {
Log.v(LOG_TAG, "Surface Destoryed");
mBindCameraToSurfaceView.DeactiveState(mBCTSV_SurfaceViewIdentifier);
}
}