I am trying to wrap my head around what proper activity flow convention is.
I currently have:
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
//do stuff
//clicklisteners setup etc
Intent intent = new Intent(this, ExampleActivity.class);
//putExtras
startActivity(intent);
}
}
public class ExampleActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
//getExtras
//objectA state lives here
//do stuff
}
}
If the user presses back when on the ExampleActivity view, and then clicks another listener that takes them to ExampleActivity, I want to be able to access "objectA" state again. How do I implement this? I am not understanding onResume or onRestart...
are these the methods to call? or is there a better convention to navigate the app activities?
Android has a mechanism for having an activity pass results back to the prior activity that started it. The documentation for that is here.
Basically, you use startActivityForResult to start the second activity, the second activity uses setResult to set results, and the first activity receives those results in the onActivityResult callback when the second activity finishes.
If the user presses back when on the ExampleActivity view, the ExampleActivity is dead and user is back in the MainActivity, which calls "onResume".
When your are back from activity1 to activity2, activity2's onResume method is called.
With that being said, after the user closed ExampleActivity objectA is destroyed.
Related
I have two activities my MainActivity and some other one called DetailActivity. When the app is first started it opens MainActivity and there it binds DataService and makes a call to fetch some data and populate a list view.
From that list view user has a button to open so called detail view for every item in the list. Opening that detail view means starting the second activity (DetailActivity).
Its done like this:
final Intent intent = new Intent(getContext(), DetailActivity.class);
intent.putExtra("data", dto);
getContext().startActivity(intent);
When second one is opened user is able to go back either by using back button (one left of home button on android) or by clicking back arrow in the header.
Everything works as expected except that when user comes back to MainActivity DataService is binded again and call to fetch the data is made and the list is updated. So if user is somewhere at item no. 205 he will be returned back to the start item.
Is there a way to hold the data or the state of MainActivity when user comes back to it that its not refreshed ?
Service is bonded like this
#Override
protected void onStart() {
super.onStart();
bind(DataService.class);
}
#Override
protected void onDestroy() {
super.onDestroy();
unbindService(connection);
}
private void bind(final Class... toBind) {
for (final Class clazz : toBind) {
bindService(new Intent(this, clazz), connection, BIND_AUTO_CREATE);
}
}
private ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(final ComponentName name, final IBinder service) {
if (service instanceof DataService.LocalBinder) {
dataService = ((DataService.LocalBinder) service).getInstance();
dataService.readData();
}
}
#Override
public void onServiceDisconnected(final ComponentName name) {
// Empty By Default
}
};
You can achieve this by binding the service in onCreate() instead of onStart().
You should also have a look at the symmetry of your life cycle. Currently you are binding in start and unbinding in destroy. If you bind in onStart you should probably unbind in onStop. When you move the binding to onCreate you can keep the unbinding in onDestroy
Explanation: onCreate() is called when the activity is created first. onStart() is called every time your activity becomes visible.
Managed to solve it like this
#Override
public boolean onSupportNavigateUp() {
onBackPressed(); // one inherited from android.support.v4.app.FragmentActivity
return false;
}
Doing it this way seamed to do the trick. I would return to the place where I left from and there is no need to load any data since everything is already there
I have a, what I thought was a, simple task. I have a list of achievements in a recyclerView in my MainActivity. Clicking on one of the achievements launches the AchievementDetailActivity. You do an action in the AchievementDetailActivity and it completes the achievement. When that happens, I need the thumbnail on the MainActivity to be the achievement icon and not the locked icon. What I thought was I could just use the LocalBroadcastManager to send and receive messages from one activity to another, but that isn't working. From everything I have read you are supposed to unregister the listener in your activity onPause and onStop lifecycle methods, however, if you unregister the listener in the onPause of MainActivity, it gets unregistered when the AchievementDetailActivity starts and nothing gets delivered to the MainActivity. I don't understand how you can use LocalBroadCastManager with receivers to send information between activities when they get unregistered as soon as you start a new activity. The following example shows it unregisters as soon as the second activity is started, so the first activity will never get the broadcast...
public class MainActivity extends AppCompatActivity {
public static final String ACTION = "update";
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("MAIN ACTIVITY", "RECEIVED EVENT");
}
}
public void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver(receiver), new IntentFilter(ACTION));
}
public void onPause() {
Log.d("MAIN ACTIVITY", "REMOVING LISTENER");
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
}
//.. The rest of MainActivity
}
public class SecondActivity extends AppCompatActivity {
#Override
public void onStart() {
super.onStart();
//initialize view and such
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getApplicationContext());
Intent intent = new Intent(MainActivity.ACTION);
intent.putExtra("Something", "somewhere");
manager.sendBroadcast(intent);
}
)};
}
If you run the above, obviously with the buttons and such, you will get a message that shows it unregisters the listener as soon as SecondActivity starts and the MainActivity will never get the message it is supposed to get when you click a button on the second activity.
Is there a way to send information from activity to activity, or because of the lifecycle events is that not possible? Everything I read said LocalBroadcastManager was the right way to do it, but it gets unregistered so, how can it? Please help, this is driving me nuts. Thank you.
If you want to use your LocalBroadcastManager to fetch results, do not unregister in onPause, in which case must to unregister in onDestroy().
If you startAvtivity only to fetch some results, it is a good way to startActivityForResult.
Its not a great idea to use LocalBroadcastReceiver for this purpose. Use startActivityForResult instead. Follow this Official doc for implementation details.
The reason its not wokring is:
You are registering the receiver in onStart and unregistering it in onPause(). When your second activity is shown, onPause () will be called as a result the BroadcastReceiver is unregistered.
Its not a great idea to keep it registered since it would lead to memory leaks.
I start activity B from activity A.
A call-back method in a non activity class is invoked indicating that I should close Activity B.
How can I close activity B from the non activity class?
Please keep in mind that activity B is not always the same activity, but A is. For example, in my case A is the MainActivity and B is any number of other activities which I can launch from MainActivity.
TLDR; How do I close the current foreground activity from a non activity class.
you should use broadcast.
you can use a SuperActivity,and other activities extend from SuperActivity
register the broadcast in the SuperActivity,
if(intent.getAction.equals(your custom action))(
finish();
)
and B Activity extends SuperActivity
pass the Context to the non activity class as an argument, at the end of this method,use Context to send broadcast.
don't forget the broadcast should be unregistered when the B Activity is finish
Avoiding passing Activity Object to non Activity Class
you can use listener for this and only pass the listener object ... create
your custom listener and your non Activity class take this listener as a
parameter and then you make a superB activity for all your B Activities
that you want to finish and then your superB implement this listener and
all B Activityes override this listener method.
Example:
this is your custom listener
public interface CustomLisnter {
void finishBActivity();
}
at your superB Activity you implement this listener
public abstract SuperBActivity implements CustomLisnter{
}
then at your B Activity extends SuperBActivity and override finishBActivity() method.
public BActivity extends SuperBActivity{
//when ever in your code you call non Activity Class that has the callback
// pass this activity in its constrictor like this
SomeClassToFinishActivity mClass = new SomeClassToFinishActivity(this);
#override
public finishBActivity(){
//here you can finish activity
finish();
}
}
at your non Activity class that responsible for finish current BActivity
pass this listener to it.
public SomeClassToFinishActivity {
private CustomLisnter mListener;
public SomeClassToFinishActivity(CustomLisnter listener){
this.mListener = listener;
}
}
and in your callback that should finish the activity call this
private void yourCallBackMethodToFinishActivity(){
mListener.finishBActivity();
}
i hope that helps you .. let me know if you have a question.
I'm pretty new to Android/java, and have come up against my first head-scratcher.
I'm trying to conditionally change the starting view based on if a user is logged in (I've set is_logged_in already, here, to more simply illustrate the problem).
Many thanks.
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
boolean is_logged_in = false;
if(is_logged_in) {
setContentView(R.layout.activity_main);
} else {
menu_sign_up(); // How would I pass the View?
}
}
// Doesn't work, at least when called inside of MainActivity's onCreate
public void menu_sign_up(/* View view */) {
Intent intent = new Intent(this, SignUp.class);
startActivity(intent);
}
// Example of a working method, at least when called from android:onClick in XML
public void menu_sign_in(View view) {
Intent intent = new Intent(this, SignIn.class);
startActivity(intent);
overridePendingTransition(0,0);
}
}
public class SignUp extends MainActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sign_up);
}
// ...
}
***** Truncated Stack Trace Below ******* (adb logcat *:W)
W/ActivityManager(19583): Activity idle timeout for HistoryRecord{40953fd8 com.example.digitalkarmaeclipse/.SignUp}
W/ActivityManager(): Activity idle timeout for HistoryRecord{}
W/ActivityManager(21900): Launch timeout has expired, giving up wake lock!
W/ActivityManager(21900): Activity idle timeout for HistoryRecord{40a23580 com.example.digitalkarmaeclipse/.MainActivity}
W/ActivityManager(21900): Activity idle timeout for HistoryRecord{40a2c6f8 com.example.digitalkarmaeclipse/.SignUp}
W/ActivityManager(21900): Activity idle timeout for HistoryRecord{40a30100 com.example.digitalkarmaeclipse/.SignUp}
W/ActivityManager(21900): Activity idle timeout for HistoryRecord{40a338e8 com.example.digitalkarmaeclipse/.SignUp}
W/ActivityManager(21900): Activity idle timeout for HistoryRecord{40a376e0 com.example.digitalkarmaeclipse/.SignUp}
Your current app structure doesn't make a lot of sense. Does SignUp have to be an Activity as well? You could use an AlertDialog to do something similar. If it does need to be an Activity then have it extend Activity, it doesn't make much sense to extend HomeActivity in this context.
I also don't like the idea of putting setContentView() in a logical statement. onCreate() is only called when the Activity is created. So if you start your SignUp Activity and then press the back button, there won't be a layout for your HomeActivity.
edit: And like Shark said it doesn't look like you have a way to pass the data from SignUp back to HomeActivity. You should try to use startActivityForResult() and then use setResult() in SignUp to say whether it was successful or not.
You need to let onCreate() finish, then move your new Activity to onResume() or onStart(); those get called as your activity is shown to the user instead of it still initializing (itself).
Also, it looks like you need to set up a
onLoginFinished(int result);
callback... either try running your login with startActivityForResult() and catching the result in onActivityResult()
OR ...
you make your own interface and once the other activity finishes it, it invokes the onLoginFinished(isOK) callback where you determine which screen to use.
How would I call a method from a different activity?
In my main activity, I have a button that shows a dialog to set the difficulty level of the game.
Then you click start game which starts a new activity containing a view with all the game information.
I need to send the difficulty level chosen to the other activity but cannot seem to figure out how to.
You could put it in the extras with the intent:
Intent StartGame = new Intent(this, StartGame.class);
StartGame.putExtra("difficulty", difficultyLevel);
startActivity(StartGame);
Then in your StartGame.class you can retrive it like this(assuming its a string):
Bundle extras = getIntent().getExtras();
if (extras != null) {
String difficulty= extras.getString("difficulty");
}
Well I don't know how sound my solution is but I created a myApplication class which sub classes the Application class .
This holds the reference to the activity I wanted to call
import android.app.Application;
public class myApplication extends Application {
public PostAndViewActivity pv;
}
When the PostAndViewActivity calls the oncreate is sets the pv to point to itself.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((myApplication) getApplication()).pv = this;
Then when I want to call the method I want I just use code like this:
((myApplication) getApplication()).pv.refreshYourself();
Perhaps a bit hacky but it works.....
I welcome some critisism for this ;-)