I have an application:
public class TestApplication extends Application {
#Override
public void attachBaseContext(Context context){
if (SomeGlobalState.doStuff) {
doStuff();
}
}
}
I'd like to do some set up of this application in the attachBaseContext that can change dependent on a flag I can pass that isn't always passed, so I don't want to add it to the metadata of the manifest.
Is there something in the Android framework that I don't know about where I can pass "Intent-like" extras to an Application or am I going to have to do something hackish to let it know that I want its state to change?
Is there something in the Android framework that I don't know about where I can pass "Intent-like" extras to an Application
No, but you don't need it.
or am I going to have to do something hackish to let it know that I want its state to change?
If the state is persistent -- and your question suggests that it is -- just use a SharedPreferences for that state. The Application can retrieve those and use them. And, if the Application needs to proactively take steps on a state change, the Application can register a listener for SharedPreferences changes.
If the state is not persistent -- meaning that the app always starts in state A and may switch to states B-Z based on runtime work -- just call a method on the Application at the point of the state change. The Application can hold onto the state in a data member.
To do this without a file, you can use adb shell setprop <propname> <value> and then in your attachBaseContext you can do something like this
Process proc = Runtime.getRuntime().exec(new String[]{"/system/bin/getprop", MY_PROPERTY});
BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String propertyValue = reader.readLine();
Then you can verify that propertyValue is equal to whatever you were expecting. Great for testing.
Related
I have an android application
#HiltAndroidApp
class MyApp extends Application {
static MyApp app;
static MyApp getApp() {
return app;
}
#Override
public void onCreate() {
super.onCreate();
app = this;
}
}
and I am trying to use it inside a class
class AppStateUsingClass {
public void mymethod() {
MyApp app = MyApp.getApp();
//use app
}
}
Now I can access the app where I don't have the context but I am not sure if its correct way of doing.
My understanding is that the application life cycle is through out app start and stop, therefore its lives as a Singleton so it shall be fine but not sure.
Isn't there any simpler cleaner API to access app, I have app state in MyApp class which I would like to access where context is absent?
Any suggestions are highly appreciated?
What you are doing is a fairly common pattern, and shouldn't really cause problems.
The application class can be treated as a singleton that is alive as long as any part of your application is alive.
The docs specifically state that this class is used to hold application state.
However, depending your actual design, the kind of state information you want to hold and where you want to access it, you may want to create your own singleton, independent of the application class and use that.
Or, you may want to initialize your AppStateUsingClass with a state object passed in the constructor.
This is a design decision, and if you want more opinions on it, create a working code example and post it on https://codereview.stackexchange.com
We've recently started running into crashes in our Android app due to the app being open in multiple processes. Several different errors point towards that. For instance this error:
com.google.firebase.database.DatabaseException: Failed to gain
exclusive lock to Firebase Database's offline persistence. This
generally means you are using Firebase Database from multiple
processes in your app. Keep in mind that multi-process Android apps
execute the code in your Application class in all processes, so you
may need to avoid initializing FirebaseDatabase in your Application
class. If you are intentionally using Firebase Database from multiple
processes, you can only enable offline persistence (i.e. call
setPersistenceEnabled(true)) in one of them.
We are also seeing similar errors from SQLite and H2. This is a new issue and we have not explicitly allowed multiple processes to run. Nothing in our AndroidManifest.xml specifies a custom android:process attribute.
I suspect that some third party library is causing this. How do I identify the root cause of the multiple processes and how do I prevent it?
Another of our apps is connecting to this app via a ContentProvider. At first I thought that it having android:multiprocess="true" was the culprit but changing it to "false" did not help. I still suspect that the other app is somehow triggering the creation of a new process. This is how to the ContentProvider is defined:
<provider
android:name=".DegooContentProvider"
android:authorities="${applicationId}.DegooContentProvider"
android:exported="true"
android:protectionLevel="signature"
android:multiprocess="false">
</provider>
You can check in your applicaition class if there is foreign process. Here is an example:
public class MyApp extends Application {
#Override
public void onCreate() {
super.onCreate();
if (!isMainProcess()) {
// Do not call thread unsafe logic. Just return
return;
}
// Thread unsafe logic.
...
}
private boolean isMainProcess() {
int pid = android.os.Process.myPid();
ActivityManager manager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
String currentProcName = processInfo.processName;
if (processInfo.pid == pid) {
if (TextUtils.equals(currentProcName, BuildConfig.APPLICATION_ID)) {
return true;
}
}
}
return false;
}
}
Looks like you are calling the method setPersistenceEnabled() multiple times.
Ensure it is not. You can do this in a several ways.
The preferred method will be to place it only in the onCreate() method of the default Application class if you are extending one.
Another solution will be to place it in the static block of any class.
static {
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
}
Or you can even set a static boolean variable say, inFirebaseInstanceInitialized and call setPersistenceEnabled() only if it is not true already.
I am using the PostContextCreate part of the life cycle in an e4 RCP application to create the back-end "business logic" part of my application. I then inject it into the context using an IEclipseContext. I now have a requirement to persist some business logic configuration options between executions of my application. I have some questions:
It looks like properties (e.g. accessible from MContext) would be really useful here, a straightforward Map<String,String> sounds ideal for my simple requirements, but how can I get them in PostContextCreate?
Will my properties persist if my application is being run with clearPersistedState set to true? (I'm guessing not).
If I turn clearPersistedState off then will it try and persist the other stuff that I injected into the context?
Or am I going about this all wrong? Any suggestions would be welcome. I may just give up and read/write my own properties file.
I think the Map returned by MApplicationElement.getPersistedState() is intended to be used for persistent data. This will be cleared by -clearPersistedState.
The PostContextCreate method of the life cycle is run quite early in the startup and not everything is available at this point. So you might have to wait for the app startup complete event (UIEvents.UILifeCycle.APP_STARTUP_COMPLETE) before accessing the persisted state data.
You can always use the traditional Platform.getStateLocation(bundle) to get a location in the workspace .metadata to store arbitrary data. This is not touched by clearPersistedState.
Update:
To subscribe to the app startup complete:
#PostContextCreate
public void postContextCreate(IEventBroker eventBroker)
{
eventBroker.subscribe(UIEvents.UILifeCycle.APP_STARTUP_COMPLETE, new AppStartupCompleteEventHandler());
}
private static final class AppStartupCompleteEventHandler implements EventHandler
{
#Override
public void handleEvent(final Event event)
{
... your code here
}
}
I am combining a static code analysis with a dynamic one. I basically create a new activity and set it up as the starting activity. During the run various methods in existing activities should be called.
I can call e.g. onCreate from outside, however, the super call to Activity will fail (or calls to SharedPreferences or other interesting classes) since Android does some initialization stuff when using the intents in order to call an activity (e.g. setting the context). But I need to somehow call methods like onCreate or onPause from outside while giving the target activity a valid context.
In my newly created activity I have got a valid context. I tried to pass it via calling ContextWrapper.attachBaseContext, but there is still a NullPointerException somewhere in Android due a the missing context. Is there some way to hack this somehow into a working state? Using reflection or other hacks would be no problem, since it is for analysis purposes only.
Thank you very much for any tips. I'd be able to modify the analyzed apps in any way to get this working.
However: Using an Intent is no option, since I cannot control which Activity-methods are being called, when and how often. I know that android has not been made for calling these methods directly, but it is not a common use case either :);
I have created a hack, which seems to help (I can get a valid context in the hacked activity). Let's see how far I get using this.
public static void hack(Activity hack, Activity main) {
try {
Field mActivityInfo = getField(Activity.class, "mActivityInfo");
mActivityInfo.set(hack, getClass("android.content.pm.ActivityInfo").newInstance());
Field mFragments = getField(Activity.class, "mFragments");
Field mContainer = getField(Activity.class, "mContainer");
Field mApplication = getField(Activity.class, "mApplication");
Field mWindow = getField(Activity.class, "mWindow");
Class FragmentManagerImpl = getClass("android.app.FragmentManagerImpl");
FragmentManager manager = (FragmentManager) mFragments.get(hack);
mApplication.set(hack, main.getApplication());
mWindow.set(hack, main.getWindow());
Class<?> FragmentContainer = getClass("android.app.FragmentContainer");
Method attachActivity = getMethod(FragmentManagerImpl, "attachActivity", Activity.class, FragmentContainer, Fragment.class);
attachActivity.invoke(manager, hack, mContainer.get(hack), null);
Method attachBaseContext = getMethod(ContextWrapper.class, "attachBaseContext", Context.class);
attachBaseContext.invoke(hack, new HackContext(main));
System.out.println("Hack performed");
} catch (Exception e) {
e.printStackTrace();
System.err.println("Hack failed :(");
}
}
I have Android application and own Application derived class holding some internal data.
Among other there are some string fields. The problem is that if I put the application in foreground, work on other application, switch back to my app again, the app may be restarted because it got killed by system. Unfortunatelly the Application object seems not to be created again because the onCreate method of application object doesn't get called and all fields are set to null. My Activity gets recreated but all Application's object fields are null. When is the Application.onCreate method called? How to handle it?
there is no onCreate that you can register to.in later API's there's a way to register to the Activity lifecycle functions. and then you can do what ever you want.
basically, what you should do is use SharedPrefrences for storing information.
what I would do is:
class MyApp extends Application {
private static String someResource = null;
public static String getSomeResource(Context context) {
if(someResource == null) {
SharedPrefrences prefs = (SharedPrefrences)
context.getSystemService(Context.SHARED_PREFRENCES);
someResource = prefs.getString(SOME_RESOURCE, null);
}
return someResource;
}
Application onCreate() will called only for one time during its life-cycle, i.e.. only when application is started.
As suggested by thepoosh below answer is valid ,if your application is killed,still the data is saved in shared preference.