Automatically log Android lifecycle events using ActivityLifecycleCallbacks? - java

I am trying to automatically capture and log Android lifecycle events using ActivityLifecycleCallbacks, however documentation on this matter is scarce, to say the least:
public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)
I don't want to have to extend the Activity class or override the existing lifecycle methods (onCreate, onResume, etc...) I'm looking to have a separate class listening for these events and acting accordingly.
Does anyone have any experience in this, or have links to good solid documentation or tutorials on how this works? Specifically, how to register for ActivityLifecycleCallbacks, and how to handle them?

I don't have any firsthand experience but judging from the API you can just write your own class that implements the Application.ActivityLifecycleCallbacks interface and register that class on the provided Application class instance
getApplicaton().registerActivityLifecycleCallbacks(yourCustomClass);
This class will receive the same callbacks as your individual activities. Good luck.
PS. This is API level 14 btw, so it won't work on older phones.

I did my own implementation of Application.ActivityLifecycleCallbacks. I'm using SherlockActivity, but for normal Activity class might work.
First, I'm creating an interface that have all methods for track the activities lifecycle:
public interface ActivityLifecycleCallbacks{
public void onActivityStopped(Activity activity);
public void onActivityStarted(Activity activity);
public void onActivitySaveInstanceState(Activity activity, Bundle outState);
public void onActivityResumed(Activity activity);
public void onActivityPaused(Activity activity);
public void onActivityDestroyed(Activity activity);
public void onActivityCreated(Activity activity, Bundle savedInstanceState);
}
Second, I implemented this interface in my Application's class:
public class MyApplication extends Application implements my.package.ActivityLifecycleCallbacks{
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onActivityStopped(Activity activity) {
Log.i("Tracking Activity Stopped", activity.getLocalClassName());
}
#Override
public void onActivityStarted(Activity activity) {
Log.i("Tracking Activity Started", activity.getLocalClassName());
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
}
#Override
public void onActivityResumed(Activity activity) {
Log.i("Tracking Activity Resumed", activity.getLocalClassName());
}
#Override
public void onActivityPaused(Activity activity) {
Log.i("Tracking Activity Paused", activity.getLocalClassName());
}
#Override
public void onActivityDestroyed(Activity activity) {
Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
}
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
Log.i("Tracking Activity Created", activity.getLocalClassName());
}
}
Third, I'm creating a class that extends from SherlockActivity:
public class MySherlockActivity extends SherlockActivity {
protected MyApplication nMyApplication;
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
nMyApplication = (MyApplication) getApplication();
nMyApplication.onActivityCreated(this, savedInstanceState);
}
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
nMyApplication.onActivityResumed(this);
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
nMyApplication.onActivityPaused(this);
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
nMyApplication.onActivityDestroyed(this);
}
#Override
protected void onStart() {
super.onStart();
nMyApplication.onActivityStarted(this);
}
#Override
protected void onStop() {
super.onStop();
nMyApplication.onActivityStopped(this);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
nMyApplication.onActivitySaveInstanceState(this, outState);
}
}
Fourth, all class that extend from SherlockActivity, I replaced for MySherlockActivity:
public class MainActivity extends MySherlockActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
Now, in the logcat you will see the logs programmed in the Interface implementation made in MyApplication.
UPDATE
This implementation was tested from API Level 9 (Gingerbread), API Level 12 (Honeycomb) and API Level 17 (Jelly Bean) and works fine. Might works in Android's older versions.

Try this: http://engineering.meetme.com/2015/04/android-determine-when-app-is-opened-or-closed/#comment-202
It proposes an AppForegroundStateManager to which each activity reports through its onStop() and onStart() functions like this:
#Override
protected void onStart() {
super.onStart();
AppForegroundStateManager.getInstance().onActivityVisible(this);
}
#Override
protected void onStop() {
AppForegroundStateManager.getInstance().onActivityNotVisible(this);
super.onStop();
}
Your Application class implements a listener like this:
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
AppForegroundStateManager.getInstance().addListener(this);
}
#Override
public void onAppForegroundStateChange(AppForegroundStateManager.AppForegroundState newState) {
if (AppForegroundStateManager.AppForegroundState.IN_FOREGROUND.equals(newState)) {
// App just entered the foreground. Do something here!
Log.i(TAG, "App Just Entered the Foreground with launch mechanism of: " + mLaunchMechanism);
} else {
// App just entered the background. Set our launch mode back to the default of direct.
mLaunchMechanism = LaunchMechanism.DIRECT;
}
}
}
It also includes tips and tricks for determining how the app was opened - from a notification, a URL opening your app or directly from the Apps menu. This is done through an Enum in the Application class:
public enum LaunchMechanism {
DIRECT,
NOTIFICATION,
URL,
BACKGROUND
}
private LaunchMechanism mLaunchMechanism = LaunchMechanism.DIRECT;
public void setLaunchMechanism(LaunchMechanism launchMechanism) {
mLaunchMechanism = launchMechanism;
}
In our implementation of this, we have flags for when we start an activity that will launch a third-party activity, like if the user makes a phone call from our app or if a browser is launched. In the launching activity's onStop() we then do a check like this to only report the activity's not-visibility when those flags are false:
if(!flag_userLaunchedThirdPartyActivity){
AppForegroundStateManager.getInstance().onActivityNotVisible(this);
}
For checking whether or not the application goes into the background - for example when the device's screen goes dark or the user receives a phone call - it works like this:
public static boolean isApplicationGoingToBackground(final Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> tasks = am.getRunningTasks(1);
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
if (!topActivity.getPackageName().equals(context.getPackageName())) {
setLaunchMechanism(LaunchMechanism.BACKGROUND);
return true;
}
}
setLaunchMechanism(LaunchMechanism.DIRECT);
return false;
}
This solution is not dependent on an API level, so it should work all the way back to API level 1.

#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(MyApplication.this/*(Your Application Name)*/);
}
Only add this line on Application class and all works well.

Related

How to check internet connectivity using broadcast receiver and Change the intent

I am working on an Android application which requires constant listener of Internet connectivity. I am using Broadcast listener and successfully applied it. But my code only shows the Toast message.
I want to stop the current activity and show a default XML file which says "No Internet Connection". and whenever it connect the Internet, previous activity resumes.
ExampleBradcastReceiver.java
public class ExampleBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
boolean noConnectivity = intent.getBooleanExtra(
ConnectivityManager.EXTRA_NO_CONNECTIVITY, false
);
if (noConnectivity) {
Toast.makeText(context, "Disconnected", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "Connected", Toast.LENGTH_SHORT).show();
}
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
ExampleBroadcastReceiver exampleBroadcastReceiver = new ExampleBroadcastReceiver();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(exampleBroadcastReceiver, filter);
}
#Override
protected void onStop() {
super.onStop();
unregisterReceiver(exampleBroadcastReceiver);
}
}
In the place of Toast Message, I want to show a default XML file whenever disconnected and resume activity whenever connected.
You can move ExampleBroadcastReceiver to MainActivity as an inner class. And since in Java inner classes have access to their parent classes' methods and fields, you can in onReceive method consider showing/hiding the Internet disconnected view.
public class MainActivity extends AppCompatActivity {
ExampleBroadcastReceiver exampleBroadcastReceiver = new ExampleBroadcastReceiver();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(exampleBroadcastReceiver, filter);
}
#Override
protected void onStop() {
super.onStop();
unregisterReceiver(exampleBroadcastReceiver);
}
private void showInternetDisconnectedView(boolean disconnected){
// show or hide based on 'disconnected'
}
private class ExampleBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
boolean noConnectivity = intent.getBooleanExtra(
ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
showInternetDisconnectedView(noConnectivity);
}
}
}
}
You need to move Broadcast receiver code into Activity and on receiving internet connection events you can stop current in progress activity and make internet failure layout visible there only as it is part of Activity class. If it is required through out the Application, then create Base activity and handle this there to avoid duplicating code on every screen.

How to check the which activity is currently running while debugging the app

I am stuck with a project developed by someone else. Its a very big app. So for the purpose of understanding the code I want to know the flow, basically the name of the activity(the java file in the code) which is currently running in on my phone. I can put break-points in all activities, but is there any other way?
You can use adb for it.
In your terminal run adb shell dumpsys activity top.
Using ActivityLifecycleCallbacks, you don't need to add handling for all the activities. Do the registration once, it works for ALL activities, and that is it!
public class MyApplication extends Application implements
ActivityLifecycleCallbacks{
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onActivityStopped(Activity activity) {
Log.i("Tracking Activity Stopped", activity.getLocalClassName());
}
#Override
public void onActivityStarted(Activity activity) {
Log.i("Tracking Activity Started", activity.getLocalClassName());
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
}
#Override
public void onActivityResumed(Activity activity) {
Log.i("Tracking Activity Resumed", activity.getLocalClassName());
}
#Override
public void onActivityPaused(Activity activity) {
Log.i("Tracking Activity Paused", activity.getLocalClassName());
}
#Override
public void onActivityDestroyed(Activity activity) {
Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
}
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
Log.i("Tracking Activity Created", activity.getLocalClassName());
}
}
Note that you can identify which activity via activity.getLocalClassName().
Simple -
In each activity override onStart() method and print log like this -
#Override
protected void onStart() {
super.onStart();
Log.d("TAG -> ", "YOUR_ACTIVITY_NAME");
}
When activity start in your device, it will print log in logcat. Hope it will help!
From manifest figure out Launching activity and to find out methods and variables used.. u can use "Find Usages" ( Ctrl+G ).
If your application extends MultiDexApplication class here is a working solution.
In onCreate() method of your application class that extends MultiDexApplication add the following line to register the callbacks.
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new MyActivityLifecycleCallbacks());
}
Now make an inner class that implements ActivityLifecycleCallbacks as follow
private static final class MyActivityLifecycleCallbacks implements ActivityLifecycleCallbacks {
public void onActivityCreated(Activity activity, Bundle bundle) {
Log.v("Application","Created");
}
public void onActivityDestroyed(Activity activity) {
Log.v("Application","Destroyed");
}
public void onActivityPaused(Activity activity) {
Log.v("Application","Paused");
}
public void onActivityResumed(Activity activity) {
Log.v("Application","Resumed");
}
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
Log.v("Application","onActivitySaveInstanceState");
}
public void onActivityStarted(Activity activity) {
Log.v("Application","Started");
}
public void onActivityStopped(Activity activity) {
Log.v("Application","Stopped");
}
}
Now you may use activity.getLocalClassName() in any overridden method you need.
This would be all you need. Happy Debugging :)

Greenrobot EventBus3 not posting to my subscribers

In my application, I pass a job into the job-queue and in my Job queue, I've registered my EventBus and I am trying to post the event with the hopes that my subscriber method would receive it. I have just recently migrated from Otto's EventBus and while this way had previously worked, it is not working for greenrobots EventBus 3.
Here is a sample case for what I'm trying to achieve:
TestPresenterImpl.class (This class has been instantiated the TestActivity.class)
#Override
public void addJob(JobData jobData) {
jobManager.addJobInBackground(new SendUpdateJob(jobData));
}
#Subscribe
#Override
public void onUpdate(JobAddedEvent event) {
if (event.getStatus() == 1) {
Log.i(LOG_TAG, "test");
}
}
#Override
public void onStart() {
mBus.getDefault().register(this);
}
#Override
public void onStop() {
mBus.getDefault().unregister(this);
}
SendUpdateJob.class (This is the class that handles the jobs in the queue using the android-priority-job queue previously maintained by Path)
#Override
public void onAdded() {
Log.i(LOG_TAG, "On added");
mBus.getDefault().register(this);
JobAddedEvent event = new JobAddedEvent();
event.setStatus(1);
mBus.getDefault().post(event);
}
Normally this works on Otto but since this is a slightly different, I want to know what I'm doing incorrectly. The error I get is: ..."SendJobUpdate and its super classes have no public methods with the #Subscribe annotation". Am I doing something wrong here?
I've also made sure that I'm importing import org.greenrobot.eventbus.Subscribe;
because I've noticed others have pointed out that they have imported googles subscribe.
Your EventBus.getDefault().register(this); should follow your Activity which your #Subscribe method is in, not in mBus.getDefault().post(event); Activity.
FirstActicity
public class FirstActicity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
//register should in this activity
EventBus.getDefault().register(this);
}
#Subscribe(threadMode = ThreadMode.MainThread)
public void onEvent(MessageEvent event) {
//eventbus notify
Log.i("2hh", "main --- eventbus ---- updatedata");
queryData();
}
#Override
protected void onDestroy() {
if (EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().unregister(this);
}
}
SecondActivity(don't register in this activity beacuse you didn't hava any #Subscribe method)
public class SecondActivity extends AppCompatActivity implements{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add);
EventBus.getDefault().post(new MessageEvent("updateData"));
}
}

Call function from Activity Class after preferences are updated Android

I would like to call a function from my main activity class after my preferences are updated, but I can't use onResume() because I have other classes that will trigger onResume (I believe).
Snippet from AndroidGPSTrackingActivity.java:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.settings:
startActivity(new Intent(this, SettingsActivity.class));
break;
}
return false;
}
SettingsActivity.java:
package com.example.gpstracking;
import android.os.Bundle;
import android.preference.PreferenceActivity;
public class SettingsActivity extends PreferenceActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
After my preferences close, I want to call a function from my Main Activity, however it MUST be from my main activity because I need to send a context from my Main Activity. I've tried so much and just can't seem to get it.
onResume() is triggered when the Activity comes to the foreground. You should never be "triggering" onResume() other than leaving/returning to the Activity (if you're ever directly calling onResume(), you're doing it wrong).
Assuming you're needing to check for the updated value when you return to your Activity, then yes, onResume() is where you should probably do that. If the user returns to MainActivity after viewing SettingsActivity, then onResume() will be called, and you can check the updated preference value there.
EDIT: So I think honestly you'd be better off checking the preference values in onResume(), and not caching them globally. They aren't expensive to look up. Also, for immediate notifications, you could register a listener for preference changes, like so:
public class MainActivity extends Activity
implements SharedPreferences.OnSharedPreferenceChangeListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerPreferenceListener();
}
#Override
protected void onDestroy() {
super.onDestroy();
unregisterPreferenceListener();
}
private void registerPreferenceListener() {
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this);
}
private void unregisterPreferenceListener() {
PreferenceManager.getDefaultSharedPreferences(this)
.unregisterOnSharedPreferenceChangeListener(this);
}
#Override
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
if ("the_key_i'm_interested_in".equals(key)) {
// Update stuff
}
}
}

file to recall in other classes: how to do?

how do I make a file that I can recall in some classes without having to paste the same code each time? For example, I have to add this code to google analystics but since I do not want to paste the same code each time how can I create a file to recall in the other classes? The code is
#Override
public void onStart() {
super.onStart();
EasyTracker.getInstance().setContext(this);
EasyTracker.getInstance().activityStart(this);
}
#Override
public void onStop() {
super.onStop();
EasyTracker.getInstance().setContext(this);
EasyTracker.getInstance().activityStop(this);
}
You could use a static method and call it in each class
public static void onStart(MyInterface curclass)
{
curclass.onStart();
EasyTracker.getInstance().setContext(curclass);
EasyTracker.getInstance().activityStart(curclass);
}
public static void onStop(MyInterface curclass)
{
curclass.onStop();
EasyTracker.getInstance().setContext(curclass);
EasyTracker.getInstance().activityStop(curclass);
}
Keep in mind that the classes you call this on need to implement an interface with the method onStart() and onStop().
Create a parent class:
public abstract class TrackedActivity extends Activity {
#Override
public void onStart() {
super.onStart();
EasyTracker.getInstance().setContext(this);
EasyTracker.getInstance().activityStart(this);
}
#Override
public void onStop() {
super.onStop();
EasyTracker.getInstance().setContext(this);
EasyTracker.getInstance().activityStop(this);
}
}
And let the activities that you want to track extend this class (rather than Activity), so if you have
public MyActivity extends Activity {
// ...
}
change this to
public MyActivity extends TrackedActivity {
// ...
}

Categories