Is it good idea use static class AppContext? - java

I found in one of github project class:
public class AppContext {
private static Context sContext;
private static Application sApplication;
public static Application getApplication() {
if (sApplication == null) {
throw new IllegalStateException("AppContext.setApplication was not called in Application.onCreate() method. " +
"Please inherit your application from the com.blandware.android.atleap.BaseApplication class.");
}
return sApplication;
}
public static void setApplication(Application application) {
sApplication = application;
}
public static Context getContext() {
if (sContext == null) {
throw new IllegalStateException("AppContext.setContext was not called in Application.onCreate() method. " +
"Please inherit your application from the com.blandware.android.atleap.BaseApplication class.");
}
return sContext;
}
public static void setContext(Context context) {
sContext = context;
}
}
It seams create, don't need more pass context to static function etc. But I'm worried about memory leaks. Can AppContext make it? When i shoud use Aplication context when activity context or view?

The Application object can not leak. There is always exactly one Application object for every app. It looks like the author is just using this class to make it easy to access in places where another Context is not available to be used to call getApplicationContext() to get the Application object.
Context, on the other hand, could be an Activity or a Service, and those really should not be stored beyond their lifetime. You will have to look at exactly which Context objects are being stored here to find out if there is a leak.

Related

Static context in App class - memory leak

To be able to get app context anywhere in my app, I created App class like this:
public class App extends Application
{
private static Context mContext;
public static Context getContext()
{
return mContext;
}
#Override
public void onCreate()
{
super.onCreate();
mContext = this
}
}
It works and also it's used in many places in my app where I need to use context (for example, to load resources) and I am not able to inject any other context to use.
However, Android Studio throws warning this approach (static context fields) causes memory leak.
Do you have any idea how to avoid static context field, but get similar functionality?
Never place static Context in your application since it will cause unexcepted memory leaks, however if you still want to use static Context in your application you can wrap the context in a WeakReference so change
private static Context mContext;
to
private static WeakReference<Context> mContext;
and on create change it to
mContext = new WeakReference<>(Context);
and finally get the Context using
public static Context getContext() {
return mContext.get();
}
if you want to research more about WeakRef use the link below,
https://developer.android.com/reference/java/lang/ref/WeakReference
Its not necessary use static for access context ,you can use get context ,get application context or get activity any where.
as far as possible you should avoid from passing context.
like this in fragments :DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(getContext(), layoutManager.getOrientation());
and in this (if the OP wanted to use Context in where the class does not host a Context method) case you can pass context without define it as a static.
for example :
public class DashboardWalletSpinnerAdapter extends ArrayAdapter<Wallet> {
private LayoutInflater mLayoutInflater;
private static final int CLOSE = 0;
private static final int OPEN = 1;
public DashboardWalletSpinnerAdapter(Context mContext, List<Wallet> walletList) {
super(mContext, R.layout.spinneritemclose_dashbaord, walletList);
mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

Install4j: how to check a RemoteCallable running unelevated

My installer is storing some information in a singleton class during the installation process. Now, I have noticed that in elevated action, the singleton class does not have the same instance. So far, I have not found any workaround/solution so that they share the same instance. So, I have decided to make sure that if anyone wants to get an instance of the singleton, they must call from an unelevated environment. Let's say the singleton looks like the following:
public class InvestigatorReport {
private final List<Report> reports = new ArrayList<>();
private final static InvestigatorReport INSTANCE = new InvestigatorReport();
private InvestigatorReport() {
MyLogger.logInfo(getClass(), "initiating...");
}
public static InvestigatorReport getInstance(Context context) {
if (context.hasBeenElevated()) {
throw new IllegalAccessError(
"this method must be called unelevated!");
}
return INSTANCE;
}
private boolean addReport(Report report) {
return reports.add(report);
}
}
But the problem is, There are some cases when I have to call this add report from an action class that is elevated. So I have tried the following in my elevated action class:
if (context.hasBeenElevated()) {
return (Boolean) context.runUnelevated(new RemoteCallable() {
#Override
public Serializable execute() {
return getInstance(context).addReport(report);
}
});
}
But, as you can see if I am passing the same context object from the elevated action class to the RemoteCallable class so, even though I am running the class unelevated, the context.hasBeenElevated() still returns true.
Is there any other way that I can check the elevation level other than the context? If you have any other better idea on preventing anyone from calling the singleton getInstance() method, I am all ears.
I would use a different pattern. Make all methods of your singleton static and wrap the data access with runUnelevated calls:
public static boolean addReport(Report report, Context context) {
context.runUnelevated(new RemoteCallable() {
#Override
public Serializable execute() {
InvestigatorReport.reports.add(report);
return null;
}
});
}
In that way, you can call the methods from both elevated and unelevated code without having to check anything at the call site.

Android application creation of internal file that isn't in an activity

I am trying to create an internal file in an android application. I have generated the code that works fine with java, but in order to create the internal file I believe I must have the context to do so.
Example: File file = new File(Context.getFilesDir(), "somefile.txt");
The problem I am running into is that the file creation and checks if it is made are maintained in a singleton class that I have created. When using the following
Example: File file = new File("somefile.txt");
everything seems to compile and work, but after closing the application it seems the file wasn't created. This leads me to believe that I need the application directory using the 1st example given. The problem is how do I get the applications context within a single class?
The problem is how do I get the applications context within a single class?
From Android Docs:
There is normally no need to subclass Application. In most situation, static singletons can provide the same functionality in a more modular way. If your singleton needs a global context (for example to register broadcast receivers), the function to retrieve it can be given a Context which internally uses Context.getApplicationContext() when first constructing the singleton.
Create your singleton like this:
// ...
private Context mAppContext = null;
private static MySingleton mSingleton = null;
// ...
private MySingleton(Context context) {
mAppContext = context;
// ... other initialization
}
public static MySingleton get(Context context) {
if (mSingleton == null) {
/*
* Get the global application context since this is an
* application-wide singleton
*/
mSingleton = new MySingleton(
context.getApplicationContext());
}
return mSingleton;
}
The each time you obtain your singleton from any activity, you have access to the global application context.
You can use it for your creation of files within your singleton like:
public void createFile(String filename) {
File file = new File(mAppContext.getFilesDir(), filename);
}
Or you can use the other ways mentioned here
Or you could extend Application class thats already a Singleton. It can be rather usefull :)
package com.example.myapp;
import android.app.Application;
import android.content.Context;
public class MyApp extends Application {
private static Context context;
private static MyApp my_instance;
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
my_instance = this;
context = this;
}
public static synchronized MyApp getInstance() {
return my_instance;
}
public static synchronized Context getContext() {
return context;
}
}
The mehod:
new File("filename")
does not create a file on disk.
You need to open the file and write to it for the file to be created, or use
File.createNewFile

How to play android sounds NOT in activity class

How can I play sound from a class that DOES NOT extend activity? I've been searching for a while and in every tutorial or answers from stackoverflow I've seen that sounds are always implemented in an activity class.
But in this case I have a class thas has the logic of my game, in which I have a gameUpdate() function; and in that function I want to make a specific sound play if something happens (for example a collision). How can I possibly access the activity that is currently running, from this class? Is there any way to do that?
Thanks.
If you need to get the current Activity instance or context you need to pass it to your other classes so that you can use it. For example:
class ABC extends Activity
{
public void onCreate(Bundle b)
{
XYZ xyz=new XYZ(this); // Sending the current Activity instance
}
}
class XYZ
{
ABC activity;
public XYZ(ABC activity)
{
this.activity = activity; //Now you can use it in this class
}
}
getActivity() or if is inside a fragment getFragment().getActivity()
Or alternativelly you can make add a Context to your class and get the activity reference from the constructor of the class.
Ex:
public class MyClass {
Context mContext();
public MyClass(Context context){
this.context = context;
}
}
and in your Activity class when you call MyClass:
MyClass myClass = new MyClass(this);
Inside your custom lass you can reference activity methods using its context.
So you actually just need a Context, not specifically an Activity (which is a Context). I would recommend that the class that should play sounds has a constructor which requires a Context. Keep a reference, not directly to the Context that you receive, but to the Application context using getApplicationContext() to get a Context that is safe to retain without the risk of memory leaks.
public class MySoundPlayingClass {
private final Context mContext;
public MySoundPlayingClass(Context ctx) {
// Since ctx could be an Activity, and this class
// could exist outside of the lifecycle of the Activity,
// grab the Application context to get a safe reference.
mContext = ctx.getApplicationContext();
}
}
Have a Util class and do something similar to below one. You can pass the context (it can be Activity instance) and the resource id to play it
Usage:
Util.play(context, R.raw.soundFile);
Sample Util class:
public class Util {
public static void play(final Context context, int resource) {
MediaPlayer mp = MediaPlayer.create(context, resource);
if (null != mp) {
mp.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp.release();
}
});
mp.start();
}
}
}

Getting a Context for use in AndroidTestCase when class under test is not an activity

I am trying to test using AndroidTestCase. I am trying to test only one particular class in my application, however this class does not extend Activity, Service or anything else. It is basically a plain Java class apart from the fact that it requires a Context. It is a pojo and some of its variables are objects that require android api calls in their creation, e.g. a call to the SensorManager.
I tried to use:
Context context = getContext();
When running my tests this gives me the exception "System services not available to activites before onCreate()". Does that method have to be overridden?
final Context context = new IsolatedContext(null, getContext()) gives the same thing.
The reason I am using the Android testing framework and not something like Robolectric is because the class I'm testing gathers hardware information about a device and so I want to run the tests on an actual device. I have looked at the developer docs for AndroidTestCase but can't see what I'm looking for in the examples. I'm not sure the other test case classes will achieve what I want. Any ideas?
My test class:
public class DeviceTest extends AndroidTestCase {
ClassToTest mClassToTest;
#Override
protected void setUp() throws Exception {
final Context context = new IsolatedContext(null, getContext()) {
#Override
public Object getSystemService(final String pName) {
return getContext().getSystemService(pName);
}
};
mClassToTest = new ClassToTest(context);
super.setUp();
}
public void testClassMethod() {
Object mObject;
mObject = mClassToTest.getObject();
assertNotNull(mObject);
}
#Override
protected void tearDown() throws Exception {
mClassToTest = null;
super.tearDown();
}
}
Thanks in advance.
UPDATE: After changing my setup to the following:
#Override
protected void setUp() throws Exception {
super.setUp();
context = this.getContext();
mClassToTest = new ClassToTest(context);
}
I am getting an error that context is null. In what scenarios would AndroidTestCase.getContext() return null? My setup seems to be ok....
From AndroidTestCase you can access directly mContext, or call getContext().
From the context returned by those, you could also call Context.getApplicationContext() if you wanted that one.
You can use mContext from super class (AndroidTestCase). I used it for the testing of the database where context is required.
AndroidTestCase.class
public class AndroidTestCase extends TestCase {
protected Context mContext;
...
}
You would be able to use Context in the inherited class of AndroidTestCase.
TestDb.java
public class TestDb extends AndroidTestCase {
void deleteTheDatabase() {mContext.deleteDatabase(DB_NAME)};
}
There are a few ways around this, you could use a mockcontext as one solution or if you really do not care what the context is just that is valid you can use an InstrumentationTestCase and get the context of the test apk via getInstrumentation().getContext().
I think the reason your context is null is that actually no android context exists at this point, you can get one by creating an application or an activity.

Categories