Why Toast.makeText and not new Toast - android

This is may be a noob question, but I was wondering why do we have to use a static method (makeText) to create a Toast and not a constructor.
Why do we have to use this:
makeText(Context context, CharSequence text, int duration)
instead of this:
new Toast(Context context, CharSequence text, int duration)
This is the makeText method:
public static Toast makeText(Context context, CharSequence text, int duration) {
Toast result = new Toast(context);
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
tv.setText(text);
result.mNextView = v;
result.mDuration = duration;
return result;
}
Why don't we have the following:
public Toast (Context context, CharSequence text, int duration) {
this(context);
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
tv.setText(text);
this.mNextView = v;
this.mDuration = duration;
}
I searched the web and source code for any reason but I didn't find.
Please if you have an idea, don't hesitate.

The question basically drill downs to when should I make a method static. The answer is simple- when your method has a very specific task and does not change the state of the object.
Something like a utility method, say add(int a, int b) which simply returns a+b. If I need to store the value a+b for later use for the object, static method is strictly no-no (you will not be able to store a non static variable in a static method). But if we are dealing with some action which is independent of state of the object, static is the answer.
Why are we giving preference to static if it is independent of state of object?
Memory- static method will only have one copy, irrespective of the actual number of object.
Availability- Method is available even if you don't have single object
Ofcourse the downside is that we are keeping a copy of method even if we do not use it at all (if it was non-static and no object was created, we would have saved this space). But this is of lower weight than the advantages mentioned above.
As the method we are discussing here (makeText), does not need to maintain a state for later use, the best way to go is static method.
--Edit--
The answer mentioned above is more generic as to when we should use static and when non-static, let me get specific to Toast class.
Toast class gives us 2 ways to create a Toast object (Refer http://developer.android.com/reference/android/widget/Toast.html)
makeText(Context context, CharSequence text, int duration) which returns a Toast object which the values assigned.
Normal way, use new Toast(context) to create an object, then set values as required.
If you use method 1, you are saying something like Toast.makeText(context, text, duration).show(); and we are done. I use this method all the time.
Method 2 is used only for a specific case, from http://developer.android.com/guide/topics/ui/notifiers/toasts.html
Do not use the public constructor for a Toast unless you are going to
define the layout with setView(View). If you do not have a custom
layout to use, you must use makeText(Context, int, int) to create the
Toast.
#CFlex, If I got your question properly, I guess you just want to know why we have Toast.makeText(context, text, duration) returning a Toast object, when the same thing could have been done by a constructor.
Whenever I look at something like ClassName.getObject returning object of class, I think about singleton pattern. Well, in this case we are not exactly talking about singleton, I would like to assume that makeText returns same object always (to save creation of N objects), otherwise it is just a fancy thing developed by Android team.

One rule: Ask yourself "Does it make sense to call this method, even if no object has been constructed yet?" If so, it should definitely be static.
Remember that objects live in memory and they are created for certain jobs. Static methods are available for all the objects in a class and it is not necessary to create an object to use them.
So there is no reason to create an object Toast to be able to access the method makeText, when you can access it as a static method (more elegant and compact)

As far as I know:
That's because we don't wish to hold an instance of the object toast, which would require an amount of memory persistently used until cleaned by the GarbageCollector.
And that it always have access to being displayed, so it is not required by your application to have any set of permissions.

Related

How do I overcome this static vs. non-static method runaround?

I've used this quick-and-dirty msgbox (long live VB) routine extensively with Swing, for both debugging and user info messages:
public static void msgbox(String s){
javax.swing.JOptionPane.showMessageDialog(null, s);
}
I'm just beginning to learn about Android app development. I found Toast in my textbook as being a quick-but-awkward way to show info to the user. Here's the book's code:
String selected="...whatever...";
Toast toast=Toast.makeText(getApplicationContext(),selected,Toast.LENGTH_SHORT);
toast.show();
So I wrote this:
public void msgbox(String message)
{
android.widget.Toast.makeText(getApplicationContext(),
message,
android.widget.Toast.LENGTH_SHORT)
.show();
}
It worked the first time I used it when I only had one class, MainActivity. Then I tried to use it with a Fragment as follows:
public class FragmentA extends Fragment {
public View onCreateView(LayoutInflater
inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_a, container, false);
MainActivity.msgbox("Fragment A"); // **********************************
Button button = (Button) v.findViewById(R.id.button1);
//...
return v;
}
}
The error on the msgbox line is Non-static method msgbox cannot be referenced from a static context.
So I added static to the declaration for msgbox, which seemed like a good idea since that's how my Swing version of msgbox is declared:
public static void msgbox(String message)
{
android.widget.Toast.makeText(getApplicationContext(), // ********************
message,
android.widget.Toast.LENGTH_SHORT).show();
}
That makes the original error go away, but it's replaced by Non-static method getApplicationContext cannot be referenced from a static context.
To fix that error, I changed the declaration for msgbox to include a Context:
public static void msgbox(Context c, String message)
{
android.widget.Toast.makeText(c, message, android.widget.Toast.LENGTH_SHORT)
.show();
}
That works and makes perfect sense, but my quick-and-dirty string-parameter-only call to msgbox has now vanished. I now have to call msgbox like this from main ...
msgbox(getApplicationContext(), "onCreate; about to show fragment A");
... and like this from a separate class: ...
MainActivity.msgbox(getActivity(), "Fragment A");
I tried passing null to Context, which works with Swing JOptionDialog, but I get null pointer exception with makeText, whose first parameter (I thus found out) is documented as #NonNull.
Is there a method other than getApplicationContext and getActivity that I could use as the first parameter to makeText that would allow me to make msgbox static?
Or do I just have to suffer through supplying a Context parameter?
On the other hand, since it possible to do so with Swing, does anybody have a static one-String-parameter msgbox-type method to share? It doesn't have to use makeText.
(I struggled a long time to get as comfortable as I thought I was with Java. Android is every bit as daunting and is making me question what the heck I know ....)
I haven't tried this, but consider subclassing Application, and have it construct a static 'singleton object', giving it the application context. Then put your 'msgbox' method in the singleton.
Context is an extremely important concept in Android. I suggest you read up on it.
To answer the question, I would highly recommend you put that method to make the Toast in your Fragment. Use getActivity() for your context. If you want, you could make a static method in a Utils class that takes Context as a parameter. Then, access it from your Activity or your Fragment and just pass in this or getActivity(), respectively.

How to use getString() on static String before onCreate()?

I am trying to use getString() to get an String from resources to assign it to an String array before my activity is created:
private static final String[] MenuNames = {
Resources.getSystem().getString(R.string.LCMeterMenu),
Resources.getSystem().getString(R.string.FrecMenu),
Resources.getSystem().getString(R.string.LogicAnalyzerMenu),
"Prueba con achartengine",
Resources.getSystem().getString(R.string.BrazoMenu)
};
When I use Resources.getSystem().getString(R.string.LCMeterMenu), Eclipse doesn't complain but I get an error at runtime:
Caused by: android.content.res.Resources$NotFoundException: String Resource ID #0x7f0a000a
But if I put inside onCreate():
Log.i("StringR", "String: " + getString(R.string.LCMeterMenu));
I get the String but I can't assign it to the final String I defined before. If I use only getString() before onCreate() I get and static error message. How can I use resources before onCreate() for global variables?
You cannot initialize a static final field from resources; the field needs to be initialized at the time the class is initialized and that happens before the application resources have been bound at run time. (By the way, the reason you cannot use Resources.getSystem() is that the Resources object you obtain that way contains only system resources, not any application resources.)
If you need those strings available before the application resources are bound, the only practical thing to do is to put the strings into the code directly. However, the "Android way" would be to organize your code so initialization only needs to happen during (or after) onCreate(). Just initialize the string array in onCreate() and don't worry about making the fields static or final.
If you don't want the string array to be associated with a particular activity, then you can subclass Application and read the array from resources inside the application class's onCreate() method. (You also need to declare your custom application class in the manifest.) However, the docs recommend against such an approach. (Since the array is private, I suspect that it is closely tied to a single activity anyway, so the use of an Application subclass doesn't seem warranted.)
An alternative is to declare a singleton class for your array. The singleton accessor function then needs a Context so it can retrieve the resources if necessary:
public class StringArray {
private static String[] theArray;
public static String[] getArray(Context context) {
if (theArray == null) {
theArray = context.getResources().getStringArray(R.array.my_strings);
}
return theArray;
}
}
(This assumes the string data are defined in a <string-array> resource like #JaiSoni suggested in his answer.) Once again, the member field cannot be declared final.
No, you can't use Resources before onCreate(). You can get the instance of Resources in onCreate() by using getResources() where you can get all the Strings. Also the strings are already declared as static by defining them in the strings.xml.
Pseudo code for accessing the Resources,
Resources res = getResources();
String app_name = res.getString(R.string.app_name);
Another approach could be to initialize the static array with resource identifiers (which are already available as opposed to the resources themselves).
private static final int[] MenuNames = {
R.string.LCMeterMenu,
R.string.FrecMenu,
...
};
This way, you can defer the loading of resources to when they are actually available:
String s = getResources().getString(MenuNames[i]);
The following is a working approach to initialize static final variables in android from XML, such as strings.xml.
Subclass application and provide a "static context"
Register the application class in manifest
Use the static context to initialize your constants
1. MyApplication.java
public abstract class MyApplication extends Application {
private static Context context;
#Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
/**
* Returns a "static" application context. Don't try to create dialogs on
* this, it's not gonna work!
*
* #return
*/
public static Context getContext() {
return context;
}
}
2. AndroidManifest.xml
<application
android:name=".android.application.MyApplication"
<!-- ... -->
</application>
3. Your application code, e.g. Activity
private static final String[] MenuNames = {
getContext().getString(R.string.LCMeterMenu),
getContext().getString(R.string.FrecMenu),
getContext().getString(R.string.LogicAnalyzerMenu),
"Prueba con achartengine",
getContext().getString(R.string.BrazoMenu)
};
protected static Context getContext() {
return MyApplication.getContext();
}
For working examples refer to AbstractApplication and PreferencesServiceSharedPreferences.
Note that this approach also has its downsides:
Apart from being opposed to the "Android way" (as #Ted Hopp suggested in his answer),
it makes testing a bit difficult. That is why the call to MyApplication.getContext() is wrapped in another method. As it is a static method, overriding it in testing code is not simple. But you could use a framework such as Powermock for this purpose.
In addition it is a bit prone to NullPointerExceptions. As soon as the context is null (e.g. in your testing code) the application code crashes. One option to overcome this, is to do the initialization in a constructor, where you could react to getContext()returning null (see example).
Whatever you get by the getString(int resId) will already be a constant for your application. Why do you have to keep it in another final static variable. You can read it like that whenever you want, right?

How many LayoutInflater are instantiated in an application?

I am using the Factory interface on LayoutInflater to be able to set a custom typeface on all my TextView in my application. Unfortunately there are places in my application where it doesn't work. After investigation, it seems that in the adapter of my ListView for example, the LayoutInflater used to inflate the cells is different from the one that has been used fo the rest of the application (my activity has a complex structure with Fragments).
Is this behaviour normal ? How can I make sure that the same LayoutInflater is always used, whatever the way I retrieve it ?
Thanks
as You get reference to LayoutInflater from Context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) (ViewGroup.inflate and Activity.getLayoutInflater are just convenient wrappers) i presume it always returns reference to the same inflater service, until it is destroyed and recreated, and then this newly created is returned, and so on... I presume that manager object aquired from getSystemService method are sth like "normal" service's binder objects.
EDIT:
And saying above i was wrong ;)
i checked out the source code and in android.view.ContextThemeWrapper (which is activitie's super class):
#Override public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
mInflater = LayoutInflater.from(mBase).cloneInContext(this);
}
return mInflater;
}
return mBase.getSystemService(name);
}
and in android.app.ContextImpl which is probably mBase Context implementation:
public Object getSystemService(String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}
where :
private static final HashMap SYSTEM_SERVICE_MAP =
new HashMap();
and ServiceFetcher is an inner class for caching and retrieving "system service instances".
What is sure there is one LayoutInflater per activity/ContextWrapper. For more observations study sources, please ;)
I don't think the quantity of LayoutInflaters really matters (unless you are literally thousands). But, yes, using multiple Inflaters is indeed normal. I use quite a few when working with my adapter views (as in I don't use one static one. Each adapter still only uses one).
If there are places in your code that the adapter doesn't work, you are probably missing something. I would post the code where the Inflater doesn't work.

What is the correct way to implement a constructor in android, application context in particular?

What is the correct way to implement a constructor in android?
It seems that in an Activity or Service 'onCreate()' is where the magic happens.
The reason I ask is because I would like to be sure I'm doing the right thing declaring
attributes in the top of my classes (Context in particular) and then setting the attribute values inside onCreate.
// Activity launched via an Intent, with some 'extras'
public class SomeActivity extends Activity {
private Context context;
private String foo;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set the object attribute for later use, good or Bad to do this?
context = getApplicationContext();
Intent fooIntent = getIntent();
foo = fooIntent.getStringExtra("foo");
}
private void someMethodThatNeedsContext() {
// For example:
Cursor c = this.context.getContentResolver().query(foo, xxx, xxx);
// Or is it better practice to:
// A) Pass the context as a local variable to this method
// B) Use getApplicationContext() locally when needed
}
}
Maybe either of these options is ok, and I'm over thinking it?
Any specific reading and/or suggestions you may have would greatly be helpful to me.
Yes, you are correct that initialization is supposed to take place in onCreate(). You don't really need neither to store a reference to a context, nor to call getApplicationContext(). Your activity is a context itself, so you just use wherever you need a context. For example, making a toast within an activity:
Toast.makeToast(this, "Some text", Toast.LENGTH_LONG).show();
Option B - Since you can call getApplicationContext() from any non-static methods in your Activity class.
In fact, Activity is derived from Context too (Somewhere in the inheritance tree..) so you can just do:
Cursor c = getContentResolver()....
You don't have to keep a reference to a context. Especially not static, that can cause problems.
And you are correct - since you usually don't create your own constructor for Activities, you put the code for construction in onCreate.
You are writing a method inside your activity, so you can call getApplicationContext() anywhere in your code, you don't need to use a local variable :
Cursor c = getApplicationContext().getContentResolver().query(foo, xxx, xxx);
Also remember that the activity itself is a context (the Activity class is derived from Context), so generally you can use this whenever you need to provide a context ( for example when creating an Intent : new Intent(this, ...)).

R.string.value Help android notification

whats the deal with
CharSequence contentTitle = R.string.value;
Error cannot convert from int to CharSequence. Is there a way around this or am i missing something?
i tried
String s = R.string.value + "";
CharSequence contentTitle = s;
it returns integers values.
Any help?
R.string.value is a call to the static field in the class R, which is auto generated by Eclipse and which does a kind of summary of all your resources. To retrieve the string, you need to use :
CharSequence contentTitle = getString(R.string.value);
If you open the R class you will see that it contains only numbers that are references to the compiled resources of your project.
To retrieve the string, you need to use getString(),
but getString() is a method from Context class.
If you want to use this method outside your Activity class, you should get link to your context first and then call:
String s = mContext.getString(R.string.somestring)
R.string.value returns the reference ID number of the resource 'value'. If you look at your R class it will appear as something like this:
public static final class string {
public static final int value=0x7f040007;
}
I've been experiencing issues with referencing the getString() method. The exact error that Eclipse spits at me is:
The method getString(int) is undefined for the type DatabaseHelper.MainDatabaseHelper
After reading for awhile I've figured out that you must reference your application's context to get access to the getString() method. I was trying to create a private SQLDatabase helper class in a content provider, however, that was not allowing me to reference the getString() method. My solution so far is to do something like this:
private class MainDatabaseHelper extends SQLiteOpenHelper {
MainDatabaseHelper(Context context) {
super(context, context.getString(R.string.createRoutesTable), null, 1);
}
public void onCreate(SQLiteDatabase db) {
db.execSQL((getContext()).getString(R.string.createRoutesTable));
}
}
Notice these two context references:
context.getString()
(getContext()).getString()
I don't know if this is the optimal long-term solution but it seems to work for the moment. Hope this helps.
You could use String s = getResources().getString(R.string.value); also.

Categories

Resources