Android | Print activity task stack/s in LogCat - android

I want to print all the tasks that my Android app has initiated with the names of the activities within them on LogCat. Is there any API in the SDK that can provide me this information?
p.s. I can't and don't want to use the adb shell commands as I want to print the logs in LogCat.
Note: I've searched quite a bit and all I've found are the adb shell commands which I can't use. Please keep that in mind while answering.
UPDATE:
Here's an example of what I want with 2 scenarios:
App starts with activity A, I finish it and start activity B. Then I
press a button on activity B that starts activity C. Now my default
task would like C -> B i.e. when I press back, I'll see activity
B and on pressing back app would finish and the launcher will be
displayed.
I open activity A, B & C consecutively then I launch an
activity X with intent flags Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK and then open activities Y & Z. The
current task will now look like Z -> Y -> X.
So, I want to print these in logcat:
C -> B in case 1
Z -> Y -> X in case 2

What I am trying to do isn't possible out of the box using APIs within the SDK as of now. More info can be found in this link:
How to get a list of my app's tasks and the stack of their Activities?
My Solution:
I had to add logging in base class of my app, printing the activity's name with its task ID to debug the problem I'm facing. Here's the code of my base activity class:
public abstract class BaseAppActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("TESTING", "CREATED: " + getClass().getSimpleName() + " -- TASK ID: " + getTaskId());
}
#Override
protected void onDestroy() {
super.onDestroy();
Log.i("TESTING", "DESTROYED: " + getClass().getSimpleName() + " -- TASK ID: " + getTaskId());
}
}

You can generate an activity state report in Android Studio. It gives you the states and routes of activities of the activity you are currently(running) in.
Find the System Information tab in Android Monitor.
Then, Activity Manager State.
Then, it shall generate you stack of your activities. Look for ACTIVITY(all caps).
Hope it helps, although if it is not the log approach.

As time moves on, some folks might have moved on to (Kotlin and) a "single activity with multiple fragments" pattern. Whichs' backstack we can log:
fun FragmentManager.printBackStack() {
Log.d("TAG", "BackStackEntryCount = $backStackEntryCount")
for (i in 0 until backStackEntryCount) {
Log.d("TAG", " #$i is ${getBackStackEntryAt(i)}")
}
}
Calling this from an activity would look like:
supportFragmentManager.printPackStack()
Please keep in mind, that fragment-transactions work asynchronously.
Therefore, the following code will produce unexpected results:
addSomeFragmentToBackStack("MyFragmentTag")
printBackStack()
// result doesn't include "MyFragmentTag"
Instead, you will need to execute the printing delayed:
addSomeFragmentToBackStack("MyFragmentTag")
Handler().postDelayed(
{ printBackStack() },
500 // ms delay
)
This solution definitely isn't perfect, but it's ok for debugging (e.g. will produce unexpected results when called in a loop due to the Handler)

It seems you are interested in logging life-cycle of your app(for components Activity,Fragments,App,Service,BroadcastReciever,etc). You can do so by making a super class and extend it to print Log in its life cycle method ,so that you need not to print it every time.You need to make a super-Activity,super-Fragment etc
For example following will log every time app is initialized by OS (by launcher, by BroadcastReciever)
public class LifeCycleApp extends Application {
String TAG = "GUFRAN " + LifeCycleApp.class.getName();
public LifeCycleApp() {
Log.d(TAG, "LifeCycleApp: constructor");
}
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate: ");
}
// Called by the system when the device configuration changes while your component is running.
// Overriding this method is totally optional!
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
// This is called when the overall system is running low on memory,
// and would like actively running processes to tighten their belts.
// Overriding this method is totally optional!
//Called when the overall system is running low on memory, and actively running processes should trim their memory usage
#Override
public void onLowMemory() {
super.onLowMemory();
}
#Override
protected void finalize() throws Throwable {
super.finalize();
Log.d(TAG, "finalize: ");
}
}
You might want to look at this
https://github.com/guffyWave/LifeCycle

According to Android Developers
Generally, you should use the Log.v(), Log.d(), Log.i(), Log.w(), and Log.e() methods to write logs. You can then view the logs in logcat.
import android.util.Log;
public class MyActivity extends Activity{
private static final String TAG = "MyActivity";
#Override
public void onCreate(Bundle bundle){
Log.v(TAG, "on create");
}
}
update
Since you want to track the active activities and you know how activity cycle works. The solution would be like this:
#Override
public void onPause(Bundle bundle){
Log.v(TAG," activity A paused"); // or whatever
}
Other solution would be to do this before the startActivity
something like this:
Intent i = new Intent(ThisActivity.class,AnotherActivity.class);
Log.v(TAG,"A->b");
// Log.v(TAG,"Z -> Y -> X"); or what ever message you want to print
startActivity(i);
Third solution would be to provide some info if you are not sure which activity will start which intent.
In activity A do this before starting the intent:
intent.putExtra("UActivity", "From A");
In activity B do this in onCreate:
String from = getIntent().getStringExtra("UActivity");
if("From A".equals(from){
Log.v(TAG,"A->B");
}else if("From C".equals(from){
Log.v(TAG,"C->B");
}// etc else if
so just follow up the activity lifecycle and print the correct log messages at the correct methods and this should make it work.

Related

how to detect app is now foreground after minimizing the app?

by using onResume() i can check if an activity is foreground or not. My problem is this onResume() is fire each time even if i come from another activity to this activity.
So my main problem is ,if i minimizing the app by home button I want execute some code in each activity when app is come foreground BUT not using onResume().
I found an answer like this How to know in BroadcastReceiver if App is running on foreground? but i dont know how to register this receiver in android menifest to get the trigger when app is visible.
Please give me some tips how can i overcome this solution or code snippet which can help me. Thanks in advance :)
One thing to do what you want is to count the number of times onStart/onStop is called in your application. This will help you determine if you transitioned to your activity from inside or outside your application.
You must extend Application then create/register ActivityLifecycleCallbacks within that class. Also, make sure to specify the new Application class you created in the AndroidManifest.
Now, the trick is to keep a count variable in onActivityStarted/onActivityStopped to determine whether your Activity was navigated to from inside or outside the application.
Say you have 2 Activities in your app: FirstActivity & SecondActivity.
If you navigate from FirstActivity to SecondActivity the lifecycle calls will happen in this order: FirstActivity.onStart() > SecondActivity.onStart(), resulting in a count of 1.
If you navigate from outside your application, you will only see FirstActivity.onStart(), so the count is 0. This is all assuming you check the count after super.onStart() is called.
So, by checking count against 0/1 you can tell if your activity was launched from within the application or outside the application.
/**
* Extending the application class lets you use ActivityLifecycleCallbacks to
* keep track of all lifecycle callbacks in your application.
*/
public class MyApplication extends Application implements ActivityLifecycleCallbacks {
private int count = 0;
//Register activity lifecycle callbacks in onCreate
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
}
void onActivityStarted(Activity activity) {
count++;
}
void onActivityStopped(Activity activity) {
count--;
}
/**
* Use this method in your Activities to test if the activity was
* transitioned to from outside the application.
*
* If you call this method in Activity.onResume(), then count should be
* compared to 0. If you call this method in Activity.onStart() but
* *before* calling super.onStart(), then count should be compared to 0.
*
* However, if you call this method after super.onStart(), then count
* should be compared to 1.
*/
public boolean cameFromOutsideApplication() {
return count == 0;
}
//Don't need to use the rest of the activity lifecycle callbacks
void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
void onActivityDestroyed(Activity activity) {
}
void onActivityPaused(Activity activity) {
}
void onActivityResumed(Activity activity) {
}
void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
}
You may gain some more information here as well but it does not use ActivityLifecycleCallbacks which is easier to use.
If you want to register your receiver you can use the following code,
<receiver
android:name="com.package.name.ReceiverClassName"
android:enabled="true" >
</receiver>

About launcher intent

I'm trying to make a certain function to start only when a user,
Opens the app for the first time,
Goes back to an app from home.
But not start if the user switches between activities within the app.
I have looked through this topic,and the best answer is to use singleTask with onNewIntent(). So, if a user is goes back to the app from Home, a onNewIntent call with the launcher intent passed to it can be used.
However, here is my code:
public class AdMobSDK_DFP_Interstitial extends Activity implements AdListener {
private static final String MOBMAX_INTERSTITIAL_AD_UNIT_ID = "/7732/test_portal7/android_app1_test_portal7/splash_banner_android_app1_test_portal7";
private DfpInterstitialAd interstitialAd;
private int num = 0;
public void onNewIntent(Intent intent){
super.onNewIntent(intent);
Log.d("flow", "onNewIntent");
}
If I switch between different activities in the app, onNewIntent() is always called, which is the same as I go back to the app from Home.
First thing you can do is to implement your own "Application" object and have it run the needed function when it is created.
public class MyApplication extends Application {
#Override
public void onCreate() {
// Call your function
}
}
Your application object will be live as long as your app is alive (any activity/service is still running), but note that the Application object is not destroyed immediately when the user presses "Home", and might stay alive for a while and a user can return to it without the function being called.
If you need this function to run as part of your main activity, just save a flag in your Application context :
public boolean alreadyDisplayed = false; and then in your activity's onStart you can just call
if ((MyApplication)getApplication().alreadyDisplayed ) {
// Call your function
(MyApplication)getApplication().alreadyDisplayed = true;
}
** If this solution is not enough for you and you need to call your function every time your main activity is displayed from the home page you'll need to do something not as nice... one suggestion I can give you is to implement the same Application object but this time with an "open activity" counter:
public class MyApplication extends Application {
public int mActivityCounter = 0;
}
Then you can increment this counter on every onStart of activity in your app and decrement on every onStop (of course this can be done by implementing a class MyActivity and make all your relevant activities inherit it. Then you can use this counter to know if there are any other activities opened. Note that you'll have to make sure the access to this counter is synchronized and work your way with it as you need.
I hope this helps...

How to always start from a startup activity on Android?

There are three different cases:
1) A user launches an app, navigates in it, pressed home and click on the app icon again to launch our app again.
2) A user launches an app, navigates in it, presses home, chooses recent and click on the app to launch our app again.
3) A user launches an app, navigates in it, click something in the app (TextView with a link), which calls another app (as example Email) and user clicks back button, which bring us back to our app.
I know about flag "clearTaskOnLaunch" flag, it solves case #1.
I know about about flag "excludeFromRecents", it solves case #2 (may be not the most user friendly solution, but it works).
What about case #3? I have a workaround right now. However, I will have to put it on all activities which can be lead to another app. I wonder, whether there is better way to solve it (without handling it in all such activities).
This should be handled on the Application level.
For API level 14, you can register an ActivityLifeCycleCallback in your Application class
public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)
You can use it, to know on an Application level, which activities are destroyed, paused, resumed etc etc. Whenever, an activity is paused, without a new activity being created/resumed, you should clear the Activity stack, and re-launch your startActivity
If you target SDK versions < 14, you should implement your own method, to know which activities are created/resumed and paused, and do the same whenever an activity is paused, without a new activity being created/resumed
1) define a public static normalPause = true variable in a Class.
2) in onPause method of all of your activities set it false (I am worry. We might not be in a normal pause)
2) in onCreate method of all of your activities set it true (Do not worry. We are in a normal pause)
3) in onResume of all of your Activities:
if(!Utilities.normalPause)
{
this.finish()
}
Enjoy!
It seems a similar question has already been asked. It sounds like the OP came up with a working solution. How do I collapse "child activities"?
EDIT:
Instead of using a button you can use a boolean to tell whether or not you need to collapse back to the main activity. Have your root activity extend from Activity and the child activities extend from CollapsableActivity. To get this to work in all cases I added startOutsideActivity() and startOutsideActivityForResult().
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class CollapsableActivity extends Activity {
private boolean returnToRoot;
public static final int COLLAPSE_BACK = -1; // something other than RESULT_CANEL (0)
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
returnToRoot = true;
}
#Override
protected void onStart() {
super.onStart();
returnToRoot = true;
}
#Override
protected void onRestart() {
super.onRestart();
// start collapsing the stack
if (returnToRoot) {
setResult(COLLAPSE_BACK);
finish();
}
}
#Override
public void startActivityForResult(Intent intent, int requestCode) {
super.startActivityForResult(intent, requestCode);
returnToRoot = false;
}
public void startOutsideActivityForResult(Intent intent, int requestCode) {
super.startActivityForResult(intent, requestCode);
returnToRoot = true;
}
#Override
public void startActivity(Intent intent) {
// call startActivityForResult to make sure and catch the collapse condition
super.startActivityForResult(intent, 0);
returnToRoot = false;
}
public void startOutsideActivity(Intent intent) {
super.startActivity(intent);
returnToRoot = true;
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == COLLAPSE_BACK) {
returnToRoot = true;
}
}
}
This worked properly for me in all cases you listed. The only difference is you need to call startOutsideActivity() or startOutsideActivityForResult() when you navigate away from you app. Personally, I think this adds clarity to your intentions. Hope it helps!
I know you don't want to manage it in all activities but you can do this and still handle the code in one place with a super activity
public abstract class BlundellActivity extends Activity {
#Override
public void onPause(){
// Whatever strategy you want
}
}
public class SomeActivity extends BlundellActivity {
// Do whatever you normally want to do
}
public class SomeActivity extends BlundellActivity {
// Do whatever you normally want to do here as well
}
Perhaps, android:noHistory is what you're looking for. If you declare all your activities except StartupActivity with this attribute, then they will be finished as the user navigates away from them and only StartupActivity will appear.
You can try this steps:
use one boolean static flag isFinish in StartupActivity with default false value.
in onCreate() of StartupActivity set isFinish value to false.
write below code in onResume() method of all activities in your project.
if(isFinish)
{
finish();
}
set isFinish value to true when you open any native app like email, browser etc.
or
5 . set isFinish value to true in onBackPress() method whenever you want to close application on back press.
Case 6: if android browser open on clicking on any link then use below code is onPause() method
if(isBrowserRunning("com.android.browser"))
{
isFinish = true;
finish();
}
////////////////
private boolean isBrowserRunning(String processName)
{
ActivityManager manager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
String packageName = manager.getRunningTasks(1).get(0).topActivity.getPackageName();
Log.i("LogTest", "Current process package name: " + packageName);
return processName.equalsIgnoreCase(packageName);
}
You can create a sample project to know other browser package name like opera mini, US browser etc.
add below permission in manifest:
<uses-permission
android:name="android.permission.GET_TASKS" />
You can call this.finish() on the onPause() of your Activity, that way the activity will be closed in the three cases.
You need to use bundle and pass appropriate parameter/or parameters from the calling app (i.e. click something in the app (TextView with a link)).
Retrieve the parameter in the called app (Email app).
You can send the name of the activity in the parameter.
Now being in Email app(the called app) Click of back button navigate back to your calling application.
Optionally you can save the state of activity from the caller program, as required.
You need to use Bundle, and Intent to implement this logic.
Code snippet:
In the calling program, we need to store parameters/data required for back button functionality in the called program.
Bundle bndleData = new Bundle();
Use putString(), putInt() methods of Bundle class.
String prefix = getPackageName().toString();
(this prefix can be stored in application level constants.java file as applicable)
bndleData.putString("ParentActivity", this.getLocalClassName());
Also store additional parameters if required
bndleData.putString("paramName", valueofParamName);
bndleData.putInt("IntChannelImage", chImageInt);
Intent intent = new Intent(v.getContext(), AMRChannelPlayer.class);
intent.putExtra(prefix + "bndleChnlData", bndleData);
startActivity(intent);
Caller Program:
Retrive the data, activity nae from bundle and use it in back button implementation:
prefix = getPackageName().toString();
Bundle extras = getIntent().getBundleExtra(prefix + "bndleData");
String parentActivity = extras.getString("ParentActivity");
extras.getString("paramName");
I hope this helps you.
Instead of using multiple solutions you can use a single one that solves all the problems.
Check this answer:
https://stackoverflow.com/a/8576529/327011
With a Broadcast and BroadcastReceivers in each activities of your application you can kill all activities whenever your application goes to background.
UPDATE:
To detect if your application when to background you can use onStop, check this to understand the theory: Activity side-by-side lifecycle
And this is the implementation: https://stackoverflow.com/a/5862048/327011
I think this is all you need :-)

Application Launch Count

I am working on an application, wherein after say 5 times the app is opened by a user, at 6th attempt the app should ask for feedback from user. I tried using Activity OnStart,OnResume, but its not working out since even after leaving and re-entering activity these methods are called. Also as per android functionality, I cannot quit app so that I can find it out from the first activity called. How do I find how many times the app was launched?
I hope this is not confusing.
Edit
Alternatively is there a way, wherein I can always resume my app from the first activity( or welcome page for eg.), once user presses home to quit the app.
This is actually quite simple. Using SharedPreference or the Database.
during OnCreate add 1 to the numberofTimes counter and commit.
OnCreate (Bundle bundle){
mPref = getPreferences();
int c = mPref.getInt("numRun",0);
c++;
mPref.edit().putInt("numRun",c).commit();
//do other stuff...
}
OnCreate is called regardless of you start the app or you resume the app, but isFinishing() returns true if and only iff the user (or you) called finish() on the app (and it was not being destroyed by the manager)
This way you only increment when you are doing fresh start.
the isFinishing() Method inside of a OnPause method to check to see if the activity is being finish() or just being paused.
#Override
protected void OnPause(){
if(!isFinishing()){
c = mPref.getInt("numRun",0);
c--;
mPref.edit().putInt("numRun",c).commit();
}
//Other pause stuff.
}
This covers all your scenarios:
1. user starts app/activity (+1)-> finishes app, exit with finish()
2. user starts app (+1) -> pause (-1) -> returns (+1)-> finish
3. user starts app (+1) -> pause (-1) -> android kills process (0) -> user returns to app (+1) -> user finish.
every scenario you only increment the "times run" counter once per "run" of the activity
Just:
declare:
private SharedPreferences prefs;
private SharedPreferences.Editor editor;
private int totalCount;
initialize in onCreate():
prefs = getPreferences(Context.MODE_PRIVATE);
editor = prefs.edit();
print or count wherever you want (any where in onCreate() or any specific click as you specified):
totalCount = prefs.getInt("counter", 0);
totalCount++;
editor.putInt("counter", totalCount);
editor.commit();
now print totalCount where you want to count e.g.:
System.out.println("Total Application counter Reach to :"+totalCount);
if you have a starting activity for app launch then you can implement it in following ways
1. Database:- through database you can save your application launch count and retrieve it on create of activity.
Static Variable:- static variable also retain values during application start and end
Application Preference:-you can store value in application preference and use it
problem with 2 and 3 approach is that if you switch off and on again your phone you will loose data. but if you still want to use 2 or 3 approach then 2 approach is very simple and
sample code for 3rd approach here
well you have to extends Application class and create a subclass from that
public class MyApp extends Application{
int visitCount;
onCreate(){
visitCount=0;
}
and you can mention it in your menifest file like
<application name="MyApp">
.....
</application>
and in onCreate of your activity you can get it by
MyApp myApp=(MyApp)getApplicationContext();
Edit1:
subclass your activity and override method
public class myActivity extends Activity{
#Override
onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
counterFlag=true;
}
}
it is get called when user press home button
and again override onResume() and check whether your counter flag is enabled or not
and create all your activity by subclassing your MyActivity
also if any other activity has exit point on click of back button then you can override
#Override
public void back_pressed(){
}
and do your task accordingly
I think this would be the best option in order to cover all scenarios:
private static boolean valueOfLaunchCountModified = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
if(!valueOfCountModified){
preferences = getPreferences(MODE_PRIVATE);
launchCount= preferences.getInt("launchCount", 0);
if(preferences.edit().putInt("launchCount", ++launchCount).commit()){
valueOfCountModified = true;
if(launchCount == 5){
//Do whatever you want
}
}
}
}
If we remember the definition of a static variable ("...They are associated with the class, rather than with any object. Every instance of the class shares a class variable...") we will discover that is perfect for us.
When onPause method or an orientation change is executed the value of "valueOfLaunchCountModified" doesn't change; however, if the app process is destroyed, the value of "valueOfLaunchCountModified" changes to false.
If you only want to count "true" invocations then extend Application and place counter logic into Application#onCreate. This could be a simple preference
I prefer to use onResume to track launch count since it’s getting called in every scenario (refer to Android Activity Lifecycle) when the activity is shown.
onResume could be called quite frequently depending on usage pattern, so instead of tracking launch count, it would be better to track launch session (as in only 1 launch count would be tracked per hour).
#Synchronized fun appSessionCount(sharedPref: SharedPreferences): Boolean {
val now = LocalDateTime.now(ZoneOffset.UTC)
val firstSeconds = sharedPref.getLong(KEY_FIRST_LAUNCH_DATE, 0)
if (firstSeconds == 0L) {
sharedPref.edit {
putLong(KEY_FIRST_LAUNCH_DATE, now.atZone(ZoneOffset.UTC).toEpochSecond())
}
}
val seconds = sharedPref.getLong(KEY_LAST_SESSION_DATE, 0)
val lastDate = if (seconds > 0) LocalDateTime.ofInstant(Instant.ofEpochSecond(seconds), ZoneOffset.UTC) else null
var count = sharedPref.getLong(KEY_SESSION_COUNT, 0)
// first time or 1 hour ago
if (lastDate == null || Duration.between(lastDate, now).toHours() >= 1) {
sharedPref.edit {
putLong(KEY_SESSION_COUNT, count + 1)
putLong(KEY_LAST_SESSION_DATE, now.atZone(ZoneOffset.UTC).toEpochSecond())
}
return true
}
return false
}
I run the code at onResume of my main activity.
class MainActivity : AppCompatActivity() {
lateinit var sharedPref: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
sharedPref = getSharedPreferences("LuaApp", Context.MODE_PRIVATE)
}
override fun onResume() {
super.onResume()
appSessionCount(sharedPref)
}
}
https://code.luasoftware.com/tutorials/android/android-track-app-launch-count/

How to display log message when an activity is started in Android

I am in one activity say Activity A. I am calling another activity B.
In activity A I write the following Statement:
startActivity(new Intent(this,ActivityB.class));
Now, I want to log a debug message only when Activity A is able to successfully launch ActivityB.
How can I achieve this feature in Android.
Any kind of Help is appreciated
Thanks,
You can use the startActivityForResult method to achieve this. Use this in your Activity "A"
Or else if thats not what you are looking for, I am guessing you want to log when activity B starts and stos. Activity class provides lifecycle methods for start, stop, pause resume, etc.
Override them and put your logging code in there:
public class B extends Activity {
private static final String TAG = "B-Activity";
#Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "Activity created"); // or Log.d for debug
}
#Override
public void onStart() {
Log.i(TAG, "Activity started"); // or Log.d for debug
}
}

Categories

Resources