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

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.

Related

How do I succeed in declaring variables globally, issue with context

I'm following tutorials on how to put functions that are used frequently in activities all in one place.
For example, a toast message that comes up throughout my project, instead of having the function in each and every activity, just having it called in one place, GlobalFunctions.java.
So, I get it with simple functions, for example, in GlobalFunctions.java :
public class GlobalFunctions {
public void simpleMessage() {
System.out.println("simpleMessage text goes here");
}
}
And the I call it like this from Activity1:
GlobalFunctions simplemessage = new GlobalFunctions();
simplemessage.simpleMessage();
But what about? :
public class GlobalFunctions {
public void simpleMessage() {
Toast.makeText(getApplicationContext(), "simpleMessage text goes here", Toast.LENGTH_LONG).show();
}
}
I've looked at several posts including getApplicationContext() error Android and no matter what I put in the Context part of Toast I get a Cannot resolve method message. Also if there's any good tutorials for Dummies on this subject I'd be grateful.
The key is static .
Static values allow you to use static methods variables ..etc in whole project.
You can use following concept:
public static class GlobalFunctions {
public static void simpleMessage(Context context, String message) {
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
}
And you have to invoke it like:
GlobalFunctions.simpleMessage(/*YourActivity.this*/ /*or*/ /*getActivity()*/, "toast");
One solution would be to pass the Context as a parameter from the Activity or Fragment.
And instead of instantiating GlobalFunctions, writing and using static methods can be a better approach.
Create a Java Utils class:
public class Utils {
public static void showToast(Context context, String text) {
Toast.makeText(context, text, Toast.LENGTH_LONG).show();
}
}
// for example on the Activity code
Utils.showToast(this, "This is the toast text");
Keeping context in field beyond activity can be reason of memory leak, but there is some workaround.
You can create Singleton with application or application context and initialize it in onCreate in your custom application class. But you have to remember that you can't use this context to build views - it is not stylized.
Other way is just pass context as argument.
Sorry for missing code, response from phone :)
try this Create class like this to pass Context and Toast message as parameter like this
public class GlobalFunctions {
public static void simpleMessage(Context context,String message) {
Toast.makeText(Context, message, Toast.LENGTH_LONG).show();
}
}
call this function like this
GlobalFunctions.simpleMessage(YourActivity.this,"your Mesaage");

What is the Android best practice if Activity or Fragment does not contain required intent or argument?

If my Activity/Fragment requires that the coder provide a particular intent or argument, how should I handle the case where the coder did not provide this intent or argument?
Say I have the following Activity and Fragment:
public class MyActivity extends AppCompatActivity {
public static final String KEY = "KEY";
#Override
protected void onCreate(Bundle savedInstanceState) {
if (!getIntent().hasExtra(KEY)) {
throw new RuntimeException("KEY is required");
}
super.onCreate(savedInstanceState);
}
}
public class MyFragment extends Fragment {
public static final String KEY = "KEY";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (!getArguments().containsKey(KEY)) {
throw new RuntimeException("KEY is required");
}
//...
}
}
My gut tells me just to throw an Exception and force the app to shut down so the coder can rectify this problem in their code. If so then which Exception best describe this situation?
Furthermore, where is the best place to place such a code? For example, onCreat() for Activity? Or onAttach() or onCreateView() for Fragment?
You can always throw Exception, just make sure the describe the error in details to help other devs understand what they did wrong.
Also, Remember that it's always better to "fail early" than to postpone the Exception -
If there is a critical data you need, validate the data as soon as you can and don't "hide" the Exception under some button click of specific user flow - you want it to be clear as soon as someone opens the Activity to minimize the risk the dev won't notice his/hers error.
P.S
There is a nice pattern to minimize that kind of coding errors.
You can create a public static method to instantiate your Fragments and Activities.
Example:
public static Intent newIntent(Context context, String requiredStr, int requiredInt) {
Intent intent = new Intent(context, MyActivity.class);
intent.putExtra("extra_str", requiredStr);
intent.putExtra("extra_int", requiredInt);
return intent;
}
That way the dev doesn't have to remember all the required data.
And then you can start your Activity like so:
startActivity(MyActivity.newIntent(context, "string", 20));
It is totally reasonable to crash the app when it enters an invalid state due to programmer error. Fail fast and fail early. Of course you should include an error message that explains how the programmer can correct the error.
If a required value isn't present in an Intent or a Bundle, I would say the most natural exception to throw would be NullPointerException. You could also throw IllegalStateException.
This exception will occur only while developing application. If users get this exception then development is useless.
And while developing, to show developers some error in a organized way is nothing but to make it meaningful and understanding easily.
Though , here a simple toast or error log or alertdialog could be enough because end level users would never see this type of data passing exceptions!
To make app crash you can use UnsupportedOperationException because, here activity is changing (operation) without data (not supported).
Also you can use NullPointerException because some variable(which would be used in next step) is not getting value (null).

Mock objects in Android not passed as parameters

I am trying to test a Fragment I've created in Android. I have complete control of the code, so I can change it as I see fit. The issue is that I'm not sure what design pattern I'm missing to make it reasonable.
I am looking for a way to mock objects in Android that are not passed as parameters. This question suggests that anything you might want to mock should be written to be passed as a parameter.
This makes sense for some situations, but I can't figure out how to get it working on Android, where some of this isn't possible. With a Fragment, for example, you're forced to let much of the heavy lifting be done in callback methods. How can I get my mocked objects into the Fragment?
For example, in this ListFragment I need to retrieve an array of things to display to the user. The things I'm displaying need to be retrieved dynamically and added to a custom adapter. It currently looks as follows:
public class MyFragment extends ListFragment {
private List<ListItem> mList;
void setListValues(List<ListItem> values) {
this.mList = values;
}
List<ListItem> getListValues() {
return this.mList;
}
#Override
public void onCreateView(LayoutInflater i, ViewGroup vg, Bundle b) {
// blah blah blah
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
this.setListValues(ListFactory.getListOfDynamicValues());
CustomAdapter adapter = new CustomAdapter(
getActivity(),
R.layout.row_layout,
this.getListValues());
this.setListAdapter(adapter);
}
}
I'm trying to do this using Mockito and Robolectric.
This is the beginning of my robolectric test case:
public class MyFragmentTest {
private MyFragment fragment;
#Before
public void setup() {
ListItem item1 = mock(ListItem.class);
ListItem item2 = mock(ListItem.class);
when(item1.getValue()).thenReturn("known value 1");
when(item2.getValue()).thenReturn("known value 2");
List<ListItem> mockList = new ArrayList<ListItem>();
mockList.add(item1);
mockList.add(item2);
MyFragment real = new MyFragment();
this.fragment = spy(real);
when(this.fragment.getValueList()).thenReturn(mockList);
startFragment();
}
}
This feels so very wrong. This section from the mockito api points out that you shouldn't have to do partial mocks like this very frequently unless you're dealing with legacy code.
Further, I'm not actually able to mock out the CustomAdapter class using this approach.
What is the right way to do this sort of thing? Am I structuring things incorrectly in my Fragment classes? I suppose I might be able to add a bunch of package-private setters, but this still doesn't feel right.
Can someone shed some light on this? I'm happy to do rewrites, I just want to know some good patterns for dealing with the state in my Fragments and how I can make them testable.
I ended up creating my own solution to this. My approach was to add another level of indirection to each my calls that create or set an object.
First, let me point out that I couldn't actually get Mockito to work reliably with Fragment or Activity objects. It was somewhat hit or miss, but especially with trying to create Mockito Spy objects, some lifecycle methods appeared to not be called. I think this is related to gotcha number 2 shown here. Perhaps this is due to the ways that Android uses reflection to recreate and instantiate activities and fragments? Note that I was NOT incorrectly holding onto the reference, as it warns of, but interacting only with the Spy, as indicated.
So, I wasn't able to mock Android objects that required lifecycle methods be invoked by the framework.
My solution was to create to more types of methods in my Activity and Fragment methods. These methods are:
getters (getX()) that return the field named X.
retrievers (retrieveX()) that do some sort of work to get an object.
creators (createMyFragment()) that create objects by calling new. Similar to the retrievers.
Getters have whatever visibility you need. Mine are usually public or private.
Retrievers and creators are package private or protected, allowing you to override them in your test packages but not making them generally available. The idea behind these methods is that you can subclass your regular objects with stub objects and inject in known values during testing. You could also just mock out those methods if Mockito mocks/spies are working for you.
Taken in toto, the test would look something like the following.
Here is the fragment from my original question, modified to use the above approach. This is in the normal project:
package org.myexample.fragments
// imports
public class MyFragment extends ListFragment {
private List<ListItem> mList;
void setListValues(List<ListItem> values) {
this.mList = values;
}
List<ListItem> getListValues() {
return this.mList;
}
#Override
public void onCreateView(LayoutInflater i, ViewGroup vg, Bundle b) {
// blah blah blah
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
this.setListValues(this.retrieveListItems());
CustomAdapter adapter = this.createCustomAdapter();
this.setListAdapter(adapter);
}
List<ListItem> retrieveListItems() {
List<Item> result = ListFactory.getListOfDynamicValues();
return result;
}
CustomAdapter createCustomAdapter() {
CustomAdapter result = new CustomAdapter(
this.getActivity();
R.layout.row_layout,
this.getListValues());
return result;
}
}
When I test this object, I want to be able to control what gets passed around. My first thought was to use a Spy, replacing the return values of retrieveListItems() and createCustomAdapter() with my known values. However, like I said above, I wasn't able to get Mockito spies to behave when working with fragments. (Especially ListFragments--I had mixed success with other types, but don't trust it.) So, we are going to subclass this object. In the test project, I have the following. Note that your method visibility in your real class must allow subclasses to override, so it needs to be package private and in the same package or protected. Note that I am overriding the retriever and creator, returning instead static variables that my tests will set.
package org.myexample.fragments
// imports
public class MyFragmentStub extends MyFragment {
public static List<ListItem> LIST = null;
public static CustomAdapter ADAPTER = null;
/**
* Resets the state for the stub object. This should be called
* in the teardown methods of your test classes using this object.
*/
public static void resetState() {
LIST = null;
ADAPTER = null;
}
#Override
List<ListItem> retrieveListItems() {
return LIST_ITEMS;
}
#Override
CustomAdapter createCustomAdapter() {
return CUSTOM_ADAPTER;
}
}
In the same package in my test project I have the actual test of the fragment. Note that while I'm using Robolectric, this should work with whatever test framework you're using. The #Before annotation becomes less useful, as you need to update your static state for individual tests.
package org.myexample.fragments
// imports
#RunWith(RobolectricTestRunner.class)
public class MyFragmentTest {
public MyFragment fragment;
public Activity activity;
#After
public void after() {
// Very important to reset the state of the object under test,
// as otherwise your tests will affect each other.
MyFragmentStub.resetState();
}
private void setupState(List<ListItem> testList, CustomAdapter adapter) {
// Set the state you want the fragment to use.
MyFragmentStub.LIST = testList;
MyFragmentStub.ADAPTER = adapter;
MyFragmentStub stub = new MyFragmentStub();
// Start and attach the fragment using Robolectric.
// This method doesn't call visible() on the activity, though so
// you'll have to do that yourself.
FragmentTestUtil.startFragment(stub);
Robolectric.ActivityController.of(stub.getActivity()).visible();
this.fragment = stub;
this.activity = stub.getActivity();
}
#Test
public void dummyTestWithKnownValues() {
// This is a test that does nothing other than show you how to use
// the stub.
// Create whatever known values you want to test with.
List<ListItem> list = new ArrayList<ListItem>();
CustomAdapter adapter = mock(CustomAdapter.class);
this.setupState(list, adapter);
// android fest assertions
assertThat(this.fragment).isNotNull();
}
}
This is definitely more verbose than using a mocking framework. However, it works even with Android's life cycle. If I'm testing an Activity, I'll also often include a static boolean BUILD_FRAGMENTS variable. If true, I'll go call through to super in the appropriate methods or return a known fragment as appropriate. In this way I'm able to inject my test objects and play nice with the Android life cycle.

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, ...)).

call static void

How do I call my function?
public static void dial(Activity call)
{
Intent intent = new Intent(Intent.ACTION_DIAL);
call.startActivity(intent);
}
Obviously not with:
dial(); /*Something should be within the brackets*/
You should try
ClassName.dial();
The reason is that static methods belong the class itself, not to an individual instance of it. The call to instance.dial() is legal, but discouraged.
you should use your ClassName.StaticMethod.... to call a static method of a class
You can't pass null. You have to send a context object.
Where is your function located? If it's inside an Activity or the such, simply pass "this" as the parameter.
If it's inside an BroadcastListener, or a Service, just change the parameter to Context and pass "this".
What exaclty is the Problem?
If you've got a class like
public class Test {
public void nonStaticFct() {
staticFct();
}
public static void staticFct() {
//do something
}
}
Works perfectly (even if you should call static functions always by Classname.FctName (Test.staticFct())
I guess the problem here is the missing argument.
[Edit] Obviously I am wrong, according to the Java Code Conventions you may use a Classmethod by simply calling it, without using the classname (even if it seems odd, since I would expect an implicit this.staticFct() - but possibly the Java compiler is smart enough)

Categories

Resources