Android: OutOfMemory error and the backstack - java

the following sheet represents the working flow in the application this question is about.
I ran into problems with OutOfMemory Errors, mostly because users were able to switch from activity B to activity D multiple times (They are showing different content for every attempt), without the previous activity being destroyed. This led into a very large backstack resulting in an OutOfMemory error.
To avoid this, I followed the recommendation to add parent activites to the manifest and create new backstacks with the TaskStackBuilder class:
void openDFromB()
{
Intent i = new Intent(this, ActivityD.class);
TaskStackBuilder.create(this)
.addParentStack(ActivityD.class)
.addNextIntent(i)
.startActivities();
}
If a user now switches from activity B to D, MainMenu, A and B are destroyed and a new Backstack (MainMenu, C, D) is created.
This solves my memory problems for api level greater 10, but unfortunately the TaskStackBuilder does not create backstacks for devices pre api 11.
Any idea what to do in order to avoid users stacking infinite amounts of activities prior api 11? Is this even possible or should I try to free as many resources as possible in onPause to gain a maximum amount of stacked activities before OoM?
thanks in advance!

Try this-
Intent intent = new Intent(getApplicationContext(),yourActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
Another solution is-
Setting android:noHistory="true" on the activity in your manifest file will remove an activity from the stack whenever it is navigated away from.
But the most appropriate way is to call finish() method whenever you start new activity like in the following sample code-
Intent intent = new Intent(this, yourActivity.class);
startActivity(intent);
finish();
You can also create custom Broadcast receiver and register it in every activity which can be fired on event of your choice. For more help you can check this link.

Zabri answer is good, but i would propose another probably is better:
You dont close every activity, but when you are opening a new activity that has been open before, you tell the system that it can clear the way back and reopen that one, of course with a new intent and all the new data that you need.
So, if you do A - B - D - B - D - B, instead of having that big stack,you will have
A - B
A - B - D
A - B
A - B - D
A - B
So, if you do C - D - B - D - B - D, your stack will be
C - D
C - D - B
C - D
C - D - B
C - D
and so on.
In this way, i think the stack actually reflects what the user can have in mind, and also navigation is very logic, you don't even need to captre back button to have the navigation making sense, (of course you can do it if you want a more specific user experience)
The code to achieve this, is:
create a function that receives a class (something like ActivityD.class), there you create an intent, with the flags Intent.FLAG_ACTIVITY_CLEAR_TOP and Intent.FLAG_ACTIVITY_NEW_TASK and then when you need to open an activity, just call this function. You dont care about if the activity is in the stack or not. If it isnt,it is created normally.
protected void startActivity(Class<?> clase) {
final Intent i = new Intent(this, clase);
int flags = Intent.FLAG_ACTIVITY_CLEAR_TOP;
flags |= Intent.FLAG_ACTIVITY_NEW_TASK;
i.setFlags(flags);
startActivity(i);
}
startActivity(ActivityD.class);

May be your application having memory leaks and due to that older activities not getting reclaim by the GC and eventually getting heap size increase every-time activity switch occurs.
How to find leaks?
Please watch: http://www.youtube.com/watch?v=_CruQY55HOk
References:
http://android-developers.blogspot.in/2011/03/memory-analysis-for-android.html.
http://kohlerm.blogspot.in/2009/07/eclipse-memory-analyzer-10-useful.html

I don't know how it works with the Navigation Drawer, but I was creating an Android application some time ago which started with a loading screen and then moved to the main activity of the app. Since I didn't need/want the user ever to go back to the loading screen, I just called finish() before moving to the main activity. Like this:
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();
And then, when the user pressed the back button from the main activity, they didn't get to the loading activity, but to wherever they were even before that.
So you could also try to do this:
void openDFromB()
{
Intent i = new Intent(this, ActivityD.class);
startActivity(i);
finish();
}
As I said, I have never implemented a Navigation Drawer, but something tells me that doing this might break its whole behaviour. Also, calling finish() in an activity is such a basic concept that I would be surprised if you hadn't tried it already. :) But still, I am posting it as a shot in the dark, just in case it helps you. Good luck. :)

Hello you can try to add the following statement in your manifest file under application tag:
android:largeHeap="true"
Also make sure your android:targetSdkVersion is 14.

Related

Android: Uri Access Life Time from one activity to another

I have a helper method for choosing images and videos, let's call it Activity B.
So, this is how it works:
// were in Activity A
// user wants to choose a video
startActivityB(callbacks);
------------------------------------
// were in Activity B now
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("video/*");
chooseVideoLauncher.launch(intent);
------------------------------------
// were in chooseVideoLauncher now
Uri videoURI = ...;
callbacks.passVideoURI(videoURI); // this way, Activity A gets the videoURI
// do some more things...
finish(); // so the helper activity B is finished now, as the video is chosen already
------------------------------------
// were in activity A again, but now we have the videoURI
// user clicked a button: "Upload video"
uploadVideo(videoURI);
This is the error I get:
java.lang.SecurityException: Permission Denial: opening provider com.miui.gallery.provider.GalleryOpenProvider from ProcessRecord{f5899ab 29899:com.xxx} (pid=xxx, uid=xxx) that is not exported from UID xxx
I have googled the error and found this SO thread: here
#CommonsWare explains the error in a comment and links his blog post: Uri Access Lifetime: Shorter Than You Might Think
So the error happens because the helper Activity B chose the file, so the access is tied to Activity B. No other activity has access, and as soon as Activity B is destroyed (what happens in my code), the access to videoURI is completely gone. So when I later try to upload the video, it throws this error.
I tried these solutions:
Create a local copy of the video and pass that copy to Activity A. This works, but is a bad solution. For longer videos the app crashes with a memory overflow. So it's not an option.
Setting the flags #CommonsWare mentioned. So the code looks like this:
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("video/*");
intent.setFlags(FLAG_GRANT_READ_URI_PERMISSION);
intent.setFlags(FLAG_GRANT_WRITE_URI_PERMISSION);
chooseVideoLauncher.launch(intent);
But this doesn't seem to change anything. The error message remains exactly the same. Am I setting them wrong?
#CommonsWare says using a service would also be a solution. I would prefer not to create a service purely for fixing this permission error. If there's no other solution, I will of course.
But is there no way to grant Activity A permission to that Uri as well?
The best solution, by far, is to combine Activity A and Activity B into a single activity. Use fragments or composables for separate screens.
Setting the flags #CommonsWare mentioned. So the code looks like this:
You would not set the flags on the ACTION_PICK Intent. Instead, Activity B needs to start Activity A (in addition to finish()). You would put the Uri into the "data" facet of the Intent (e.g., via setData()), and you would put the flags on that Intent. You would also need something like FLAG_ACTIVITY_REORDER_TO_FRONT or something to avoid having two copies of Activity A on the back stack.
For the second way Set the flags to get the long lifetime, you can try to add the following code, to get the permission, then start the activity
val contentResolver = applicationContext.contentResolver
val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
// Check for the freshest data.
contentResolver.takePersistableUriPermission(uri, takeFlags)

Finishing an Activity with transition shows the previous Activity from the activity stack first and then transitions to the new Activity

I have 2 Activities in my Android application - Activity A and Activity B.
When the user presses a button on Activity A, he is navigated to Activity B.
I want Activity A to close after going to the next activity and is completely removed from the activity stack.
I tried adding intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); , but this not work as Activity A was still accessible after pressing the back button.
I added finishAffinity(); , this worked, but while transitioning, the previous activity in the activity stacks gets visible and then it goes to Activity B.
Video
(here the application drawer is shown, and then it goes to Activity B) -
Code -
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(MainActivity.this);
Intent i = new Intent(MainActivity.this, MainActivity2.class);
startActivity(i, options.toBundle());
finishAffinity();
Any fix so that the previous activity is not visible and activity is also closed ?
EDIT : I tried finish(), finishAfterTransition() and supportFinishAfterTransition() instead of finishAffinity() but still the previous screen is visible.
So I guess there's an issue with finishAfterTransition()
I solved it by adding android:noHistory="true" attribute to the activity (which has to be removed from the stack) in the manifest file solves the issue.
There is no need to use finish() in the java code after this change.
Method 2 -
Refer #Pratyay ' s answer for a way around.
you have to use finish but like this:
startActivity(Your Activity here)
then Finish()
if you do reverse you can see black screen for a short time do this way
#Override
public void onBackPressed() {
finishAffinity();
}
Use this function in Activity B, this will exit app on pressing back button. And don't use any finish(); or finishAffinity(); in Activity A. This will give a smooth transition

Start two activities from Android application

I have an application A that wants to start an Activity in another application, B, which I don't own and cannot edit.
If B is already running and visible in recent apps, there's no problem in executing the wanted Activity of B using an Intent.
If B isn't running, I use the following code to execute its main Activity first, and then the one I want to execute:
String bPackage = "com.example.applicationb";
PackageManager pm = getPackageManager(this);
Intent main = pm.getLaunchIntentForPackage(bPackage);
Intent wanted = new Intent();
wanted.setPackage(bPackage);
wanted.setComponent(new ComponentName(bPackage,bPackage+".WantedActivity"));
main.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
wanted.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
wanted.setExtras(mPreviouslyCreatedBundle);
startActivity(main);
startActivity(wanted);
The wanted Activity executes, but after some seconds I get an error and it stops working. Am I setting the Intents in a wrong way?
Make sure u have setted exported="true" for activity you are trying to redirect to another package
Basically my idea is when your second paackage app leave u need :
android.os.process.killprocess(android.os.process.mypid())
and when you launch use flag as start new task :
setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

How to destroy an activity

In fact, I am new to Android App Development. In my application, I have a couple of activities and I have provided my users with an exit option menu to be able to leave the application. But there is a problem. When they hit the Exit button, they are able to leave the application but when they enter the application for the second time, the page that they left off the last time will be launched.
Here comes my code:
#Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch (item.getItemId()) {
case 0 :
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
Toast.makeText(this, "Goodbye Dear", Toast.LENGTH_LONG).show();
break;
Android Activity has two methods onPause and onDestroy where you can do the necessary cleanup.
http://developer.android.com/reference/android/app/Activity.html
Instead of using finish(), use System.exit(0);.
You have to override onPause and/or onDestroy methods inside your activity and delete your view within these methods.
The problem in your code is that Intent.FLAG_ACTIVITY_NEW_TASK doesn't remove your current Task. Read more about it here: Task and Back Stack | Android Developers.
Try using Intent.FLAG_ACTIVITY_CLEAR_TOP. From the documentation we can see that this gives the desired behavior.
If set, and the activity being launched is already running in the
current task, then instead of launching a new instance of that
activity, all of the other activities on top of it will be closed and
this Intent will be delivered to the (now on top) old activity as a
new Intent.

How do I create an android shortcut to an activity?

OK,I'm new at this forum, so don't blame me for putting this in the wrong tags,not putting something in,eg.
I want to learn how to create a shortcut(I did that by Googling) and link it to an activity (In this case, com.android.mms.ui.ComposeMessageActivity)
I tried doing it, but it only showed me a toast saying "Application not installed" and I'm pretty sure it is.
It would be better if you can display a "complete action with another application" dialog.
If I assumed your question correctly, you mean a button or something within an activity that leads to another activity, that being -- "com.android.mms.ui.ComposeMessageActivity"
if your activity that you want to link to is in another application-- then
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.mine", "com.android.mms.ui.ComposeMessageActivity"));
startActivity(intent);
if it is within the same application, then
Intent intent = new Intent(this, ComposeMessageActivity.class);
startActivity(intent);
//optional add this to your manifest to finish the current loading activity so
//as to not keep it in the activity stack
//<activity android:name="yourActivity" android:noHistory="true" ... />
EDIT If you mean a shortcut on a homescreen, then I would create a tiny application that only has one activity which uses the above method to link to a different application. Then I would drag that application to the home screen, and boom. If there's a better way, then please feel free to correct me

Categories