Obsolete
Status Update
Comments
jo...@google.com <jo...@google.com>
sa...@google.com <sa...@google.com> #2
Information redacted by Android Beta Feedback.
No update yet.
Information redacted by Android Beta Feedback.
Description
I would like to suggest, that the documents be modified and explicit example
be added to prevent average program stumbling into this problem, if he has not test his application in 100 different host environments.
Heading:
Heading Activity Life Cycle
Add additional detail to the following: Given describing undefined event order of callbacks, such as SurfaceView:
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.
Below find an example abstracts class that allows for guarantee order of execution and allowing for correct binding.
Then under that a basic example implementation.
I have not take a look into the thread execution order guaranties,
but understand this to be executing on the current UI thread.
If other words, if the callback is executed on the UI thread.
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 hose 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:
* 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 Map<Object, Boolean> mReferancesState = new HashMap<Object, Boolean>();
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();
// Typically this can be called in OnStop, but check the manual as other may actually need to use the camera.
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) {
}
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);
}
}