Consider scenario 1:
I have activity and launch another activity to do something. After 2nd activity done (action complete) - I need to update 1st activity. I understand this can be done using "launch for result"
Scenario #2:
I have service that runs on background, fetches data, etc. This also can update 1st activity. In this case activity can be live or it can be paused too.
In iOS I just use NotificationCenter and it works. How can I "register" to receive events in Activity and make sure it is updated even if activity sleeps at that moment? I was thinking about creating global flags and check them onResume, but not sure what is the proper way to handle this scenarios most proper and "loose" way?
There are two common options.
LocalBroadcastManager is provided by the compatibility library and will allow Android components in the same app to send and receive messages in the form of Intents. It's a lot like normal broadcasts.
You can also find a third party "event bus" library, which will be a little more flexible.
You are correct, you would use startActivityForResult() when starting your second activity. Then, in your first activity, override onActivityResult() which you will put logic in to receive the result from the second activity. For example:
First Activity
int REQUEST_CODE = 1;
Intent newActivityIntent = new Intent(this, SecondActivity.class);
startActivityForResult(newActivityIntent, REQUEST_CODE);
then override onActivityResult()
#Override
protected void onActivityResult (int requestCode, int resultCode, Intent data) {
// Make sure the result is OK.
if (resultCode == RESULT_OK) {
// Make sure this result is from the corresponding request.
if (requestCode == REQUEST_CODE) {
// Extract your data from the intent.
MyData myResultData = data.getExtra("my_extra"); // Generic example.
}
}
}
In the second activity you simply gather the information needed to send back when the user is finished, and put it in the return intent.
Second Activity
Intent returnIntent = new Intent();
returnIntent.putExtra("my_extra", data);
// Now set the result.
setResult(RESULT_OK, returnIntent);
// Call finish, and the return intent is passed back.
finish();
This is probably the easiest and least complex way to achieve what you want. This way, you know the activity receiving the result will always be unpaused when it gets the result.
Related
I have an activity to add an object to a database in my application, and the database is displayed in activity which calls an AddActivity. My problem is I need to refresh ArrayList after i update a database, but i can't have access to it inside my AddActivity. So i wanted to track in MainActivity when the AddActivity has ended and then call the refreshing function. For now i tried using code below and this is working, but i don't think this is the way i should do it, I feel like there is better function for that but I couldn't find it.
Code in MainActivity inside onClickListener:
Intent intent = new Intent(mContext, ExerciseAdd.class);
startActivityForResult(intent, 1);
Code in AddActivity also inside onClickListener:
dataBaseAccess = ExercisesDataBaseAccess.getInstance(getApplicationContext());
dataBaseAccess.open();
dataBaseAccess.addExercise(nameString, typeString, "NULL");
dataBaseAccess.close();
Intent intent = new Intent();
intent.putExtra("result", 1);
setResult(1, intent);
finish();
And refhresing function in MainActivity:
#Override
public void onActivityResult(int requestCode, int resultCode, #Nullable Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
dataBaseAccess.open();
exercises = dataBaseAccess.getAllExercises();
dataBaseAccess.close();
adapter.setExercises(exercises);
adapter.notifyDataSetChanged();
}
Or maybe is there any way to pass an reference to ArrayList to AddActivity? it was hard to me to find any information about this
The way you are doing it is the most robust and clean way. You are storing your data in the database and you have one Activity that adds items to the database and another Activity that reads the data from the database and displays it. This may seem like overkill, but this architecture is more robust than sharing data in memory between activities.
Of course, it is possible to share data in memory between activities. You can store a reference to your ArrayList<Exercise> in a public static variable and it can be accessed by both activities. This is a simple way to share data but can be tricky if your app goes to the background and Andriod kills off the OS process hosting the app and then the user returns to the app. If he was last using the AddActivity, when he returns to the app, Android will create a new OS process to host the app and instantiate the AddActivity, but MainActivity is not yet instantiated and your shared public static data will be gone. So this approach is error-prone.
Another approach would be to use Live Data. This is a more complicated approach to sharing data in memory between activities. You can read more about live data here: https://developer.android.com/topic/libraries/architecture/livedata
So I have the following currently:
public void addProject(View v) {
String pn = "";
Bundle extras = new Bundle();
Intent i = new Intent(mMainContext, AddProject.class);
mMainContext.startActivity(i, extras);
if(extras.getString("projectName") != null) {
pn = extras.getString("projectName");
}
}
This is just an example, the first bit is correct though the if statement may not be but this isn't about that block.
What I have noticed is that if I place breakpoints on the new intent, start activity and then the if statement,
Android seems to skip past the startActivity and go onto the if statement.
If I then remove the if statement and run it again, it then launches the intent.
So what I've realised is that it runs to the end of the addProject block and then actually launches the intent...
Why is this? And more importantly, how I do stop it doing that?
Intended outcome is as follows:
1) user presses "Add Project" button
2) intent launched and user inputs project name and presses submit
3) calling intent then receives the p name for use later in the function.
Why is this?
startActivity() is asynchronous. When it returns, the activity is not yet started.
how I do stop it doing that?
You can't.
Intended outcome is as follows: 1) user presses "Add Project" button 2) intent launched and user inputs project name and presses submit 3) calling intent then receives the p name for use later in the function.
Having separate activities may not be the best plan for this (compared with using fragments).
If you wish to use activities, the most natural approach is:
Use startActivityForResult() instead of startActivity()
Have AddProject use setResult() to create a result Intent to deliver back to your first activity
Have your first activity override onActivityResult(), where it can receive a copy of that result Intent and get the results from it
Note that this still will all happen after addProject() returns. However, you will (eventually) get your results.
The Android documentation does not cover this very well, but this is what it has on the subject.
you must use onActivityResult for such task.
From your main Activity use: startActivityForResults(intent) in place
of startActivity();
In your AddProject Activity when user inputs project name and press the
button .
Intent i = new Intent();
i.putExtra("projectName",userInputTextFromEditText);
setResults(RESULT_OK,i)
In your MainActivity
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data){
pn = intent.getExtras.getStringExtra("projectName","");
}
this way you will find your result
So I have a Fragment that calls the following method which launches the camera:
private void launchCamera() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getActivity().getPackageManager()) != null) {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
And I expect to receive the picture data in this method in my fragment:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (!(resultCode == RESULT_OK)) return;
if (requestCode == REQUEST_IMAGE_CAPTURE || requestCode == REQUEST_GALLERY_IMAGE) {
Uri imageURI = data.getData();
// do something
}
}
However, after I take a picture and confirm it, the app goes to my launcher. After setting breakpoints in the onActivityResult method, the app never even reaches this method before crashing. I've granted made sure to grant all permissions in both the manifest and at runtime.
There are also no outputs to Logcat with this crash, both in the app logs and the device logs. I have also tested on both my device (Moto G5 Plus) & Pixel XL API 26 emulator; both have the same result.
I think you need to call super.onActivityResult().
The fragment is the one making the startActivityForResult() call, but the activity gets the first shot at handling the result so you need to implement super.onActivityResult() to make the fragment handle the result.
the app immediately crashes to my launcher
No, the camera app immediately brings up the launcher. Apparently, the developers of this camera app wrote it to bring up the home screen when the user is done taking the picture. This is a bug, of course, but there is little that you can do about it.
Use ACTION_IMAGE_CAPTURE when you would like a picture but do not mind if you do not get it, due to bugs in some of the hundreds of camera apps that you are integrating with. Otherwise, use a camera library (Fotoapparat, CameraKit-Android, etc.) to take the picture directly in your own app.
Also, and FWIW, ACTION_IMAGE_CAPTURE does not return a Uri, so your onActivityResult() code will not work anyway. Given your particular ACTION_IMAGE_CAPTURE Intent configuration, in onActivityResult(), data.getParcelableExtra("data") will return a Bitmap representing a thumbnail-sized image.
I believe this is yet another manifestation of the Android: Activity getting Destroyed after calling Camera Intent.
The bottom line is, the system restarts your app from scratch and your activity is created but has a chance to restore its state.
So you must implement onRestoreInstanceState(). I am not sure you can guarantee that the fragment will be ready to receive onActiviyResult() timely, so to be in the safe side, I prefer to handle the captured image in activity itself.
So this issue was actually because of an intent flag I had attached to the activity. My activity was started using the NO_HISTORY intent flag, which apparently prevented it from being recreated when returned from a startActivityForResult call.
I have been spending lots of hours figuring the reason why top of the stack is not cleared yet.
Well I tried the following:
Intent intent = new Intent(ActionBarActivity.this, MainActivity.class);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
And it turned me to the MainActivity. Then when I try to press back button, the program does not exit, but instead it turns me to the first page of ActionBarActivity.
Let me be more specific:
In MainActivity I call ActionBarActivity. In the ActionBarActivity I have a search bar and I enter a query there and just print the value of the query.
If you think how it will work is a below:
MainActivity -> ActionBarActivity1 -> ActionBarActivity2 -> ActionBarActivity3 -> ..
In that ActionBarActivity I have also an option which brings me back to the MainActivity.
So as I said when I run application with the way above, it will bring me to the MainActivity.
Nice so far, but when I press the back button I expect it to exit, but instead it goes to ActionBarActivity1. I doubt the stack is not properly erased.
What should I do in this case. Thanks
I'm assuming you've seen this post: Android: Clear the back stack and it is not what you want
The FLAG_ACTIVITY_NEW_TASK is creating an new task stack. When you hit "back" that is the only activity in that stack, so Android goes to the previous task - which has the calling activity at the top.
If you remove that flag, then MainActivity is already in the task stack and is brought to the top. And "back" removes it and brings you back to the calling activity. So far, that is the expected Android behavior.
EDIT: Since you commented that you only have 2 activities, then one option is to "finish()" ActionBarActivity after it starts MainActivity like this:
startActivity(intentMainActivity);
finish();
To get the behavior you want with many activities or only in certain situations, you need to use SET_RESULT and finish the activities in the stack when you hit the button to go to the MainActivity. In other words, Android assumes you want to keep activities even when new ones start. But in this case you don't, so you have to tell the activity to destroy itself.
i.e. call setResult(DONE) and then use startActivityForResult and check for your "DONE" flag in the onActivityResult method.
then this:
MainActivity -> ActionBarActivity1 -> ActionBarActivity2 -> ActionBarActivity3
-> MainActivity
becomes:
MainActivity -> ActionBarActivity1 -> ActionBarActivity2 -> ActionBarActivity3
-> call "Done"
Destroy ActionBarActivity3
Destroy ActionBarActivity2
Destroy ActionBarActivity1
MainActivity remains
For example, in ActionBarActivity2:
startActivityForResult(intentActionBarActivity3, ActionBarActivity2_ID);
Then in ActionBarActivity3:
public void exitButton(View view) {
setResult(MY_DONE_FLAG);
finish();
}
And back in ActionBarActivity2:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == ActionBarActivity2_ID && resultCode == MY_DONE_FLAG) {
setResult(MY_DONE_FLAG, data);
finish();
}
}
And so on with all activities that you want to "close" when that button is pressed.
try
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
finish();
insetead of
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
and you should also use it with every intent which leads you to ActionBarActivity.
i mean if you are going back to ActionBarActivity form ActionBarActivity1 then you use it with intent.
then create intent for MainActivity in your so called ActionBarActivity.
Intent intent = new Intent(ActionBarActivity.this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
Good luck !!!
Say I have 2 activities (ActivityOne and ActivityTwo). How would I call ActivityTwo from ActivityOne? Then how would I return to ActivityOne from ActivityTwo? For example, I have a listview with all the contacts on the host phone. When I tap on a contact, another activity shows information and allows editing of that contact. Then I could hit the back button, and I would go back to the exact state that ActivityOne was in before I called ActivityTwo. I was thinking an Intent object, but I am not sure. Could someone post some code?
In your first activity (AcitvityOne) you could call
Intent intent = new Intent(this, ActivityTwo.class);
startActivityForResult(intent, CODE);
Then you can override
public void onActivityResult(int reqCode, int resultCode, Intent data) {
to receive the result of this activity
In your example, it seems like you will want to pass data between Activity One and Activity Two (namely, the reference of the contact you want to edit), so you should use a Bundle with your intent in Activity One.
Intent myIntent = new Intent(this, ActivityTwo.class);
myIntent.putExtra("CONTACT", position);
this.startActivity(myIntent);
"CONTACT" is a key and position is a value (position would have been defined before, it could be whatever you want it to be, for example an integer. The way you retrieve it is slightly different based on what type it is, note the getInt() in Activity Two code below).
And in Activity Two, in onCreate()
Bundle extras = getIntent().getExtras();
if (extras != null) {
contact = extras.getInt("CONTACT");
/**Do what you want with the integer value you have retrieved**/
}
As for your other question, the back button removes the current Activity from the top of the tasks stack and therefore, you return to the previous Activity, normally in exactly the same state as you have left it.
However, if the phone is running low on memory, Android might kill off a process or Activity so it is possible in theory that your previous Activity will not show in exactly the same state as you have left it. If you have any important data, you should save it before launching Activity Two. If you just want to return to Activity One in the same state for user friendliness, then it's fine to rely on the back button.