Application Context in Non Activity Class? - java

There is alot of info on this topic but none of it assist me.
I have several global variables/fields inside the app's application class:
public class App extends Application {
private static App instance
public string PUBNUB_SUB = BuildConfig.PUBNUB_SUB_KEY;
public string PUBNUB_PUB = BuildConfig.PUBNUB_PUB_KEY;
public void onCreate() {
instance = this;
}
public static App getInstance(){
return instance;
}
}
In Activities/Fragments I'm successfully accessing those variables like so:
class ActivityA extend Activity {
App baseApp;
Pubnub pubnub;
public void onCreate(Bundle savedInstanceState){
baseApp = new(App)getApplicationContext();
Pubnub(baseApp.PUBNUB_SUB, baseApp.PUBNUB_PUB)
}
Now How do I access getApplicationContext() in a non-Activity/non-Fragment Class?
public class Events {
App baseApp;
Context mContext;
app = new(LoQooApp)mContext.getApplication(); ???
public Events(Context context) {
app = new(LoQooApp)mContext.getApplication(); ???
}
The above doesnt work, where should
app = new(LoQooApp) getApplication(); go?

Looks like PUBNUB_SUB and PUBNUB_PUB are constants, so you could declare them as public static final and then access using a static reference: App.PUBNUB_SUB and App.PUBNUB_PUB

Related

Application onCreate called(not called) after Activity onCreate

In crash logs, I've found very strange application bug which happens on android 7.0-8.0 for some small amount of users, but quite frequently. I was not able to reproduce the issue, here the code which reflects the current application status.
I have a static reference to my application class.
public class MyApplication extends Application {
private static MyApplication sInstance;
public static MyApplication get() {
return sInstance;
}
#Override
public void onCreate() {
super.onCreate();
sInstance = this;
}
}
In the main activity I do initialization of a singleton:
public class MainActivity extends AppCompatActivity{
public void onCreate(final Bundle savedInstanceState) {
initSingletone();
super.onCreate(createBundleNoFragmentRestore(savedInstanceState))
}
public void initSingleTone(){
Singleton singleton = Singleton.getInstance();
}
}
The singleton:
public class Singleton{
public static Singleton instance;
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return ;
}
public Singleton(){
Context context = MyApplication.get();
final File baseDir = context.getCacheDir();
....
}
}
The NullPointerException occurs on the following line.
final File baseDir = context.getCacheDir();
Because for some reasons MyApplication.get() returns null.
Seems onCreate of the Application was not called in the moment of onCreate of MainActivity, really weird.
Have anyone faced with the same problem?
What could be a reason for such strange initialization process of the Android components?
You can use a safe method:
public static MyApplication get() {
if(sInstance == null){
sInstance = this;
}
return sInstance;
}

singleton shared preferences issue

This just an example of my application.
I have created a singleton shared preferences which used a custom Shared preference manager class to edit the shared preferences data values:
public class MySharedPrefManager {
private static MySharedPrefManager instance= null;
private static SharedPreferences SharedPref;
private static Editor SPEditor;
private MySharedPrefManager () {
}
public static MySharedPrefManager getInstance(){
if(instance==null)
instance= new MySharedPrefManager ();
return instance;
}
public void setSharedPreferences(Context context){
SharedPref= context.getSharedPreferences("MySharedPref", Context.MODE_PRIVATE);
SPEditor= SPEditor.edit();
}
public void setAdmin(boolean pAdmin) {
SharedPrefManager.editBoolean("isAdmin", pAdmin, SPEditor);
}
public boolean isAdmin() {
return SharedPref.getBoolean("isAdmin", false);
}
}
Shared preference manager:
public class SharedPrefManager {
public static void editString(String key, String value, Editor pEditor){
pEditor.putString(key, value);
pEditor.commit();
}
public static void editBoolean(String key, boolean value, Editor pEditor){
pEditor.putBoolean(key, value);
pEditor.commit();
}
I have lots of activities which goes like activities: A->B->C->D->E->F->G
Activity A, which is the start-up activity, i get the instance of MySharedPrefManager and set the SharedPreferences also:
public class ActivityA extends Activity{
private MySharedPrefManager myPref;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.initialization);
myPref= MySharedPrefManager.getInstance();
// I am setting the SharedPreference Context with getApplicationContext(),
// as it is singleton, and I am using it through out my application
myPref.setSharedPreferences(getApplicationContext());
// other stuff...
}
private void changeData(){
myPref.setAdmin(true);
}
private void check(){
if(myPref.isAdmin()){
// do- something
}
}
}
ActivityD :
public class ActivityD extends Activity{
private MySharedPrefManager myPref;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.initialization);
// Here i called only the instance not the setSharedPreferences.
myPref= MySharedPrefManager.getInstance();
// other stuff...
}
private void changeData(){
myPref.setAdmin(true);
}
private void check(){
if(myPref.isAdmin()){
// do- something
}
}
}
Now, the issue I am facing right now is that, some times I get Null pointer exception in myPref.isAdmin() when I call it both in Activity A and D. But most of the time it works.
Also do I need to set the SharedPreferences (call the setSharedPreferences() method) in each of the activities? I don't feel the need to set is as it is singleton class.
I repeat, I am setting the SharedPreference Context with getApplicationContext(),as it is singleton and I am using it through out my application.
Edited: There are 3-4 shared preferences that I am using with the same structure.. With the similar problem. Using this PreferenceManager.getDefaultSharedPreferences(context);, I will be able to use only ONE SharedPreference which is the default SharedPreference.
The null pointer exception is probably caused by your singleton not being initialized. I suppose this happens when you rotate your device?
I expect the following to be happening: In activity A you do getInstance() and setSharedPreferences(). In activity B you only do getInstance() and expect to get the same instance that was created in A. But if you rotate the device while in B, then B is recreated which does the getInstance(), but this time it has to create its instance itself. And this time it will not do the initialization with setSharedPreferences().
My advise is to make your prefs class simpler. Make sure you initialize on construction. I.e. do not separate out getInstance() and setSharedPreferences(). Do something like this:
public class MySharedPrefManager {
private SharedPreferences sharedPref;
private MySharedPrefManager(Context context) {
sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
}
public static MySharedPrefManager from(Context context) {
return new MySharedPrefManager(context);
}
public void setAdmin(boolean pAdmin) {
sharedPref.edit().putBoolean("isAdmin", pAdmin).apply();
}
public boolean isAdmin() {
return sharedPref.getBoolean("isAdmin", false);
}
}
And use it like this when you need to do multiple settings:
MySharedPrefManager prefs = MySharedPrefManager.from(this);
prefs.setAdmin(true);
prefs.setSomethingElse();
boolean isFoo = prefs.isFoo();
Or use the shorter version if you only need one setting:
MySharedPrefManager.from(this).setAdmin(true);
Before using the data members or contents of the MySharedPrefManager, check for the null-check.
In MySharedPrefManager class add a getter method like this:
public SharedPreferences getSharedPreference(){
return SharedPref;
}
In the activity where you are accessing shared preference contents, in onCreate or onResume add this before using the contents:
myPref= MySharedPrefManager.getInstance();
if(myPref.getSharedPreference()==null){
myPref.setSharedPreferences(getApplicationContext());
}
This way you will be able to avoid NPE.
No need to, there already is a singleton. You can do:
PreferenceManager.getDefaultSharedPreferences(context);

Dagger dependency injection

Actually i am new to dependency injection and dagger, i have been writing boiler plate code all these time and am trying to learn dagger
I have a global class to save preference values
#Module(injects = AppPrefes.class)
public class AppPrefes {
private SharedPreferences appSharedPrefs;
private Editor prefsEditor;
public AppPrefes(Context context, String Preferncename) {
this.appSharedPrefs = context.getSharedPreferences(Preferncename,
Activity.MODE_PRIVATE);
this.prefsEditor = appSharedPrefs.edit();
}
/****
*
* getdata() get the value from the preference
*
* */
#Provides
public String getData(String key) {
return appSharedPrefs.getString(key, "");
}
/****
*
* SaveData() save the value to the preference
*
* */
#Provides
public void SaveData(String Tag, String text) {
prefsEditor.putString(Tag, text);
prefsEditor.commit();
}
}
How could i possibly inject this class in My activity
In my activity oncreate i have put like this
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ObjectGraph objectGraph = ObjectGraph.create();
AppPrefes app = objectGraph.get(AppPrefes.class);
}
but how should i pass dependency i.e the context and the preference name to AppPrefes class,i am completely new to dependency injection please correct me if i am wrong
Since dagger documentation seems to be little tough for me am asking this question.
I believe if you are willing to take the risk of creating a static variable to store the instance of the application,
public enum ApplicationHolder
{
INSTANCE;
private MyApplication application;
public MyApplication getApplication()
{
return application;
}
public void setApplication(MyApplication application)
{
this.application = application;
}
}
public MyApplication extends Application
{
#Override
public void onCreate()
{
super.onCreate();
ApplicationHolder.INSTANCE.setApplication(this);
objectGraph.create(new RootModule());
}
}
Afterwards, you can create a Dagger module which can provide the Context of the Application instance.
#Module(complete = false, library = true)
public class ContextModule
{
#Provides
public Context providesContext()
{
return ApplicationHolder.INSTANCE.getApplication().getApplicationContext();
}
}
#Module(
includes =
{
ContextModule.class
},
injects =
{
AppPrefes.class
}
)
public class RootModule
{
}
Then you can inject this in your Activity with #Inject, however you need to provide the String you want to inject as a constructor parameter like this: https://stackoverflow.com/a/18105271/2413303
The structure of modules and stuff for me were like here: How to set up DAGGER dependency injection from scratch in Android project?

Android - get manifest meta-data out of activity

I want to do something like this
private static final String url;
private static final String pass;
private static final String user;
static {
Bundle metadata = ctx.getPackageManager().getApplicationInfo(ctx.getPackageName(), PackageManager.GET_META_DATA).metaData;
url = (String) metadata.get("JMSQueueURL");
user = (String) metadata.get("JMSQueueUsername");
pass = (String) metadata.get("JMSQueuePassword");
}
So far it was in activity (but not as static), so that I was possible to get package manager, but now i want to move this piece of code to another class which doesn't inherits ContextWrapper (from where we can get package manager). Is it possible somehow? This class is something like util class.
You can pass the Context from the calling method to the method in the Util class and use the context there to get the details you want. This way you can call the method in the Util class from different modules in your application, with different contexts.
// Calling the Util method
Bundle metadata = Util.getMetaData(context);
...
// Inside the Util class
public static Bundle getMetaData(Context context) {
return context.getPackageManager().getApplicationInfo(ctx.getPackageName(), PackageManager.GET_META_DATA).metaData;
}
This allows me to access the metadata from anywhere in my application, without a context:
public class MyAndroidApp extends Application {
private static MyAndroidApp instance;
#Override
public void onCreate() {
super.onCreate();
instance = this;
}
public static MyAndroidApp getInstance() {
return instance;
}
public static Bundle getMetadata() {
try {
return getInstance()
.getPackageManager()
.getApplicationInfo(getInstance().getPackageName(), PackageManager.GET_META_DATA)
.metaData;
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
}

How can I get a resource content from a static context?

I want to read strings from an xml file before I do much of anything else like setText on widgets, so how can I do that without an activity object to call getResources() on?
Create a subclass of Application, for instance public class App extends Application {
Set the android:name attribute of your <application> tag in the AndroidManifest.xml to point to your new class, e.g. android:name=".App"
In the onCreate() method of your app instance, save your context (e.g. this) to a static field named mContext and create a static method that returns this field, e.g. getContext():
This is how it should look:
public class App extends Application{
private static Context mContext;
#Override
public void onCreate() {
super.onCreate();
mContext = this;
}
public static Context getContext(){
return mContext;
}
}
Now you can use: App.getContext() whenever you want to get a context, and then getResources() (or App.getContext().getResources()).
For system resources only!
Use
Resources.getSystem().getString(android.R.string.cancel)
You can use them everywhere in your application, even in static constants declarations!
My Kotlin solution is to use a static Application context:
class App : Application() {
companion object {
lateinit var instance: App private set
}
override fun onCreate() {
super.onCreate()
instance = this
}
}
And the Strings class, that I use everywhere:
object Strings {
fun get(#StringRes stringRes: Int, vararg formatArgs: Any = emptyArray()): String {
return App.instance.getString(stringRes, *formatArgs)
}
}
So you can have a clean way of getting resource strings
Strings.get(R.string.some_string)
Strings.get(R.string.some_string_with_arguments, "Some argument")
Please don't delete this answer, let me keep one.
Shortcut
I use App.getRes() instead of App.getContext().getResources() (as #Cristian answered)
It is very simple to use anywhere in your code!
So here is a unique solution by which you can access resources from anywhere like Util class .
(1) Create or Edit your Application class.
import android.app.Application;
import android.content.res.Resources;
public class App extends Application {
private static App mInstance;
private static Resources res;
#Override
public void onCreate() {
super.onCreate();
mInstance = this;
res = getResources();
}
public static App getInstance() {
return mInstance;
}
public static Resources getRes() {
return res;
}
}
(2) Add name field to your manifest.xml <application tag. (or Skip this if already there)
<application
android:name=".App"
...
>
...
</application>
Now you are good to go.
Use App.getRes().getString(R.string.some_id) anywhere in code.
There is also another possibilty. I load OpenGl shaders from resources like this:
static private String vertexShaderCode;
static private String fragmentShaderCode;
static {
vertexShaderCode = readResourceAsString("/res/raw/vertex_shader.glsl");
fragmentShaderCode = readResourceAsString("/res/raw/fragment_shader.glsl");
}
private static String readResourceAsString(String path) {
Exception innerException;
Class<? extends FloorPlanRenderer> aClass = FloorPlanRenderer.class;
InputStream inputStream = aClass.getResourceAsStream(path);
byte[] bytes;
try {
bytes = new byte[inputStream.available()];
inputStream.read(bytes);
return new String(bytes);
} catch (IOException e) {
e.printStackTrace();
innerException = e;
}
throw new RuntimeException("Cannot load shader code from resources.", innerException);
}
As you can see, you can access any resource in path /res/...
Change aClass to your class. This also how I load resources in tests (androidTests)
The Singleton:
package com.domain.packagename;
import android.content.Context;
/**
* Created by Versa on 10.09.15.
*/
public class ApplicationContextSingleton {
private static PrefsContextSingleton mInstance;
private Context context;
public static ApplicationContextSingleton getInstance() {
if (mInstance == null) mInstance = getSync();
return mInstance;
}
private static synchronized ApplicationContextSingleton getSync() {
if (mInstance == null) mInstance = new PrefsContextSingleton();
return mInstance;
}
public void initialize(Context context) {
this.context = context;
}
public Context getApplicationContext() {
return context;
}
}
Initialize the Singleton in your Application subclass:
package com.domain.packagename;
import android.app.Application;
/**
* Created by Versa on 25.08.15.
*/
public class mApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
ApplicationContextSingleton.getInstance().initialize(this);
}
}
If I´m not wrong, this gives you a hook to applicationContext everywhere, call it with ApplicationContextSingleton.getInstance.getApplicationContext();
You shouldn´t need to clear this at any point, as when application closes, this goes with it anyway.
Remember to update AndroidManifest.xml to use this Application subclass:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.domain.packagename"
>
<application
android:allowBackup="true"
android:name=".mApplication" <!-- This is the important line -->
android:label="#string/app_name"
android:theme="#style/AppTheme"
android:icon="#drawable/app_icon"
>
Now you should be able to use ApplicationContextSingleton.getInstance().getApplicationContext().getResources() from anywhere, also the very few places where application subclasses can´t.
Please let me know if you see anything wrong here, thank you. :)
Another solution:
If you have a static subclass in a non-static outer class, you can access the resources from within the subclass via static variables in the outer class, which you initialise on creation of the outer class. Like
public class Outerclass {
static String resource1
public onCreate() {
resource1 = getString(R.string.text);
}
public static class Innerclass {
public StringGetter (int num) {
return resource1;
}
}
}
I used it for the getPageTitle(int position) Function of the static FragmentPagerAdapter within my FragmentActivity which is useful because of I8N.
I think, more way is possible.
But sometimes, I using this solution. (full global):
import android.content.Context;
import <your package>.R;
public class XmlVar {
private XmlVar() {
}
private static String _write_success;
public static String write_success() {
return _write_success;
}
public static void Init(Context c) {
_write_success = c.getResources().getString(R.string.write_success);
}
}
//After activity created:
cont = this.getApplicationContext();
XmlVar.Init(cont);
//And use everywhere
XmlVar.write_success();
I load shader for openGL ES from static function.
Remember you must use lower case for your file and directory name, or else the operation will be failed
public class MyGLRenderer implements GLSurfaceView.Renderer {
...
public static int loadShader() {
// Read file as input stream
InputStream inputStream = MyGLRenderer.class.getResourceAsStream("/res/raw/vertex_shader.txt");
// Convert input stream to string
Scanner s = new Scanner(inputStream).useDelimiter("\\A");
String shaderCode = s.hasNext() ? s.next() : "";
}
...
}
I am using API level 27 and found a best solution after struggling for around two days. If you want to read a xml file from a class which doesn't derive from Activity or Application then do the following.
Put the testdata.xml file inside the assets directory.
Write the following code to get the testdata document parsed.
InputStream inputStream = this.getClass().getResourceAsStream("/assets/testdata.xml");
// create a new DocumentBuilderFactory
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// use the factory to create a documentbuilder
DocumentBuilder builder = factory.newDocumentBuilder();
// create a new document from input stream
Document doc = builder.parse(inputStream);
Getting image resouse as InputStream without context:
Class<? extends MyClass> aClass = MyClass.class;
URL r = aClass.getResource("/res/raw/test.png");
URLConnection urlConnection = r.openConnection();
return new BufferedInputStream(urlConnection.getInputStream());
If you need derectory tree for your files, it will also works (assets supports sub-dirs):
URL r = aClass.getResource("/assets/images/base/2.png");
why you dont try
Resources.getSystem().getString(R.string.foo);
Here is an alternative, slightly different, approach that you may try.
You could subclass the Application class like what other solutions mentioned, and store a static reference to an instance of Resources.
Create an application class and initialise the Resources variable in the onCreate method. This will be called when your app starts. We can use WeakReference here to prevent memory leaks that might happen as a result of storing this instance as a static variable(although it is very unlikely to happen)
public class App extends Application {
private static WeakReference<Resources> res;
Since you mentioned that you only want to retrieve strings from the xml resource declaration, there is no need to expose this resource variable to other classes, for encapsulation of the resources instance and to prevent it from leaking out. Hence, you may store the reference as a private variable.
Remember to initialise this variable in onCreate:
#Override
public void onCreate() {
super.onCreate();
res = new WeakReference<>(getResources());
}
We also need to declare the application's android:name as .App(or any other name you set it to) in AndroidManifest.xml under the application tag.
<application android:name=".App"
........... other attributes here ...........
Another way of retrieving the string resource is not by using the Resources instance in other classes(or the Context instance), but to get the App class to get this for you in a static method. This keeps the instance encapsulated/private.
You can use a static method in your App class to retrieve these values(e.g. getStringGlobal, just do not call it getString as it will conflict with the default method)
public static String getStringGlobal(#StringRes int resId) {
if (res != null && res.get() != null) {
return res.get().getString(resId);
} else {
// This should not happen, you should throw an exception here, or you can return a fallback string to ensure the app still runs
}
}
As seen, you can also add error handling in case the instance of Resources is not available(this should not happen, but just in case).
You can then retrieve the string resource by calling
App.getStringGlobal(R.string./*your string resource name*/)
So your App.java:
public class App extends Application {
private static WeakReference<Resources> res;
#Override
public void onCreate() {
super.onCreate();
res = new WeakReference<>(getResources());
}
public static String getStringGlobal(#StringRes int resId) {
if (res != null && res.get() != null) {
return res.get().getString(resId);
} else {
// This should not happen(reference to Resources invalid), you should throw an exception here, or you can return a fallback string to ensure the app still runs
}
}
}
In your class, where you implement the static function, you can call a private\public method from this class. The private\public method can access the getResources.
for example:
public class Text {
public static void setColor(EditText et) {
et.resetColor(); // it works
// ERROR
et.setTextColor(getResources().getColor(R.color.Black)); // ERROR
}
// set the color to be black when reset
private void resetColor() {
setTextColor(getResources().getColor(R.color.Black));
}
}
and from other class\activity, you can call:
Text.setColor('some EditText you initialized');
if you have a context, i mean inside;
public void onReceive(Context context, Intent intent){
}
you can use this code to get resources:
context.getResources().getString(R.string.app_name);
public Static Resources mResources;
#Override
public void onCreate()
{
mResources = getResources();
}

Categories