Android switching between gl views causes app freeze - java

I'm still learning how to program in Android and I created an Activity with a start view:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity);
mMainView = (RajawaliSurfaceView) findViewById(R.id.my_view);
mMainView.setSurfaceRenderer(new Renderer());
mMainView.setZOrderOnTop(false);
// Also set up a surface view I'd like to switch on back and forth
mGLView = new GLSurfaceView(this);
mGLView.setEGLContextClientVersion(2);
mGLView.setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR);
mGLView.setRenderer(cameraRenderer_ = new GLRenderer(this));
mGLView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
This works and I can even switch to the GL view seamlessly:
public void switchToGLView(View view) {
setContentView(mGLView);
}
But after I'm done with the GL thing, I have the GLRenderer call a restore function in my main activity
public void restoreRenderer() {
// Restoring the previous view (my_view) with the non-GL renderer
setContentView(mMainView); <<<<<< FREEZES HERE (blackscreen)
}
and unfortunately this never works: the screen remains black from the previous GL renderer and by debugging the setContentView line I discovered that:
I don't get ANY error or warning log in the Android Monitor
The execution NEVER continues. It gets stuck in there forever
Any tips or hints on how to solve this?

You could solve this problem using a Frame or Relative layout.
If I understand correctly, both mMainView and mGLView are fullscreen.
There are a few options:
You could change the visibility of the views:
public void switchViews() {
if(mMainView.getVisibility() == View.VISIBLE){
mMainView.setVisibility(View.GONE);
mGLView.setVisibility(View.VISIBLE);
} else{
mMainView.setVisibility(View.VISIBLE);
mGLView.setVisibility(View.GONE);
}
}
The other option is to change the Z order of the views:
public void switchViews(View view){
view.bringToFront();
view.invalidate();
}
Unless there are any drawbacks you encountered using the above methods.

Related

How to write code between onCreate and onStart?

Several times I've had problems writing code on onCreate(). Mostly because the UI has not been sized and laid out on the screen yet (even if I place my code at the end of the function). I've looked over the activity life-cycle to see if there's anything that runs after onCreate(). There is onStart(), but the problem is that onRestart() recalls onStart(), I don't want that. So is there a way to write code between onCreate() and onStart()? OR where should I write code that runs after the UI is placed and only runs once during its process?
Not sure what exactly you need but you can "cheat" and simply store whether you have run code or not:
private boolean mInit = false;
void onStart() {
if (!mInit) {
mInit = true;
// do one time init
}
// remaining regular onStart code
}
The other way of running code when UI is placed is to use the global layout listener:
public class FooActivity extends Activity implements ViewTreeObserver.OnGlobalLayoutListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_content);
View content = findViewById(android.R.id.content);
content.getViewTreeObserver().addOnGlobalLayoutListener(this);
}
#Override
public void onGlobalLayout() {
// unregister directly, just interested once.
View content = findViewById(android.R.id.content);
content.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// do things here.
}
}

Call a method when fragment is visible to user

I need execute a method when the fragment is visible (to the user).
Example:
I have 2 buttons (button 1 and button 2) ,
2 fragments(fragment 1 and fragment 2)
and the method loadImages() inside the class fragment 2.
when I press "button2" I want to replace fragment 1 by fragment 2
and then after the fragment 2 is visible (to the user) call loadImages().
I tried to use onResume() in the fragment class but it calls the method before the fragment is visible and it makes some delay to the transition.
I tried setUserVisibleHint() too and did not work.
A good example is the Instagram app. when you click on profile it loads the profile activity first and then import all the images.
I hope someone can help me. I will appreciate your help so much. Thank you.
Use the ViewTreeObserver callbacks:
#Override
public void onViewCreated(View v, Bundle savedInstanceState) {
super.onViewCreated(v, savedInstanceState);
final View view = v;
// Add a callback to be invoked when the view is drawn
view.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
#Override
public void onDraw() {
// Immediately detach the listener so it only is called once
view.getViewTreeObserver().removeOnDrawListener(this);
// You're visible! Do your stuff.
loadImages();
}
});
}
I'm a little confused by what you are trying to do. It sounds like the images are loading too fast for you... so does that mean that you have the images ready to display? And that is a bad thing?
My guess (and this is just a guess) is that Instagram does not have the profile pictures in memory, so they have to make an API call to retrieve them, which is why they show up on a delay. If the same is the case for you, consider starting an AsyncTask in the onResume method of the fragment. Do whatever loading you need to do for the images in the background, and then make the images appear in the onPostExecute callback on the main thread. Make sure you only start the task if the images are not already loaded.
However, if you already have the images loaded in memory, and you just want a delay before they appear to the user, then you can do a postDelayed method on Handler. Something like this:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
loadImages();
}
}, 1000);
Edit
As kcoppock points out, the handler code is pretty bad. I meant it to be a quick example, but it is so wrong I should not have included it in the first place. A more complete answer would be:
private Handler handler;
public void onResume(){
super.onResume();
if(handler == null){
handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
loadImages();
}
}, 1000);
}
}
public void onDestroyView(){
super.onDestroyView();
handler.removeCallbacksAndMessages(null);
handler = null;
}
Use the onActivityCreated() callBck

Android, call method in UI thread after restarting Activity due to configuration change (device rotation) does nothing

SITUATION:
An application with resources for portait and landscape, has a simulator that I keep after configuration changes (the user can switch orientation while the simulation is running).
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Installer.installApkData(this);
simulator = new Simulator(this);
MainActivity prevActivity = (MainActivity)getLastCustomNonConfigurationInstance();
if(prevActivity!= null) {
// So the orientation did change
// Restore some field for example
this.simulator = prevActivity.simulator;
//this.mNavigationDrawerFragment = prevActivity.mNavigationDrawerFragment;
//this.mTitle = prevActivity.mTitle;
Log.d("APP","Activity restarted: simulator recreated");
}
requestWindowFeature(Window.FEATURE_PROGRESS);
setContentView(R.layout.activity_main);
setProgressBarVisibility(true);
mNavigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager()
.findFragmentById(R.id.navigation_drawer);
mTitle = getTitle();
// Set up the drawer.
mNavigationDrawerFragment.setUp(R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
}
#Override
public Object onRetainCustomNonConfigurationInstance() {
//restore all your data here
return this;
}
...
There is a method in the activity that changes the selected section in the NavigationDrawer, in the UI thread because if not it crashes.
public void showHud() {
// TODO Auto-generated method stub
runOnUiThread( new Runnable() {
public void run() {
mNavigationDrawerFragment.select(1);
onSectionAttached(2);
restoreActionBar();
}
});
}
This method is used to go directly to display the simulation once the simulator has been connected.
PROBLEM:
All this system works except for when I connect the simulator after switching the orientation. It executes the runOnUiThread but it does nothing. I think the reason for that is that it loses the UI thread that created that view when the activity is restarted.
As you can see there are two lines commented in the reloading of the simulator where I also tried to save the NavigationDrawer object without success in the test: same behavior.
I also tried to save the prevActivity and in the method showHUD(), first asking if its null and if not, execute the method inside the prevActivity. Expecting that it will access the original UI Thread, but I was mistaken.
Is there any solution to keep this UI Thread during the restarting of an activity? or maybe another type of solution?
Thanks a lot.
You should be checking your onSavedInstanceState in your Activity. This is how the Android OS is designed to handle this. You are trying to do this yourself, when you should be relying on the OS supplied functionality.
Quite a few examples of this (if you search SO):
Android: Efficient Screen Rotation Handling
Handle screen rotation without losing data - Android
If you want to save configuration, you need to save specific things. You can do this in the onPause() or on onSaveInstanceState().
If onCreate() is called after your configuration change, you can get what you need back out of the bundle. when you get it back out, you can then set what you need.
See this: http://developer.android.com/training/basics/activity-lifecycle/recreating.html
I am correctly retaining the data object but after a device rotation the function in UI thread has no effect, a function to change the selected section in the NavigationDrawer. I thought it was because I was losing the correct UI thread but actually, what I was losing is this NavigationDrawerFragment.
Just by adding the setRetainInstance(true) line in the OnCreate() of the NavigationDrawerFragment solves the problem:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
...

SurfaceView drawn on top of other elements after coming back from specific activity

I have an activity with video preview displayed via SurfaceView and other views positioned over it. The problem is when user navigates to Settings activity (code below) and comes back then the surfaceview is drawn on top of everything else. This does not happen when user goes to another activity I have, neither when user navigates outside of app eg. to task manager.
Now, you see in code below that I have setContentVIew() call wrapped in conditionals so it is not called every time when onStart() is executed. If its not wrapped in if statements then all works fine, but its causing loosing lots of memory (5MB+) each time onStart() is called. I tried various combinations and nothing seems to work so any help would be much appreciated.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Toast.makeText(this,"Create ", 2000).show();
// set 32 bit window (draw correctly transparent images)
getWindow().getAttributes().format = android.graphics.PixelFormat.RGBA_8888;
// set the layout of the screen based on preferences of the user
sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
}
public void onStart()
{
super.onStart();
String syncConnPref = null;
syncConnPref = sharedPref.getString("screensLayouts", "default");
if(syncConnPref.contentEquals("default") && currentlLayout!="default")
{
setContentView(R.layout.fight_recorder_default);
}
else if(syncConnPref.contentEquals("simple") && currentlLayout!="simple")
{
setContentView(R.layout.fight_recorder_simple);
}
// I I uncomment line below so it will be called every time without conditionals above, it works fine but every time onStart() is called I'm losing 5+ MB memory (memory leak?). The preview however shows under the other elements exactly as I need memory leak makes it unusable after few times though
// setContentView(R.layout.fight_recorder_default);
if(getCamera()==null)
{
Toast.makeText(this,"Sorry, camera is not available and fight recording will not be permanently stored",2000).show();
// TODO also in here put some code replacing the background with something nice
return;
}
// now we have camera ready and we need surface to display picture from camera on so
// we instantiate CameraPreviw object which is simply surfaceView containing holder object.
// holder object is the surface where the image will be drawn onto
// this is where camera live cameraPreview will be displayed
cameraPreviewLayout = (FrameLayout) findViewById(id.camera_preview);
cameraPreview = new CameraPreview(this);
// now we add surface view to layout
cameraPreviewLayout.removeAllViews();
cameraPreviewLayout.addView(cameraPreview);
// get layouts prepared for different elements (views)
// this is whole recording screen, as big as screen available
recordingScreenLayout=(FrameLayout) findViewById(R.id.recording_screen);
// this is used to display sores as they are added, it displays like a path
// each score added is a new text view simply and as user undos these are removed one by one
allScoresLayout=(LinearLayout) findViewById(R.id.all_scores);
// layout prepared for controls like record/stop buttons etc
startStopLayout=(RelativeLayout) findViewById(R.id.start_stop_layout);
// set up timer so it can be turned on when needed
//fightTimer=new FightTimer(this);
fightTimer = (FightTimer) findViewById(id.fight_timer);
// get views for displaying scores
score1=(TextView) findViewById(id.score1);
score2=(TextView) findViewById(id.score2);
advantages1=(TextView) findViewById(id.advantages1);
advantages2=(TextView) findViewById(id.advantages2);
penalties1=(TextView) findViewById(id.penalties1);
penalties2=(TextView) findViewById(id.penalties2);
RelativeLayout welcomeScreen=(RelativeLayout) findViewById(id.welcome_screen);
Animation fadeIn = AnimationUtils.loadAnimation(this, R.anim.fade_in);
welcomeScreen.startAnimation(fadeIn);
Toast.makeText(this,"Start ", 2000).show();
animateViews();
}
Settings activity is below, after coming back from this activity surfaceview is drawn on top of other elements.
public class SettingsActivity extends PreferenceActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(MyFirstAppActivity.getCamera()==null)
{
Toast.makeText(this,"Sorry, camera is not available",2000).show();
return;
}
addPreferencesFromResource(R.xml.preferences);
}
}

How do I handle screen orientation changes when a dialog is open?

I have an android app which is already handling changes for orientation, i.e. there is a android:configChanges="orientation" in the manifest and an onConfigurationChange() handler in the activity that switches to the appropriate layout and preps it. I have a landscape / portrait version of the layout.
The problem I face is that the activity has a dialog which could be open when the user rotates the device orientation. I also have a landscape / portrait version of the dialog.
Should I go about changing the layout of the dialog on the fly or perhaps locking the activity's rotation until the user dismisses the dialog.
The latter option of locking the app appeals to me since it saves having to do anything special in the dialog. I am supposing that I might disable the orientation when a dialog opens, such as
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
and then when it dismisses
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
Would that be a sensible thing to do? If the screen orientation did change while it was locked, would it immediately sense the orientation change when it was unlocked?
Are there alternatives?
I would recommend not turning off the screen rotation, instead of this handle the configuration changes for the Dialog. You could use one of these two approach for this:
The first one is using a flag variable in onSaveInstanceState(outState) method, and restore the dialog onCreate(bundle) method:
in this example my flag variable is called 'isShowing Dialog', when the onCreate method is called by the android System for first time, the bundle argument will be null and nothing happens. However when the activity it's recreated by a configuration change (screen rotation), the bundle will have the boolean value isShowing Dialog, previously saved by the inSaveInstanceState(...) method, so if the variable gets true the dialog is created again, the trick here is set the flag in true when the dialog get showing, and false when it's not, is a little but simple trick.
Class MyClass extends Activity {
Boolean isShowingDialog = false;
AlertDialog myDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
if(savedInstanceState!=null){
isShowingDialog = savedInstanceState.getBoolean("IS_SHOWING_DIALOG", false);
if(isShowingDialog){
createDialog();
}
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putBoolean("IS_SHOWING_DIALOG", isShowingDialog);
super.onSaveInstanceState(outState);
}
#Override
protected void onPause() {
if(myDialog!=null && myDialog.isShowing()) {
myDialog.dismiss();
}
}
private void createDialog() {
AlertDialog.Builder dialog_builder = new AlertDialog.Builder(this);
dialog_builder.setTitle("Some Title"):
... more dialog settings ...
myDialog = dialog_builder.create();
myDialog.show();
isShowingDialog = true;
}
private void hideDialog(){
myDialog.dismiss();
isShowingDialog = false;
}
}
The second approach is to use the ability of the fragments components to retain its states, the main idea is create the dialog inside a fragment, there is the problem about detach and reattach the fragment during the configuration changes (because you need dismiss and show the dialog correctly), but the solution is very similar to the first approach. The advantage of this approach is that if you have an AlertDialog with a couple of configurations, when the fragment is recreated there is not needed to create and setting up the dialog again, only make it show() and the AlertDialog state is maintained by the fragment.
I hope this helps.
I suggest your Dialog should override onSaveInstanceState() and onRestoreInstanceState(Bundle) to save its state into a Bundle.
You then override those methods in your Activity, checking if the Dialog is shown and if so - calling the dialog's methods to save and restore it's state.
If you are displaying this dialog from a fragment, you will want to override OnActivityCreated(Bundle) instead of OnRestoreInstanceState.
For a source example see the built-in clock app provided with Android, where the SetAlarm Activity handles the TimePickerDialog this way.
If you are handling orientation changes yourself, then here is an approach.
I won't claim that this is an elegant solution, but it works:
You can keep track of whether the dialog has an active instance inside the dialog class itself, by using a static variable activeInstance, and overriding onStart() to set activeInstance = this and onCancel() to set activeInstance = null.
Provide a static method updateConfigurationForAnyCurrentInstance() that tests that activeInstance variable and, if non-null, invokes a method activeInstance.reInitializeDialog(), which is a method that you will write to contain the setContentView() call plus the code that wires the handlers for the dialog controls (button onClick handlers, etc. - this is code that would normally appear in onCreate()). Following that, you would restore any displayed data to those controls (from member variables in your dialog object). So, for example, if you had a list of items to be viewed, and the user were viewing item three of that list before the orientation change, you would re-display that same item three at the end of updateConfigurationForAnyCurrentInstance(), right after re-loading the controls from the dialog resource and re-wiring the control handlers.
You would then call that same reInitializeDialog() method from onCreate(), right after super.onCreate(), and place your onCreate()-specific initialization code (e.g., setting up the list of items from which the user could choose, as described above) after that call.
This will cause the appropriate resource (portrait or landscape) for the dialog's new orientation to be loaded (provided that you have two resources defined having the same name, one in the layout folder and the other in the layout-land folder, as usual).
Here's some code that would be in a class called YourDialog:
ArrayList<String> listOfPossibleChoices = null;
int currentUserChoice = 0;
static private YourDialog activeInstance = null;
#Override
protected void onStart() {
super.onStart();
activeInstance = this;
}
#Override
public void cancel() {
super.cancel();
activeInstance = null;
}
static public void updateConfigurationForAnyCurrentInstance() {
if(activeInstance != null) {
activeInstance.reInitializeDialog();
displayCurrentUserChoice();
}
}
private void reInitializeDialog() {
setContentView(R.layout.your_dialog);
btnClose = (Button) findViewById(R.id.btnClose);
btnClose.setOnClickListener(this);
btnNextChoice = (Button) findViewById(R.id.btnNextChoice);
btnNextChoice.setOnClickListener(this);
btnPriorChoice = (Button) findViewById(R.id.btnPriorChoice);
btnPriorChoice.setOnClickListener(this);
tvCurrentChoice = (TextView) findViewById(R.id.tvCurrentChoice);
}
private void displayCurrentUserChoice() {
tvCurrentChoice.setText(listOfPossibleChoices.get(currentUserChoice));
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
reInitializeDialog();
listOfPossibleChoices = new ArrayList<String>();
listOfPossibleChoices.add("One");
listOfPossibleChoices.add("Two");
listOfPossibleChoices.add("Three");
currentUserChoice = 0;
displayCurrentUserChoice();
}
#Override
public void onClick(View v) {
int viewID = v.getId();
if(viewID == R.id.btnNextChoice) {
if(currentUserChoice < (listOfPossibleChoices.size() - 1))
currentUserChoice++;
displayCurrentUserChoice();
}
}
else if(viewID == R.id.btnPriorChoice) {
if(currentUserChoice > 0) {
currentUserChoice--;
displayCurrentUserChoice();
}
}
Etc.
Then, in your main activity's onConfigurationChanged() method, you would just invoke YourDialog.updateConfigurationForAnyCurrentInstance() whenever onConfigurationChanged() is called by the OS.
Doesn't seem the title was ever resolved (Google Necro Direct).
Here is the solution, matching the request.
When your activity is created, log the screen orientation value.
when onConfiguration change is called on your activity, compare the orientation values. if the values don't match, fire off all of your orientation change listeners, THEN record the new orientation value.
Here is some constructive code to put in your activity (or any object that can handle configuration change events)
int orientation; // TODO: record orientation here in your on create using Activity.this.getRequestedOrientation() to initialize!
public int getOrientation(){return orientation;}
public interface OrientationChangeListener {
void onOrientationChange();
}
Stack<OrientationChangeListener> orientationChangeListeners = new Stack<>();
public void addOrientationChangeListener(OrientationChangeListener ocl){ ... }
public void removeOrientationChangeListener(OrientationChangeListener ocl){ ... }
That's the basic environment. Here's your executive:
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (orientation != newConfig.orientation)
for (OrientationChangeListener ocl:orientationChangeListeners) ocl.onOrientationChange();
orientation = newConfig.orientation;
}
In YOUR code model, you may need to send the new configuration, with the event, or the two orientation values with the event. However, Activity.this.getOrientation() != Activity.this.getRequestedOrientation() during event handling (because we are in a logical state of change between two logical values).
In review of my post, i have determined that there could be some synchronization issues, with multiple events! This is not a fault of this code, but a fault of "Android Platform" for not having defacto orientation sense handlers on every window, thusly trashing the polymorphic benefits of using java in the first place..
See the answer from Viktor Valencia above. That will work perfectly with the slight adjustment that you move the createDialog() to onResume.
#Override
protected void onResume() {
super.onResume();
if(isShowingDialog){
createDialog();
}
}
Fetch the boolean isShowingDialog value at onCreate, as suggested, but wait for onResume to display the dialog.

Categories

Resources