Related
This question already has answers here:
Determine if Android app is being used for the first time
(15 answers)
Closed 6 years ago.
I am new to android development and and I want to setup some of application's attributes based on Application first run after installation. Is there any way to find that the application is running for the first time and then to setup its first run attributes?
The following is an example of using SharedPreferences to achieve a 'first run' check.
public class MyActivity extends Activity {
SharedPreferences prefs = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Perhaps set content view here
prefs = getSharedPreferences("com.mycompany.myAppName", MODE_PRIVATE);
}
#Override
protected void onResume() {
super.onResume();
if (prefs.getBoolean("firstrun", true)) {
// Do first run stuff here then set 'firstrun' as false
// using the following line to edit/commit prefs
prefs.edit().putBoolean("firstrun", false).commit();
}
}
}
When the code runs prefs.getBoolean(...) if there isn't a boolean saved in SharedPreferences with the key "firstrun" then that indicates the app has never been run (because nothing has ever saved a boolean with that key or the user has cleared the app data in order to force a 'first run' scenario). If this isn't the first run then the line prefs.edit().putBoolean("firstrun", false).commit(); will have been executed and therefore prefs.getBoolean("firstrun", true) will actually return false as it overrides the default true provided as the second parameter.
The accepted answer doesn't differentiate between a first run and subsequent upgrades. Just setting a boolean in shared preferences will only tell you if it is the first run after the app is first installed. Later if you want to upgrade your app and make some changes on the first run of that upgrade, you won't be able to use that boolean any more because shared preferences are saved across upgrades.
This method uses shared preferences to save the version code rather than a boolean.
import com.yourpackage.BuildConfig;
...
private void checkFirstRun() {
final String PREFS_NAME = "MyPrefsFile";
final String PREF_VERSION_CODE_KEY = "version_code";
final int DOESNT_EXIST = -1;
// Get current version code
int currentVersionCode = BuildConfig.VERSION_CODE;
// Get saved version code
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
int savedVersionCode = prefs.getInt(PREF_VERSION_CODE_KEY, DOESNT_EXIST);
// Check for first run or upgrade
if (currentVersionCode == savedVersionCode) {
// This is just a normal run
return;
} else if (savedVersionCode == DOESNT_EXIST) {
// TODO This is a new install (or the user cleared the shared preferences)
} else if (currentVersionCode > savedVersionCode) {
// TODO This is an upgrade
}
// Update the shared preferences with the current version code
prefs.edit().putInt(PREF_VERSION_CODE_KEY, currentVersionCode).apply();
}
You would probably call this method from onCreate in your main activity so that it is checked every time your app starts.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkFirstRun();
}
private void checkFirstRun() {
// ...
}
}
If you needed to, you could adjust the code to do specific things depending on what version the user previously had installed.
Idea came from this answer. These also helpful:
How can you get the Manifest Version number from the App's (Layout) XML variables?
User versionName value of AndroidManifest.xml in code
If you are having trouble getting the version code, see the following Q&A:
How to get the build/version number of your Android application?
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.UUID;
import android.content.Context;
public class Util {
// ===========================================================
//
// ===========================================================
private static final String INSTALLATION = "INSTALLATION";
public synchronized static boolean isFirstLaunch(Context context) {
String sID = null;
boolean launchFlag = false;
if (sID == null) {
File installation = new File(context.getFilesDir(), INSTALLATION);
try {
if (!installation.exists()) {
launchFlag = true;
writeInstallationFile(installation);
}
sID = readInstallationFile(installation);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return launchFlag;
}
private static String readInstallationFile(File installation) throws IOException {
RandomAccessFile f = new RandomAccessFile(installation, "r");// read only mode
byte[] bytes = new byte[(int) f.length()];
f.readFully(bytes);
f.close();
return new String(bytes);
}
private static void writeInstallationFile(File installation) throws IOException {
FileOutputStream out = new FileOutputStream(installation);
String id = UUID.randomUUID().toString();
out.write(id.getBytes());
out.close();
}
}
> Usage (in class extending android.app.Activity)
Util.isFirstLaunch(this);
There is no way to know that through the Android API. You have to store some flag by yourself and make it persist either in a SharedPreferenceEditor or using a database.
If you want to base some licence related stuff on this flag, I suggest you use an obfuscated preference editor provided by the LVL library. It's simple and clean.
Regards,
Stephane
I'm not sure it's good way to check it. What about case when user uses button "clear data" from settings? SharedPreferences will be cleared and you catch "first run" again. And it's a problem. I guess it's better idea to use InstallReferrerReceiver.
Just check for some preference with default value indicating that it's a first run. So if you get default value, do your initialization and set this preference to different value to indicate that the app is initialized already.
The following is an example of using SharedPreferences to achieve a 'forWhat' check.
preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferencesEditor = preferences.edit();
public static boolean isFirstRun(String forWhat) {
if (preferences.getBoolean(forWhat, true)) {
preferencesEditor.putBoolean(forWhat, false).commit();
return true;
} else {
return false;
}
}
There's no reliable way to detect first run, as the shared preferences way is not always safe, the user can delete the shared preferences data from the settings!
a better way is to use the answers here Is there a unique Android device ID? to get the device's unique ID and store it somewhere in your server, so whenever the user launches the app you request the server and check if it's there in your database or it is new.
This might help you
public class FirstActivity extends Activity {
SharedPreferences sharedPreferences = null;
Editor editor;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
sharedPreferences = getSharedPreferences("com.myAppName", MODE_PRIVATE);
}
#Override
protected void onResume() {
super.onResume();
if (sharedPreferences.getBoolean("firstRun", true)) {
//You can perform anything over here. This will call only first time
editor = sharedPreferences.edit();
editor.putBoolean("firstRun", false)
editor.commit();
}
}
}
SharedPreferences mPrefs;
final String welcomeScreenShownPref = "welcomeScreenShown";
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
// second argument is the default to use if the preference can't be found
Boolean welcomeScreenShown = mPrefs.getBoolean(welcomeScreenShownPref, false);
if (!welcomeScreenShown) {
// here you can launch another activity if you like
SharedPreferences.Editor editor = mPrefs.edit();
editor.putBoolean(welcomeScreenShownPref, true);
editor.commit(); // Very important to save the preference
}
}
Hi After doing lots of Search , I could not find the answer of a question, that seems somewhat simple.
I have multiple apps installed on device. Is there any way of finding the last launching date of all apps?
You could put the time and date in an SharedPref when the app opens.
Then, the next time you open the app the app reads the SharedPref and displays it.
Something like this:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Calendar c = Calendar.getInstance();
SharedPreferences sp = this.getPreferences(Context.MODE_PRIVATE);
String lastLaunch = sp.getString("launch", "First launch!");
SharedPreferences.Editor editor = sp.edit();
editor.putString("launch", c.getTime().toString());
editor.commit();
}
The String lastLaunch is the last time it launched! If it's the first time the string is: "First launch!"
I hope that i have helped you a little bit :)
Based on your requirements from the comments above, I'm providing a method to share the launch times between a bunch of our apps using SharedPreferences.
Firstly, we need to allow our apps to share local storage so they can access preference data. For this, we use the sharedUserId property in the app manifest. This can be your own unique string and each of the apps that share the space must have this:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="example.com.myapp"
android:sharedUserId="string.user.id.here">
...
</manifest>
Secondly, we use the Application class to determine when the app comes to the foreground and when it does, write the timestamp in Context.CONTEXT_IGNORE_SECURITY mode to share between our apps:
public class MyApp extends Application implements ActivityLifecycleCallbacks {
private boolean appInBg;
private int TIMEOUT = 5000;
private Handler mHandler = new Handler();
#Override
public void onActivityResumed() {
if (appInBg) {
writeToPref();
} else {
mHandler.cancelCallbacksAndMessages(null);
}
}
#Override
public void onActivityPaused() {
mHanlder.postDelayed(new Runnable() {
#Override
public void run() {
appInBg = true;
}
}, TIMEOUT);
}
...
private void writeToPref() {
SharedPreferences prefs = myContext.getSharedPreferences("run_prefs", Context.CONTEXT_IGNORE_SECURITY);
prefs.edit().putInt("last_run", System.currentTimeMillis()).apply();
}
}
Here, we allow a buffer time of 5 seconds to switch between screens. This should be adequate in most cases.
Finally, we can read the written SharedPreference value as follows:
Context context = createPackageContext("example.com.myapp", 0);
SharedPreferences pref = context.getSharedPreferences("run_prefs", Context.CONTEXT_IGNORE_SECURITY);
int lastLaunch = pref.getString("last_run", System.currentTimeMillis());
// Similarly, for other apps.
Hope this solves your issue.
I made a class for handling important data changes such as App Purchase Status and other stuff .
For this goal I have created a class which does the setting and reading of the values. but the problem is whenever I call the appIsPurchased() method, the result is true while it hasen't been changed since app installation and its first initial launch.
This is my code:
/**
* Created by neemasa on 5/29/14.
* This class handles more crucial data values within app.
*/
public class AppCore {
private SharedPreferences settings;
private String keyPurchase = "app_purchased";
private Context context;
public AppCore(Context context){
this.context = context;
settings = PreferenceManager.getDefaultSharedPreferences(context);
}
public void setAppInPurchasedMode(String status){
if (status.equals("successful")){
settings.edit().putBoolean(keyPurchase, true).commit();
}else if (status.equals("failed")){
settings.edit().putBoolean(keyPurchase, false).commit();
}
}
public boolean appIsPurchased(){
boolean purchased = false;
if (settings.getBoolean(keyPurchase,true)){
purchased = true;
}
return purchased;
}
}
Question 1st: is there something wrong with my code? if there is then why appIsPurchased() always return true?
Question 2nd: do all values in the shared preferences are true by default?
Meanwhile when I use this class in my code the toast "Purchased!" runs even when app is running for the first time.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AppCore appCore = new AppCore(getApplicationContext());
if (appCore.appIsPurchased()){
Toast.makeText(getApplicationContext(),"Purchased!",Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(getApplicationContext(),"Not Purchased!",Toast.LENGTH_SHORT).show();
}
}
Actually there is a problem in your code!! thats why its always showing purchased!!
if (settings.getBoolean(keyPurchase,true)){
purchased = true;
}
in this lines if the keyPurchased tag if not used , u are passing true value by default
so when u call
if (appCore.appIsPurchased()){
it always return a true value.. The solution is that make sure that the preference values are set before u call them.. hope this helps
Found It, the problem is that I was thinking
settings.getBoolean(keyPurchase,false)
returns the value of keyPurchased variable but the fact is it only returns the variable itself not its value so I fixed the problem by changing the method of my class to this:
public boolean appIsPurchased(){
return settings.getBoolean(keyPurchase,false);
}
you are setting the default value to true, so either your sharedpreference does not contains an entry for key_purchased or setAppInPurchasedMode is never called or is called wit status successful. On the minor side, your
public boolean appIsPurchased(){
boolean purchased = false;
if (settings.getBoolean(keyPurchase,true)){
purchased = true;
}
return purchased;
}
can be implemented like:
public boolean appIsPurchased(){
return settings.getBoolean(keyPurchase, false);
}
about setAppInPurchasedMode, if I were in you I would change the way you compare status, this way:
public void setAppInPurchasedMode(String status){
if ("successful".equals(status)){
settings.edit().putBoolean(keyPurchase, true).commit();
} else if ("failed".equals(status)){
settings.edit().putBoolean(keyPurchase, false).commit();
}
}
the difference is that if status is null, the way you implemented will crash your application with NPE. With my implementation you'll get false, because "successful" instanceof null is always false, and instanceof is the first check for equals
For those still having a problem, remember to apply the changes to your preferences.
private SharedPreferences sharedPreferences ;
private SharedPreferences.Editor sharedPreferencesEditor;
sharedPreferencesEditor.putBoolean("myVariable", false);
sharedPreferencesEditor.apply();
So I have constructed a class SaveAndLoad that looks like this:
public class SaveAndLoad {
public Preferences pref;
public final String path = "Highscore";
public SaveAndLoad() {
pref = Gdx.app.getPreferences("Gametitle");
}
public void saveInt(int value) {
pref.putInteger(path, value);
pref.flush();
}
public int getInt() {
return pref.getInteger(path);
}
}
When I try to save my highscore I call saveInt(highscore) and when you turn the game on again I call getInt.
constructor(){
SaveAndLoad sdRemote = new SaveAndLoad ();
try {
highscore = sdRemote.getInt();
} catch (NullPointerException e) {
highscore = 0;
}
}
The problem is that when I start the game again after a earlier game, the highscore don't load, it returns zero...
I've done the manifest permission:
Thanks
///Daniel
Ps. In the last answer I say it works on some devices, but I found out later that was not the case.
Make sure you retrieve data like this:
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
int silent = settings.getInt("score", 0);
And save it like this:
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putInt("score", playerScore);
// Commit the edits!
editor.commit(); //<----------
Edit:
Ah you are using GDX. In that case make sure you are initializing this class after GDX has initialized. Check if Gdx.app.getPreferences("Game"); is returning null.
Edit2:
Ok, so we have confirmed it's a problem with Gdx on certain devices (that sucks).
If you REALLY need to make this work on both Desktop and Android devices there is another way. If you just want your game to work on Android you just should't use Gdx at all for saving the data.
I suggest you use something called a GDX Android interface. This way you can handle saving data on Android devices yourself using native Android calls that I used in this answer. This isn't really easy though but there is a tutorial here:
https://code.google.com/p/libgdx-users/wiki/IntegratingAndroidNativeUiElements3TierProjectSetup
you can store and retrive data from this.
following code of class and methods.
public class SaveAndLoad {
public SharedPreferences pref;
public final String path = "Highscore";
public SaveAndLoad() {
pref = Gdx.app.getSharedPreferences("Gametitle",MODE_PRIVATE);
}
public void saveInt(int value) {
Editor edit = pref.edit();
edit.putInt(path, value);
edit.commit();
}
public int getInt() {
return pref.getInt(path,0);
}
}
for Save HighScore.
int highscore = 0;
SaveAndLoad sdRemote = new SaveAndLoad ();
try {
highscore = sdRemote.getInt();
} catch (NullPointerException e) {
e.printStackTrace();
}
for get HighScore.
int highscore = 255;
SaveAndLoad sdRemote = new SaveAndLoad ();
try {
sdRemote.saveInt(highscore);
} catch (NullPointerException e) {
e.printStackTrace();
}
this code working properly for me. and use this i'm sure its useful for you.
From the sounds of it you are doing everything correctly, which leaves the option of us testing the device in question. If you could run the following application listener on the device and see if MyValue is increasing each time you run the application on the device.
public class PrefTest implements ApplicationListener {
#Override public void create() {
final Preferences pref = Gdx.app.getPreferences("TestApp");
int value = pref.getInteger("MyValue", 0);
Gdx.app.log("MyValue", Integer.toString(value));
value++;
pref.putInteger("MyValue", value);
pref.flush();
}
#Override public void render() {}
#Override public void dispose() {}
#Override public void resize(final int width, final int height) {}
#Override public void pause() {}
#Override public void resume() {}
}
You should see value increase in the LogCat each time you launch the application. Example, install app and run, back out and launch from the icon, repeat the back out and launch. If this works, then we know the issue is something to do with your code. If this doesn't work, then we know the issue is something with LibGDX or the phone itself.
Is there any simple way of determining whether or not a certain activity is active?
I want to do certain things depending on which activity is active.
eg:
if(activityrunning == activity1)
//do this
else if (activityrunning == activity2)
//do something else
You can use a static variable within the activity.
class MyActivity extends Activity {
static boolean active = false;
#Override
public void onStart() {
super.onStart();
active = true;
}
#Override
public void onStop() {
super.onStop();
active = false;
}
}
The only gotcha is that if you use it in two activities that link to each other then onStop on the first is sometimes called after onStart in second. So both might be true briefly.
Depending on what you are trying to do (update the current activity from a service?). You could just register a static listener in the service in your activity onStart method then the correct listener will be available when your service wants to update the UI.
I realize this issue is quite old, but I think it's still worth sharing my solution as it might be useful to others.
This solution wasn't available before Android Architecture Components were released.
Activity is at least partially visible
getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)
Activity is in the foreground
getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)
I think more clear like that:
public boolean isRunning(Context ctx) {
ActivityManager activityManager = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> tasks = activityManager.getRunningTasks(Integer.MAX_VALUE);
for (RunningTaskInfo task : tasks) {
if (ctx.getPackageName().equalsIgnoreCase(task.baseActivity.getPackageName()))
return true;
}
return false;
}
An option without using any auxiliar variable is:
activity.getWindow().getDecorView().getRootView().isShown()
where activity is f.e.: this or getActivity().
The value returned by this expression changes in onStart() / onStop(), which are the events that start / stop showing the layout of the activity on the phone.
I used MyActivity.class and getCanonicalName method and I got answer.
protected Boolean isActivityRunning(Class activityClass)
{
ActivityManager activityManager = (ActivityManager) getBaseContext().getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(Integer.MAX_VALUE);
for (ActivityManager.RunningTaskInfo task : tasks) {
if (activityClass.getCanonicalName().equalsIgnoreCase(task.baseActivity.getClassName()))
return true;
}
return false;
}
Far better way than using a static variable and following OOP
Shared Preferences can be used to share variables with other activities and services from one application
public class example extends Activity {
#Override
protected void onStart() {
super.onStart();
// Store our shared preference
SharedPreferences sp = getSharedPreferences("OURINFO", MODE_PRIVATE);
Editor ed = sp.edit();
ed.putBoolean("active", true);
ed.commit();
}
#Override
protected void onStop() {
super.onStop();
// Store our shared preference
SharedPreferences sp = getSharedPreferences("OURINFO", MODE_PRIVATE);
Editor ed = sp.edit();
ed.putBoolean("active", false);
ed.commit();
}
}
Use shared preferences. It has the most reliable state information, less application switch/destroy issues, saves us to ask for yet another permission and it gives us more control to decide when our activity is actually the topmost. see details here abd here also
if(!activity.isFinishing() && !activity.isDestroyed())
From the official docs:
Activity#isFinishing()
Check to see whether this activity is in the process of finishing, either because you called finish() on it or someone else has requested that it finished. This is often used in onPause() to determine whether the activity is simply pausing or completely finishing.
Activity#isDestroyed()
Returns true if the final onDestroy() call has been made on the Activity, so this instance is now dead.
This is code for checking whether a particular service is running. I'm fairly sure it can work for an activity too as long as you change getRunningServices with getRunningAppProcesses() or getRunningTasks(). Have a look here http://developer.android.com/reference/android/app/ActivityManager.html#getRunningAppProcesses()
Change Constants.PACKAGE and Constants.BACKGROUND_SERVICE_CLASS accordingly
public static boolean isServiceRunning(Context context) {
Log.i(TAG, "Checking if service is running");
ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);
boolean isServiceFound = false;
for (int i = 0; i < services.size(); i++) {
if (Constants.PACKAGE.equals(services.get(i).service.getPackageName())){
if (Constants.BACKGROUND_SERVICE_CLASS.equals(services.get(i).service.getClassName())){
isServiceFound = true;
}
}
}
Log.i(TAG, "Service was" + (isServiceFound ? "" : " not") + " running");
return isServiceFound;
}
thanks kkudi! I was able to adapt your answer to work for an activity... here's what worked in my app..
public boolean isServiceRunning() {
ActivityManager activityManager = (ActivityManager)Monitor.this.getSystemService (Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> services = activityManager.getRunningTasks(Integer.MAX_VALUE);
isServiceFound = false;
for (int i = 0; i < services.size(); i++) {
if (services.get(i).topActivity.toString().equalsIgnoreCase("ComponentInfo{com.lyo.AutoMessage/com.lyo.AutoMessage.TextLogList}")) {
isServiceFound = true;
}
}
return isServiceFound;
}
this example will give you a true or false if the topActivity matches what the user is doing. So if the activity your checking for is not being displayed (i.e. is onPause) then you won't get a match. Also, to do this you need to add the permission to your manifest..
<uses-permission android:name="android.permission.GET_TASKS"/>
I hope this was helpful!
There is a much easier way than everything above and this approach does not require the use of android.permission.GET_TASKS in the manifest, or have the issue of race conditions or memory leaks pointed out in the accepted answer.
Make a STATIC variable in the main Activity. Static allows other activities to receive the data from another activity. onPause() set this variable false, onResume and onCreate() set this variable true.
private static boolean mainActivityIsOpen;
Assign getters and setters of this variable.
public static boolean mainActivityIsOpen() {
return mainActivityIsOpen;
}
public static void mainActivityIsOpen(boolean mainActivityIsOpen) {
DayView.mainActivityIsOpen = mainActivityIsOpen;
}
And then from another activity or Service
if (MainActivity.mainActivityIsOpen() == false)
{
//do something
}
else if(MainActivity.mainActivityIsOpen() == true)
{//or just else. . . ( or else if, does't matter)
//do something
}
I think the accepted answer is an awful way of handling this.
I don't know what the use case is, but please consider a protected method in the base class
#protected
void doSomething() {
}
and override it in the derived class.
When the event occurs, just call this method in the base class. The correct 'active' class will handle it then. The class itself can then check if it is not Paused().
Better yet, use an event bus like GreenRobot's, Square's, but that one is deprecated and suggests using RxJava
Have you tried..
if (getActivity() instanceof NameOfYourActivity){
//Do something
}
ActivityLifecycleCallbacks is a great way of keeping track of all the activities in App:
public class BaseActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
private ActivityState homeState, contentState;
#Override
public void onActivityCreated(Activity activity, Bundle bundle) {
if (activity instanceof HomeActivityv2) {
homeState = ActivityState.CREATED;
} else if (activity instanceof ContentDisplayActivity) {
contentState = ActivityState.CREATED;
}
}
#Override
public void onActivityStarted(Activity activity) {
if (activity instanceof HomeActivityv2) {
homeState = ActivityState.STARTED;
} else if (activity instanceof ContentDisplayActivity) {
contentState = ActivityState.STARTED;
}
}
#Override
public void onActivityResumed(Activity activity) {
if (activity instanceof HomeActivityv2) {
homeState = ActivityState.RESUMED;
} else if (activity instanceof ContentDisplayActivity) {
contentState = ActivityState.RESUMED;
}
}
#Override
public void onActivityPaused(Activity activity) {
if (activity instanceof HomeActivityv2) {
homeState = ActivityState.PAUSED;
} else if (activity instanceof ContentDisplayActivity) {
contentState = ActivityState.PAUSED;
}
}
#Override
public void onActivityStopped(Activity activity) {
if (activity instanceof HomeActivityv2) {
homeState = ActivityState.STOPPED;
} else if (activity instanceof ContentDisplayActivity) {
contentState = ActivityState.STOPPED;
}
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
#Override
public void onActivityDestroyed(Activity activity) {
if (activity instanceof HomeActivityv2) {
homeState = ActivityState.DESTROYED;
} else if (activity instanceof ContentDisplayActivity) {
contentState = ActivityState.DESTROYED;
}
}
public ActivityState getHomeState() {
return homeState;
}
public ActivityState getContentState() {
return contentState;
}
}
ActivityState:
public enum ActivityState {
CREATED, STARTED, RESUMED, PAUSED, STOPPED, DESTROYED;
}
Extend the Application class and provide its reference in Android Manifest file:
import android.app.Application;
public final class BaseApplication extends Application {
private BaseActivityLifecycleCallbacks baseALC;
#Override
public void onCreate() {
super.onCreate();
baseALC = new BaseActivityLifecycleCallbacks();
this.registerActivityLifecycleCallbacks(baseALC);
}
public BaseActivityLifecycleCallbacks getBaseALC() {
return baseALC;
}
}
Ckeck anywhere from Activity for status of other activity:
private void checkAndLaunchHomeScreen() {
Application application = getApplication();
if (application instanceof BaseApplication) {
BaseApplication baseApplication = (BaseApplication) application;
if (baseApplication.getBaseALC().getHomeState() == null || baseApplication.getBaseALC().getHomeState() == ActivityState.DESTROYED) {
//Do anything you want
}
}
}
https://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html
I used a check if (!a.isFinishing()) and it seems to do what i need. a is the activity instance. Is this incorrect? Why didn't anyone try this?
what about activity.isFinishing()
Not sure it is a "proper" way to "do things".
If there's no API way to resolve the (or a) question than you should think a little, maybe you're doing something wrong and read more docs instead etc.
(As I understood static variables is a commonly wrong way in android. Of cause it could work, but there definitely will be cases when it wont work[for example, in production, on million devices]).
Exactly in your case I suggest to think why do you need to know if another activity is alive?.. you can start another activity for result to get its functionality. Or you can derive the class to obtain its functionality and so on.
Best Regards.
If you are interested in the lifecycle state of the specific instance of the activity, siliconeagle's solution looks correct except that the new "active" variable should be an instance variable, rather than static.
Use an ordered broadcast. See http://android-developers.blogspot.nl/2011/01/processing-ordered-broadcasts.html
In your activity, register a receiver in onStart, unregister in onStop. Now when for example a service needs to handle something that the activity might be able to do better, send an ordered broadcast from the service (with a default handler in the service itself). You can now respond in the activity when it is running. The service can check the result data to see if the broadcast was handled, and if not take appropriate action.
In addition to the accepted answer, if you have multiple instances of the activity, you can use a counter instead to handle multiple instances :
class MyActivity extends Activity {
static int activeInstances = 0;
static boolean isActive() {
return (activeInstances > 0);
}
#Override
public void onStart() {
super.onStart();
activeInstances++;
}
#Override
public void onStop() {
super.onStop();
activeInstances--;
}
}
public static boolean isActivityActive(Activity activity) {
if (null != activity)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
return !activity.isFinishing() && !activity.isDestroyed();
else return !activity.isFinishing();
return false;
}
Found an easy workaround with the following code
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
// Activity is being brought to front and not being created again,
// Thus finishing this activity will bring the last viewed activity to foreground
finish();
}
}
Use the isActivity variable to check if activity is alive or not.
private boolean activityState = true;
#Override
protected void onDestroy() {
super.onDestroy();
activityState = false;
}
Then check
if(activityState){
//add your code
}
If you want to check if the activity is in the back stack just follow next steps.
1. Declared an ArrayList in your Application class [Application class is defined in your mainfest file in application tag]
private ArrayList<Class> runningActivities = new ArrayList<>();
And add the following public methods to access and modify this list.
public void addActivityToRunningActivityies (Class cls) {
if (!runningActivities.contains(cls)) runningActivities.add(cls);
}
public void removeActivityFromRunningActivities (Class cls) {
if (runningActivities.contains(cls)) runningActivities.remove(cls);
}
public boolean isActivityInBackStack (Class cls) {
return runningActivities.contains(cls);
}
In your BaseActivity, where all activities extend it, override onCreate and onDestroy methods so you can add and remove activities from back stack as the following.
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((MyApplicationClass)getApplication()).addActivityToRunningActivityies
(this.getClass());
}
#Override
protected void onDestroy() {
super.onDestroy();
((MyApplicationClass)getApplication()).removeActivityFromRunningActivities
(this.getClass());
}
Finally if you want to check whether the activity is in the back stack or not just call this function isActivityInBackStack.
Ex: I want to check if the HomeActivityis in the back stack or not:
if (((MyApplicationClass)
getApplication()).isActivityInBackStack(HomeActivity.class)) {
// Activity is in the back stack
} else {
// Activity is not in the back stack
}
I have used task.topActivity instead of task.baseActivity and it works fine for me.
protected Boolean isNotificationActivityRunning() {
ActivityManager activityManager = (ActivityManager) getBaseContext().getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(Integer.MAX_VALUE);
for (ActivityManager.RunningTaskInfo task : tasks) {
if (task.topActivity.getClassName().equals(NotificationsActivity.class.getCanonicalName()))
return true;
}
return false;
}
I know this question is old and has a lot of varying answers, with various bonuses and drawbacks. My take on it, is why not roll your own IPC implementation.
class IPC {
companion object {
private val appContext : Context by lazy { /*genericApplicationContext*/ }
fun initIPC(process: String){
var file : File? = null
file = File(appContext.cacheDir.absolutePath + "/$process")
var output : OutputStream? = null
try {
output = FileOutputStream(file!!)
output.write(0)
} finally {
output?.close()
}
}
fun destroyIPC(process: String){
var file : File? = null
file = File(appContext.cacheDir.absolutePath + "/$process")
file.delete()
}
fun checkForIPC(process: String) : Boolean {
var file : File? = null
file = File(appContext.cacheDir.absolutePath + "/$process")
if(file.exists()) return true
return false
}
}
}
This allows you to create the file before launching the activity, and then close out the "process/file" when you close the launched activity. This allows you to check in a background thread or current activity if your "process activity" is in the background to see if the file is still open signaling that the activity is alive. In my case I am calling an external API in succession but need to rate limit the calls, so use this to make sure only one activity is alive calling the APIs at a time.
This is what I came up with for helping keep track of different activities with their classes derived from a base 'helper' class.
protected static Dictionary<string, bool> _activityInstances = new Dictionary<string, bool>();
protected static Dictionary<string, bool> _activitiesVisible = new Dictionary<string, bool>();
protected override void OnStart()
{
base.OnStart();
_activityInstances[this.GetType().Name] = true;
}
protected override void OnDestroy()
{
_activityInstances[this.GetType().Name] = false;
base.OnDestroy();
}
protected override void OnResume()
{
base.OnResume();
_activitiesVisible[this.GetType().Name] = true;
}
protected override void OnPause()
{
_activitiesVisible[this.GetType().Name] = false;
base.OnPause();
}
public static bool activityIsInstanced(string type)
{
return _activityInstances.ContainsKey(type) ? _activityInstances[type] : false;
}
public static bool activityIsVisible(string type)
{
return _activitiesVisible.ContainsKey(type) ? _activityInstances[type] : false;
}
It is able to inherited but in order to run a test (i.e. before launching a new copy of a particular activity) you would need to call the static method with the name of the class, e.g.
if (!SettingsEdit.activityIsInstanced("SettingsEdit"))
{
Intent intent = new Intent(this, typeof(SettingsEdit));
Bundle bundle = new Bundle();
bundle.PutString(MY_SETTING, this.someSetting);
intent.PutExtras(bundle);
this.StartActivityForResult(intent, 0);
}
If it helps anyone, I thought I'd share it here.
This work if you don't have the same activity in foreground.
If you open from notification don't work i made some adjustments and came with this:
public static boolean ativo = false;
public static int counter = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
counter++;
}
#Override
protected void onStart() {
super.onStart();
ativo = true;
}
#Override
protected void onStop() {
super.onStop();
if (counter==1) ativo = false;
}
#Override
protected void onDestroy() {
counter--;
super.onDestroy();
}
That works for me with several activitys open at the same time.