Firing an event as soon as activity is visible to the user - android

I wanted to create countdown as soon as the user sees an activity. However, there does not seem to be an appropriate callback for that. Neither onResume nor onWindowFocusChanged seem to be the correct callbacks, because the whole code is executed before the user even see anything. As a result I end up with "Go!" on the screen right from the start.
In a nutshell:
Do you have any idea how to implement a countdown without any user interaction as soon as the activity is visible to the user?
EDIT:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import java.util.concurrent.TimeUnit;
import android.widget.TextView;
public class ChallengeModeTutorial extends AppCompatActivity {
private void delayOneSec()
{
try
{
TimeUnit.SECONDS.sleep(2);
}
catch (InterruptedException e)
{
assert true;
}
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_challenge_mode_tutorial);
}
#Override
public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);
TextView readySteadyGo = (TextView) findViewById(R.id.challengeModeTutorialReadySteadyGoTextView);
// TextView tutorialText = (TextView) findViewById(R.id.challengeModeTutorialTextTextView);
TextView timeUntilStart = (TextView) findViewById(R.id.challengeModeTutorialReadyTimeTextView);
readySteadyGo.setText("");
timeUntilStart.setText("5");
delayOneSec();
timeUntilStart.setText("4");
delayOneSec();
timeUntilStart.setText("3");
delayOneSec();
readySteadyGo.setText("Ready!");
timeUntilStart.setText("2");
delayOneSec();
readySteadyGo.setText("Steady!");
timeUntilStart.setText("1");
delayOneSec();
readySteadyGo.setText("");
readySteadyGo.setText("Go!");
}
}

The problem is that you're blocking the UI thread. When you call setText(String) it doesn't immediately gets drawn. The TextView gets invalidated and it will be draw on the next draw phase. But if you block the thread, this will never happen. You have to use a postDelayed() to execute the next setText(String) a second later.

I'm not sure if this works, but you could try using a ViewTreeObserver in onCreate. It gets called once the layout is drawn. (Replace LinearLayout with whatever Layout your "activity_challenge_mode_tutorial" actually is.)
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_challenge_mode_tutorial);
final LinearLayout layout = (LinearLayout) findViewById(R.id.YOUR_VIEW_ID);
ViewTreeObserver vto = layout.getViewTreeObserver();
vto.addOnGlobalLayoutListener (new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// Remove listener to prevent repeat calls.
layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// Start countdown here.
}
});
}
Code is taken from this answer: https://stackoverflow.com/a/7735122/8118328

You can start your countdown, inside of your onStart method, which you need to overwrite.
The visible lifetime of an activity happens between a call to onStart() until a corresponding call to onStop(). During this time the user can see the activity on-screen, though it may not be in the foreground and interacting with the user.
From:
https://developer.android.com/reference/android/app/Activity
#Override
protected void onStart() {
super.onStart();
}

I finally got it to work like this:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class ChallengeModeTutorial extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_challenge_mode_tutorial);
}
#Override
protected void onStart()
{
super.onStart();
final TextView readySteadyGo = (TextView) findViewById(R.id.challengeModeTutorialReadySteadyGoTextView);
final TextView timeUntilStart = (TextView) findViewById(R.id.challengeModeTutorialReadyTimeTextView);
readySteadyGo.setText("");
Thread t=new Thread(){
#Override
public void run(){
while(continueThread){
try {
Thread.sleep(1000);
runOnUiThread(new Runnable() {
#Override
public void run() {
if(currentCount > 0)
{
timeUntilStart.setText(String.valueOf(currentCount));
}
else
{
timeUntilStart.setText("Go!");
}
switch (currentCount)
{
case 2: readySteadyGo.setText("Ready!"); break;
case 1: readySteadyGo.setText("Steady!"); break;
default: readySteadyGo.setText(""); break;
}
currentCount--;
if (currentCount == 0)
{
continueThread = false;
}
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
}
private boolean continueThread = true;
private int currentCount = 5;
}

Related

No text appears on TextView

This is my code, it should print a random value (between 0 and 1) but it doesn't do that!
I don't know how to fix it! I tried multiple things, none of them are working out!
Here's the code:
package com.example.lode.coder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.TextView;
import java.util.Random;
public class Coder extends AppCompatActivity {
TextView display;
String one;
boolean bl= true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_coder);
display = (TextView) findViewById(R.id.text);
}
void all() {
while (bl) {
Random rand = new Random();
int n = rand.nextInt(2);
one = n + (String) display.getText();
display.setText(one);
}
}
}
You don't show where the all() method is called. If it's not called anywhere, that would explain why display never gets any text set.
But assuming that it is called somewhere, there's still a big problem: the all() method is an infinite loop. Until you return control to the framework, any changes you make to display won't show up on the screen. (In fact, your app will likely then be killed off by the framework when it notices that the app has become unresponsive.)
If you want to change the text continuously, look into using a Handler. You can create a Runnable that does the actual change and then reschedules itself to run again after a short time. Don't use a loop like you currently have in all().
Something like this would work:
public class Coder extends AppCompatActivity {
private static long UPDATE_INTERVAL = 500; // every half second -- adjust as needed
TextView display;
String one;
Handler handler;
Runnable updater;
Random rand = new Random();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_coder);
display = (TextView) findViewById(R.id.text);
Handler handler = new Handler();
updater = new Runnable() {
#Override public void run() {
int n = rand.nextInt(2);
one = n + display.getText().toString();
display.setText(one);
handler.postDelayed(updater, UPDATE_INTERVAL);
}
}
}
#Override protected void onStart() { // or override onResume() instead
super.onStart();
startUpdates();
}
#Override protected void onStop() { // or override onPause() instead
super.onStop();
stopUpdates();
}
void startUpdates() {
handler.post(updater);
}
void stopUpdates() {
handler.removeCallbacks(updater);
}
}
you haven't called all() function anywhere and even if you do, the logic would still be wrong because bl always stays true and so the while loop is an infinite loop which never stops. Try this code:
public class Coder extends AppCompatActivity {
TextView display;
String one;
boolean bl= true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_coder);
display = (TextView) findViewById(R.id.text);
all();
}
void all() {
Random rand = new Random();
int n = rand.nextInt(2);
one = n + display.getText().toString();
display.setText(one);
}
}
if you want the loop you need to set your bl variable to false at some point to stop the loop from going on infinitely.

Handle AsyncTask if the task completes while the Activity is in the background

I've been using AsyncTasks for a while however, I've recently encountered a scenario where I'm unsure of how to handle correctly. Since I thought it would be a somewhat common scenario I decided to ask the question here.
So, I'm trying to use an AsyncTask to make a simple call to sign a user in to the app. After the call completes, if it succeeds, the user should be taken to another activity. This logic is simple. The problem arrises when the user navigates away from the app before the sign in call returns. In such a case, what should I do in onPostExecute()?
What I've seen some apps do is they continue with the call anyways, as long as the activity is still around, and will launch the next activity. However this creates a weird experience where the user navigates away from the app, then several seconds later, the app just pops back up in their face. Of course, I would like to avoid doing this.
Update
Example code:
public class ExampleActivity extends Activity {
private boolean mIsPaused;
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
Button btnSignIn = (Button) findViewById(R.id.btn_sign_in);
btnSignIn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
new SignInTask(ExampleActivity.this).execute();
}
});
...
}
#Override
protected void onPause() {
super.onPause();
mIsPaused = true;
}
#Override
protected void onResume() {
super.onResume();
mIsPaused = false;
}
private boolean isPaused() {
return mIsPaused;
}
...
private static class SignInTask extends AsyncTask<Void, Void, SomeResult> {
private final WeakReference<ExampleActivity> mAct;
public SignInTask(ExampleActivity act) {
mAct = new WeakReference<ExampleActivity>(act);
}
#Override
protected SomeResult doInBackground(Void... params) {
return mApi.signIn(creds);
}
#Override
protected void onPostExecute(SomeResult result) {
if (result.getCode() == OK) {
ExampleActivity act = mAct.get();
if (act != null) {
if (act.isPaused()) {
// do something
} else {
startActivity(new Intent(act, NextActivity.class));
}
} else {
// do something
}
}
}
}
}
made your AsyncTask class as static inner class.
Pretty interesting problem... Going with what you've started by using booleans, you could save the response the Activity receives to the SharedPreferences in the event it is paused, or continue processing normally if it is not. If the Activity later resumes (or is recreated), check whether or not there is a saved response and handle accordingly. I was thinking something along the lines of:
import org.json.JSONObject;
import android.app.Activity;
import android.os.Bundle;
public class TaskActivity extends Activity {
private static final String KEY_RESPONSE_JSON = "returned_response";
private boolean paused = false;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
// don't setup here, wait for onPostResume() to figure out what to do
}
#Override
public void onPostResume(){
super.onPostResume();
paused = false;
if(isSavedResponseAvailable()) processResponse(getSavedResponse());
else setup();
}
#Override
public void onPause(){
paused = true;
super.onPause();
}
private void setup(){
// normal setup
}
public void onReceiveResponse(JSONObject response){
if(paused) setSavedResponse(response);
else processResponse(response);
}
private void processResponse(JSONObject response){
// Continue with processing as if they never left
getSharedPreferences(this.getClass().getName(), 0).edit().clear().commit(); // Clear everything so re-entering won't parse old data
}
private boolean isSavedResponseAvailable(){
return getSavedResponse() != null;
}
private JSONObject getSavedResponse(){
try{
return new JSONObject(getSharedPreferences(this.getClass().getName(), 0).getString(KEY_RESPONSE_JSON, ""));
}
catch(Exception e){ }
return null;
}
private void setSavedResponse(JSONObject response){
getSharedPreferences(this.getClass().getName(), 0).edit().putString(KEY_RESPONSE_JSON, response.toString()).commit();
}
}
Clearly that's assuming your response from the task is JSON, but there's no reason you couldn't extend that to save the data individually and rebuild the necessary response object from the saved preference data.
As far as clean approaches go, though... I give this about a 3/10, but I can't think of anything better (well, other than making the TaskActivity abstract and forcing implementations to override setup(), processResponse(), isResponseAvailable(), getSavedResponse(), and setSavedResponse(), but that would only be mildly better for like a 4/10)
I would suggest putting a try/catch statement in the post execute - as far as I know what would happen in this situation is that you would get some kind of Window Manager exception.
What I would STRONGLY recommend, however, is stopping any async tasks (with the cancel method) on the onPause method, meaning that you won't interrupt them.
http://developer.android.com/reference/android/os/AsyncTask.html#cancel(boolean)
public final boolean cancel (boolean mayInterruptIfRunning)
Added in API level 3
Attempts to cancel execution of this task. This attempt will fail if the task has already completed, already been cancelled, or could not be cancelled for some other reason. If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.
Calling this method will result in onCancelled(Object) being invoked on the UI thread after doInBackground(Object[]) returns. Calling this method guarantees that onPostExecute(Object) is never invoked. After invoking this method, you should check the value returned by isCancelled() periodically from doInBackground(Object[]) to finish the task as early as possible.
Parameters
mayInterruptIfRunning true if the thread executing this task should be interrupted; otherwise, in-progress tasks are allowed to complete.
Returns
false if the task could not be cancelled, typically because it has already completed normally; true otherwise
See Also
isCancelled()
onCancelled(Object)
boolean isRunning; //set it to true in onResume, and false in onStop
boolean isWaiting; // set it to true in onPostExecute, if "isRunning" is false
check in onResume whether isWaiting is true, if yes, take user to another screen.
Use the cancel() of AsynchTask class onBackPress() of Activty class
public class ExampleActivity extends Activity {
private boolean mIsPaused;
SignInTask singleTaskObj;
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
Button btnSignIn = (Button) findViewById(R.id.btn_sign_in);
btnSignIn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
singleTaskObj = new SignInTask(ExampleActivity.this).execute();
}
});
...
}
#Override
protected void onPause() {
super.onPause();
mIsPaused = true;
}
#Override
protected void onResume() {
super.onResume();
mIsPaused = false;
}
protected void onBackPressed()
{
singleTaskObj.cancel();
}
private boolean isPaused() {
return mIsPaused;
}
...
private static class SignInTask extends AsyncTask<Void, Void, SomeResult> {
private final WeakReference<ExampleActivity> mAct;
public SignInTask(ExampleActivity act) {
mAct = new WeakReference<ExampleActivity>(act);
}
#Override
protected SomeResult doInBackground(Void... params) {
return mApi.signIn(creds);
}
#Override
protected void onPostExecute(SomeResult result) {
if (result.getCode() == OK) {
ExampleActivity act = mAct.get();
if (act != null) {
if (act.isPaused()) {
// do something
} else {
startActivity(new Intent(act, NextActivity.class));
}
} else {
// do something
}
}
}
}
}

Updating Views through AsyncTasks

Below is working sample code of an Android Activity that has 2 buttons, of which only 1 is ever visible: a refresh button and a stop button.
This code does the following:
Refresh is clicked: start some AsyncTasks. The preExecute of the tasks will hide the refresh button and show the stop button. The postExecute of the task will check if there are no more running tasks and if so, show the refresh button and hide the stop button.
Stop is clicked: all tasks are cancelled, the refresh button is shown and the stop button is hidden.
This code works fine, but for one exception: when I recreate the activity while a task is running by changing the screen orientation. The buttons will now return to the state as defined in the xml (refresh=visibile, stop=gone).
Using a static variable to keep track of the current state of the visibility only makes it worse, because the running Task that has to toggle it back, can only modify views in the calling activity, which has been stopped or destroyed at that point!
public class MainActivity extends Activity
{
private static List<MyAsyncTask> activeTasks = new LinkedList<MyAsyncTask>();
private View refresh;
private View stop;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_main);
this.refresh = findViewById(R.id.refresh);
this.refresh.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{ // Start a couple tasks
new MyAsyncTask(MainActivity.this).execute();
new MyAsyncTask(MainActivity.this).execute();
}
});
this.stop = findViewById(R.id.stop);
this.stop.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{ // Cancel all tasks and toggle refresh button
cancelAll();
MainActivity.this.enableRefresh(true);
}
});
}
public void enableRefresh(boolean enable)
{
if (this.refresh != null && this.stop != null)
{
this.refresh.setVisibility(enable ? View.VISIBLE : View.GONE);
this.stop.setVisibility(!enable ? View.VISIBLE : View.GONE);
}
}
public static void cancelAll()
{
for (MyAsyncTask task : MainActivity.activeTasks)
task.cancel(true);
MainActivity.activeTasks = new LinkedList<MyAsyncTask>();
}
private class MyAsyncTask extends AsyncTask<Void,Void,Void>
{
private MainActivity activity;
public MyAsyncTask(MainActivity activity)
{
this.activity = activity;
}
#Override
protected void onPreExecute()
{
MainActivity.activeTasks.add(this);
this.activity.enableRefresh(false);
}
#Override
protected Void doInBackground(Void... v)
{
try
{ // Simulate a task
Thread.sleep(3000);
}
catch (InterruptedException e)
{
}
return null;
}
#Override
protected void onPostExecute(Void v)
{
MainActivity.activeTasks.remove(this);
if (MainActivity.activeTasks.size() == 0)
this.activity.enableRefresh(true);
}
}
}
http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
See this it will help you ... on handling the states in orientation change
to handle AsyncTasks on screen orientation follow this example
MyAsyncTask myasynce;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_main);
this.refresh = findViewById(R.id.refresh);
this.refresh.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
//Register new Task
myasynce = ( MyAsyncTask ) new MyAsyncTask(MainActivity.this).execute();
}
});
this.stop = findViewById(R.id.stop);
this.stop.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{ // Cancel all tasks and toggle refresh button
cancelAll();
MainActivity.this.enableRefresh(true);
}
});
}
now onSaveInstanceState add
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//put myasynce status true if still runing false if finished
outState.putBoolean("myasynce", ( myasynce != null && query.getStatus() != AsyncTask.Status.FINISHED ) ? true : false );
if ( myasynce != null )
{
myasynce.cancel(true);
}
}
on savedInstanceState
add
if ( savedInstanceState.getBoolean("myasynce") == true )
{
//if task was running before screen orientation run it again
myasynce = ( MyAsyncTask ) new MyAsyncTask(MainActivity.this).execute();
}
hope this help
I think this link http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html by Tony Stark is probably the best solution, because that potentially solves even more problems.
However, I think I came up with an simpler solution for the problem at hand:
Add static vars to MainActivity:
private static MainActivity current;
private static boolean enableRefresh = true;
Save input value of enableRefresh():
public static void enableRefresh(boolean enableRefresh)
{
MainActivity.enableRefresh = enableRefresh;
(...) // Same as before
}
Add to MainActivity onCreate():
MainActivity.current = this;
enableRefresh(enableRefresh);
In the AsyncTask, use
MainActivity.current as the activity to update, instead of the activity provided in the constructor.

Android back-button causes crash?

I have a problem where my app keeps crashing once Im in the app and press the "back-button" on my phone and then enters the app again.. I guess im handeling some kind of state or something wrong:
package com.animeus;
import com.animeus.Factories.CameraDialogsFactory;
import com.animeus.Factories.CameraFactory;
import com.animeus.Services.CameraService;
import android.app.Activity;
import android.hardware.Camera;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
public class LightBulbActivity extends Activity {
/** Called when the activity is first created. */
Camera c;
//Application starts here
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
loadComponentsToUse();
setComponentEvents();
if (c == null)
CameraDialogsFactory.GetNoCameraFoundDialog(this).show();
else
setComponentEvents();
}
//Sets all the components events
private void setComponentEvents() {
View v = (View)findViewById(R.id.LightBulbView);
v.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
triggerLightEvent(event.getAction());
return false;
}
});
}
//Turns the led on or off
private void triggerLightEvent(int currentevent) {
if (currentevent == MotionEvent.ACTION_DOWN)
CameraService.turnLedLightOn(c);
else if (currentevent == MotionEvent.ACTION_UP)
{
CameraService.turnLedLightOff(c);
}
}
//Loads the "global" components we are supposed to use
private void loadComponentsToUse() {
c = CameraFactory.getCamera();
}
//Called once the activity ain't visible for the user anymore
#Override
public void onStop() {
super.onStop();
}
#Override
public void onPause() {
super.onPause();
}
#Override
public void onResume() {
super.onResume();
}
}
any ideas?
I also tried to release the camera on "onPause" and "onStop" and then recreate the camera "onResume" but that cause the app to crash aswell...
and yeah I know.. this aint the full code.. but if you need more of the code, then let me know
Thanks in advance!
What kind of action do you want to take place when the user hits the back button? In my apps if the user is in the main activity and not deep in the activity stack I override the backbutton to prompt the user with an AlertDialog that asks them if they would like to exit the app and then if they press yes I use System.exit() after I clean house so to speak with a method I create to kill and nullify everything first.
You can override the back button like so:
#Override
public void onBackPressed(){
super.onBackPressed();
cleanHouse();
System.exit(0);
}
You could also put this in your onPause():
#Override
protected void onPause() {
super.onPause();
try{
cam.camera.release();
}catch(NullPointerException e){
e.printStackTrace();
try{
cam.camera.unlock();
}catch(NullPointerException f){
f.printStackTrace();
}
}
}

Android object value changed when resume

I got a VERY STRANGE situation...(to me)
For example, 2 objects,
1 is an activity member boolean called isInPage,
2 is a static bitmap object called bmpPhoto.
When I get into my own activity called FacebookShareActivity
isInPage will be true until I quit this activity,
bmpPhoto will be given a picture.
After onCreare() and onResume(), there is no any code running, until user click some GUI.
What I did is close screen by press hardware power button, and maybe wait 5 or 10 minutes.
OK, now I press porwe again to wake phone up, unlock screen,
and my FacebookShareActivity goes back to front.
And I click my GUI button to check variable value via Logcat, it says:
isInPage=false;
And I forget bmpPhoto's value, but on my GUI, the photo just gone,
not displayed anymore...
How is this happen ?
And it just not happen every time after I do that......
What if I override onSaveInstanceState(Bundle savedInstanceState) and
onRestoreInstanceState(Bundle savedInstanceState) ?
Will it help ?
And what about the bitmap object ?
Still don't know how is that happen...
Did I miss something ?
I really need your help, please everyone~
Following is part of my code, quite long...
The "isPageRunning" and "bmp" changed sometime when back from desktop, but not everytime.
public class FacebookShareActivity extends Activity
{
private Bundle b=null;
private Bitmap bmp=null;
private boolean isFacebookWorking=false;
private boolean isPageRunning=true; //This value sometime changed when back from desktop, but not every time
protected void onCreate(Bundle savedInstanceState)
{
Log.i(Constants.TAG, "ON Facebook Share create......");
super.onCreate(savedInstanceState);
setContentView(R.layout.facebook_share);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
private void initUI()
{
btnBack=(Button)findViewById(R.id.btnBack);
btnBack.setOnClickListener(new ButtonClickHandler());
formImage=(RelativeLayout)findViewById(R.id.form_image);
formImage.setDrawingCacheEnabled(true);
btnShare=(Button)findViewById(R.id.btnShare);
btnShare.setOnClickListener(new ButtonClickHandler());
txtIntroText=(TextView)findViewById(R.id.txtIntroText);
txtIntroText.setOnClickListener(new ButtonClickHandler());
txtIntroText.setText(getUploadInImageText());
photo=(ImageView)findViewById(R.id.photo);
bmp=Constants.PROFILE.getName().getPhoto();
if(bmp!=null)
{photo.setImageBitmap(bmp);} //bmp wouldn't be null, it filled by some other activity before
}
#Override
protected void onResume()
{
super.onResume();
Log.i(Constants.TAG, "Trying to set UI on resume...");
b=getIntent().getExtras();
// ...
// ... Get some String value passed from prev activity
facebook=new Facebook("123456789012345"); //Test
asyncFacebook=new AsyncFacebookRunner(facebook);
initUI();
System.gc();
}
#Override
public void onBackPressed()
{
Log.d(Constants.TAG, "Activity receive back key...");
lockButtons(false);
return;
}
private void lockButtons(boolean b)
{
if(isPageRunning)
{
btnBack.setClickable(!b);
btnShare.setClickable(!b);
}
}
private class DelayReleaseKey implements Runnable
{
public void run()
{
try{Thread.sleep(10000);}
catch(InterruptedException ie){}
handler.sendEmptyMessage(0);
}
}
private class ButtonClickHandler implements OnClickListener
{
public void onClick(View v)
{
if(v==btnBack)
{
if(isFacebookWorking)
{ShowAlertDialog(Constants.MESSAGE_FACEBOOK_WORK);}
else
{
lockButtons(true);
formImage=null;
photo=null;
b=null;
facebook=null;
isPageRunning=false;
Intent intent=new Intent(FacebookShareActivity.this, PracticeListActivity.class);
startActivity(intent);
FacebookShareActivity.this.finish();
overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_out_right);
}
}
if(v==btnShare)
{
lockButtons(true);
facebookLogin();
}
}
}
}
Now I know i must override onSaveInstanceState, onRestoreInstanceState.
They can help me to save variable like String, int, boolean...
What about Bitmap ?
And what if my variable is static ?
Now try again.
public class FacebookShareActivity extends Activity
{
private Bundle b=null;
private static Bitmap bmp=null;
private static boolean isFacebookWorking=false;
private static boolean isPageRunning=true; //This value sometime changed when back from desktop, but not every time
protected void onCreate(Bundle savedInstanceState)
{
Log.i(Constants.TAG, "ON Facebook Share create......");
super.onCreate(savedInstanceState);
setContentView(R.layout.facebook_share);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
private void initUI()
{
btnBack=(Button)findViewById(R.id.btnBack);
btnBack.setOnClickListener(new ButtonClickHandler());
formImage=(RelativeLayout)findViewById(R.id.form_image);
formImage.setDrawingCacheEnabled(true);
btnShare=(Button)findViewById(R.id.btnShare);
btnShare.setOnClickListener(new ButtonClickHandler());
txtIntroText=(TextView)findViewById(R.id.txtIntroText);
txtIntroText.setOnClickListener(new ButtonClickHandler());
txtIntroText.setText(getUploadInImageText());
photo=(ImageView)findViewById(R.id.photo);
bmp=Constants.PROFILE.getName().getPhoto();
if(bmp!=null)
{photo.setImageBitmap(bmp);} //bmp wouldn't be null, it filled by some other activity before
}
#Override
protected void onResume()
{
super.onResume();
isPageRunning = true;
Log.i(Constants.TAG, "Trying to set UI on resume...");
b=getIntent().getExtras();
// ...
// ... Get some String value passed from prev activity
facebook=new Facebook("123456789012345"); //Test
asyncFacebook=new AsyncFacebookRunner(facebook);
initUI();
System.gc();
}
#Override
protected void onPause()
{
isPageRunning = false;
}
#Override
public void onBackPressed()
{
Log.d(Constants.TAG, "Activity receive back key...");
lockButtons(false);
return;
}
private void lockButtons(boolean b)
{
if(isPageRunning)
{
btnBack.setClickable(!b);
btnShare.setClickable(!b);
}
}
private class DelayReleaseKey implements Runnable
{
public void run()
{
try{Thread.sleep(10000);}
catch(InterruptedException ie){}
handler.sendEmptyMessage(0);
}
}
private class ButtonClickHandler implements OnClickListener
{
public void onClick(View v)
{
if(v==btnBack)
{
if(isFacebookWorking)
{ShowAlertDialog(Constants.MESSAGE_FACEBOOK_WORK);}
else
{
lockButtons(true);
formImage=null;
photo=null;
b=null;
facebook=null;
isPageRunning=false;
Intent intent=new Intent(FacebookShareActivity.this, PracticeListActivity.class);
startActivity(intent);
FacebookShareActivity.this.finish();
overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_out_right);
}
}
if(v==btnShare)
{
lockButtons(true);
facebookLogin();
}
}
}
}
For primitive values, you should use onSaveInstanceState. For restoring you can use onRestoreInstanceState or you can some code in onCreate like this:
if(savedInstanceState != null) {
// restore old state
} else {
// a fresh start
}
Now for restoring objects like Bitmap if they are not expensive to create and doesn't make your UI sluggish, create them again on restore. If you do not want to that then use onRetainNonConfigurationInstance and code will look like this:
#Override
public Object onRetainNonConfigurationInstance () {
return bmp;
}
#Override
public void onCreate() {
if
bmp = (Bitmap)getLastNonConfigurationInstance();
}
WARNING: This api is deprecate, you might use it on old platforms. I put it here for illustration purpose. The new way to do this is more involving.
Here is detailed ref:
getLastNonConfigurationInstance
onRetainNonConfigurationInstance

Categories

Resources