Logging switch realization - java

This code:
public class MyActivity extends Activity {
private final boolean logging = getResources().getBoolean(R.bool.logging);
#Override
public void onCreate(Bundle savedInstanceState) {
if(logging) Log.d("my_log", "some text here");
// some onCreate code...
}
}
generates NullPointerException.
But this one:
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
final boolean logging = getResources().getBoolean(R.bool.logging);
if(logging) Log.d("my_log", "some text here");
// some onCreate code...
}
}
Does not.
The main idea to switch logging in entire application with a boolean resource.
I can successfully declare this variable for every function in class, but can it be done for entire class just once?

Have you considered using a proper logging framework?
If you use the slf4j API you can write stuff like
log.debug("A={}, B={}", a, b)
where the switch is externally set in a well-documented way whether to generate a log statement or not. Also the slf4j {}-construct allows delaying the call to a.toString() and b.toString() until after the logging framework has decided that the log message actually needs to be generated.
slf4j is an API. You have several backends to choose from. For starters you can just pick the "simple" backend.
See http://slf4j.org/manual.html for an introduction.

Instead of putting value to string resource, I used static variable in special class.
public class constants {
public static final boolean logging = true;
}
so it can be accessed from any activity:
private boolean logging = constants.logging;

Related

Mockito tests for class with Handler

I'm a new one in android dev, so I have an app which contain viewPager with 2 UI fragments and 1 nonUIFragment in which operations are performed (i used "setRetainInstance(true)", it deprecated, but i must use it). In this nonUIFragment i have Handler which accepts messages from operations started with ExecutorServices.
But now my task is test this app with Mockito and i'm totaly confused.
Mentor said "you have to mock the operation that produces the result, is performed in a nonUIFragment, and its result is stored in a collection."
How must look this test, I can't create spy() class NonUIFragment and use real methods because of "Method getMainLooper in android.os.Looper not mocked."
All of my methods are void, they don't returne something, how can i trace this chain.
NonUIFragment.java
private NonUIToActivityInterface nonUIInterface;
private final Map<DefOperandTags, HashMap<DefOperationTags, String>> allResultsMap
= new HashMap<>();
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
//Handler pass result to here
public void passAndSaveResult(DefOperandTags operandTag, DefOperationTags operationTag, String result) {
allResultsMap.get(operandTag)).put(operationTag, result);
}
private final Handler handler = new Handler(Looper.getMainLooper()) {
public void handleMessage(Message msg) {
if (msg.what != null)
passAndSaveResult(defOperandTags, defOperationTag, msg.obj.toString());
};
OneOfOperation.java (add value to the List)
public class AddToStartList extends Operation {
public AddToStartList(List list, DefOperationTags operationTag) {
super(list);
key = operationTag;
}
#Override
public void operation(Object collection) {
((List)collection).add(0, "123");
}
So, how can I implement what my mentor said?
This is going to be tricky, because your Android testing library has no implementations, and static methods are generally more difficult to mock safely and effectively.
Recent versions of Mockito have added the ability to mock static methods without using another library like PowerMock, so the first choice would be something like that. If at all possible, use mockStatic on Looper::getMainLooper to mock.
Another solution is to add some indirection, giving you a testing seam:
public class NonUIFragment extends Fragment {
/** Visible for testing. */
static Looper overrideLooper;
// ...
private final Handler handler = new Handler(
overrideLooper != null ? overrideLooper : Looper.getMainLooper()) {
/* ... */
};
}
Finally, if you find yourself doing this kind of mock a lot, you can consider a library like Robolectric. Using Robolectric you could simulate the looper with a ShadowLooper, which would let you remote-control it, while using Mockito for any classes your team has written. This would prevent you from having to mock a realistic Looper for every test, for instance.

Android: Updating textviews in multiple activities

I need some pointers on doing the following:
lets say i have 10/20 (number doesn't matter) of activities.
each of these activities has a textview that should work like a counter.
each of these activities has a button to go to the next activity.
this counter starts when the app is launched, and increment itself every second.
So what i did so far is:
have in my main activity a method that instantiate a class that extends Thread.
In that class in the run() method, i increment a variable when a second passes.
Now i'm stuck on what i should do next. Any pointers would be appreciated thanks.
Edit: i need a way to communicate from inside the run method, to whichever activity is now currently on screen, to update its textview.
Just a bit of theory here for standard Object Oriented Programming : stick to the recommended principles like Loose Coupling which makes your project code less tied to each other. You can read more on that later.
Now, using Events, you can setup a system that is synonymous with the natural Publisher/Subscriber design pattern. Like this:
The activity that needs to notify the other activities is called Publisher and the other activities that need to be notified are called Subscribers.
From here:
There are already built and tested libraries to do Events in android. Like my favorite EventBus.
Step 1 Add this line to your app-level build.gradle file:
compile 'org.greenrobot:eventbus:3.0.0'
Then create a simple Plain Old Java Object aka POJO class like this:
public class UpdateTextViewEvent{
private String textToShow;
public UpdateTextViewEvent(String text){
this.textToShow = text;
}
//add your public getters and setters here
}
Step 2 Notify others:
When you want to notify anyone of the changes, you simply called this method:
EventBus.getDefault().post(new UpdateTextViewEvent("Some new Text"));
Step 3 Receive notifications
For those who want to be notified of this event, simply do this:
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
NOTE: to actually handle the event:
#Subscribe
public void onEvent(UpdateTextViewEvent event){
String text = event.getTextToShow();
//now you can show by setting accordingly on the TextView;
}
This is so much easier to do, do decouple your code by eliminating static references in your different activities
I hope this helps! Good luck!
make that Textview in second class as
public static Textview text;
and call it in main activity as
SecondActivity obj=new SecondActivity();
obj.text.settext("");
You can create one another activity e.g. BaseActivity extend with Activity class and your all 10/20 activity extends with created BaseActivity Class.
You can use your textview with protected access specifiers.
What you need to do is inside the counter class, create an a method and passed in a TextView as the parameter. Then create an int variable and set the counter as the instance:
Like this
public static class Counter extends Thread{
private static int x;
#Override
public void run(){
x = counter;
}
public void setCounter(TextView tv){
tv.setText(String.valueOf(x));
}
}
Now call this method setCounter(TextView) in all the activity's onCreate() method you'll like to display the counter, and passed in your the layout TextView as the argument. Like this
...
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState):
....
TextView cTextView = (TextView)findViewById(R.id.texT1);
Counter c = new Counter();
c.setCounter(cTextView);
}

Logcat during production

I used to use my a custom class that is responsible for logging with the following implementation
public class Logger {
public final static boolean DEBUG = true;
public static void d(String tag, String msg) {
if(DEBUG)
Log.d(tag, msg);
}
}
When I release the app I set the flag to false, however I was said that the following method is not that efficient since the Strings are still being allocated and then deallocated in the memory
the logger being used like that:
Logger.d("tag", "message");
or maybe like that, by doing that the StringBuilder will be invoked
Logger.d("tag", "server response: " + response + " timing: " + timing);
Is that true, can't the dalvik/compiler optimize it to prevent such behaviour. and if so, is there something I can do to optimize it other than moving the if outside?
Try to use Proguard to entirely remove logging from bytecode of your production apk. On debug version just disable "proguarding".
E.g.:
Remove all debug logging calls before publishing: are there tools to do this?
By the way, few unnecesary string concatenations are not good, but usually are not that hard (unless you are doing it many times in the loop), so don't demonize it :)
A simple solution would be to just just declare two String variables in your Activity and reuse them whenever you add a log entry like so:
public class YourActivity extends Activity
{
String tag;
String message;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Rest of Activity
Then whenever you want to add a log entry simply set the tag/message value:
tag = "myTag";
message = "myMessage";
Logger.d(tag, message);
This way your if check remains inside Logger and no additional memory is allocated.

Separate a java listener to its own function?

Can I break the set-listener line into smaller pieces?
Here is the code I have:
protected void onCreate(Bundle savedInstanceState) {
Preference button = (Preference)getPreferenceManager().findPreference("exitlink");
button.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
#Override
public boolean onPreferenceClick(Preference arg0) {
finish();
return true;
}
});
I would like this to look something like:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Preference button = (Preference)getPreferenceManager().findPreference("exitlink");
if(button != null) {
button.setOnPreferenceClickListener(onPreferenceClick);
}
}
public boolean onPreferenceClick(Preference arg0) {
finish();
return true;
}
You can also create a variable outside of your method:
private Preference.OnPreferenceClickListener listener = new Preference.OnPreferenceClickListener() {
#Override
public boolean onPreferenceClick(Preference arg0) {
finish();
return true;
}
};
Then you use it as a variable: setListener(listener). This would allow you to have multiple instances of the same listener class in your Activity.
Your code above nearly works already. Use your above code with this tiny change:
button.setOnPreferenceClickListener(this);
Then you just let your class implement the specific interface needed, in this case Preference.OnPreferenceClickListener.
In addition to dmon's suggestion below about using variables for this, it is also possible to write a function that returns a listener, which is very useable when you want to have similar listeners but with slight changes, like in the example below.
private Preference.OnPreferenceClickListener getListener(int listenerId) {
return new Preference.OnPreferenceClickListener() {
#Override
public boolean onPreferenceClick(Preference arg0) {
Log.i("MyTag", "Listener " + listenerId + " invoked!");
finish();
return true;
}
};
}
As others have mentioned, even though you cannot pass a method name to setOnPreferenceClickListener you can create a variable of a type that extends Preference.OnPreferenceClickListener. In your original code, that is actually exactly what you are doing: you are creating an object of an anonymous inner class.
The advantage of this approach, say over Simon André Forsberg's answer above is of scope: it keeps the listener functionality in that small block, instead of potentially all over the class.
Creating a separate variable outside the method as in dmon's answer loses one big benefit of the anonymous inner class, that they can access the variables in the containing scope: in your original code, the listener can access the variables button and savedInstanceState. This is not possible with a separate variable defined outside the function.
None of this means that you must use anonymous inner class. Oracle has an excellent tutorial titled General Information about Writing Event Listeners that you will greatly benefit from.
Not exactly. The set-listener requires an instance of listener, so you always need to create one. And I don't think it's a good manner for activity implementing listener interfaces.
The workaround is that you can use annotations with reflection, such as http://code.google.com/p/roboguice/. This may make the code cleaner, but also introduces dependencies.

Android: Referring to a string resource when defining a log name

In my Android app, I want to use a single variable for the log name in multiple files. At the moment, I'm specifying it separately in each file, e.g.
public final String LOG_NAME = "LogName";
Log.d(LOG_NAME, "Logged output);
I've tried this:
public final String LOG_NAME = (String) getText(R.string.app_name_nospaces);
And while this works in generally most of my files, Eclipse complains about one of them:
The method getText(int) is undefined
for the type DatabaseManager
I've made sure I'm definitely importing android.content.Context in that file. If I tell it exactly where to find getText:
Multiple markers at this line
- Cannot make a static reference to the non-static method getText(int)
from the type Context
- The method getText(int) is undefined for the type DatabaseManager
I'm sure I've committed a glaringly obvious n00b error, but I just can't see it! Thanks for all help: if any other code snippets would help, let me know.
That's because getText is a method of Context. It does not matter if you import the Context class; what matters is that you invoke that method from a Context (for instance, the Activity class is a Context (it inherits Context)).
In that case, what I'd recommend, is creating a Application class that returns the context you want. Here I explain how to do it. After that you can do something like:
public final String LOG_NAME = (String) App.getContext().getText(R.string.app_name_nospaces);
Depending on what sort of 'files' you are using, you can define a TAG that is used.
For example, when I create an app, I like to create a base class for my Activity classes...
Suppose my app is called 'Wibble', and my package is com.mydomain.Wibble...I create my base Activity like so...
package com.mydomain.Wibble
public class WibbleActivity extends Activity {
final protected String TAG = this.getClass().getName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// I'll explain how this next line works later
android.util.Log.d(TAG, "Entered onCreate()...");
}
}
Now suppose I derive an activity as follows...
package com.mydomain.Wibble
public class SomeActivity extends WibbleActivity {
#Override
protexted void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Don't Log "Entered onCreate()..." - WibbleActivity does it for me
android.util.Log.d(TAG, "SomeText");
}
}
Then I derive another Activity...
package com.mydomain.Wibble
public class SomeOtherActivity extends WibbleActivity {
#Override
protexted void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Don't Log "Entered onCreate()..." - WibbleActivity does it for me
android.util.Log.d(TAG, "SomeOtherText");
}
When onCreate() is called for SomeActivity, the output will be...
com.mydomain.Wibble.SomeActivity Entered onCreate()...
com.mydomain.Wibble.SomeActivity SomeText
...when onCreate() is called for SomeOtherActivity however, the output will be...
com.mydomain.Wibble.SomeOtherActivity Entered onCreate()...
com.mydomain.Wibble.SomeOtherActivity SomeOtherText
Neither activity needs to know specifics through an explicit string and the package name is prefixed. Obviously it will only work in certain situations but I find it useful.

Categories