android


Is it good practice to develop a helper network class that is responsible for all network tasks?


I have created the following class:
public class AsyncHttpsClientHelper {
public static final int REMOVE_CREDIT_CARD = 1;
public static final int ENABLE_AUTORENEW = 2;
// +10 final ints...
private static AsyncHttpsClientHelper instance = null;
private static Activity activity;
// Some other variables
private AsyncHttpsClientHelper(Context context) {
// Initiate variables
}
public static AsyncHttpsClientHelper getInstance(Context context) {
// Guarantees the same instance for this class (Singleton)
}
public void performNetworkTask(int networkTaskType, String value)
{
switch (networkTaskType)
{
case REMOVE_CREDIT_CARD:
{
CupsLog.d(TAG, "path: " + Consts.ACCOUNT_REMOVE_CREDIT_CARD_PATH);
client.post(Consts.ACCOUNT_REMOVE_CREDIT_CARD_PATH , new JsonHttpResponseHandler() {
#Override
public void onSuccess(JSONObject result) {
try {
CupsLog.d(TAG, Consts.ACCOUNT_REMOVE_CREDIT_CARD_PATH + " -> onSuccess, result: " + result.toString(3));
AccountService.getInstance(activity).pullAccountDetailsFromServer();
Toast.makeText(activity, "Your credit card was removed", Toast.LENGTH_SHORT).show();
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Throwable arg0) {
CupsLog.d(TAG, "commitPaymentToServer -> onFailure");
BusProvider.getInstance().post(new DialogChangeEvent(DialogChangeEvent.REMOVE_CREDIT_CARD, "failed"));
}
});
break;
}
case ENABLE_AUTORENEW:
{
// Do ENABLE_AUTORENEW logic
}
// +10 cases goes here...
}
}
}
This class is not finished yet and I still have to add here another 10 other network calls I perform all around the application.
To run one of the network tasks I run this line for example:
AsyncHttpsClientHelper.getInstance(this).performNetworkTask(AsyncHttpsClientHelper.COMMIT_COUPON_TO_SERVER, event.getValue());
When the task finishes I fire an Event using the Square event Bus tool to commit the needed visual changes to the activity/ fragment layout.
The Question: My boss claims that this is a bad practice and that this class will become a mess when I finish it. More over he claims that this class should be a stupid class that all he knows how to do is to configure the AsyncHttpClient object and return it so I could use it and perform the https tasks inside the relevant Activity. Basically he says the the https calls themselves should be located in the Activity classes. I like this way better and think it makes my activities more cleaner and nicer to read. He on the other hand says that this way its harder to debug and that this class combines a part of Controller and View functionality which it should not do.
So who is right? is it really a bad practice to create a class like this?
Consider what Dimitry said about rotation, because its true. Never use static reference to objects that have any connection to the UI. It causes memory leaks, and it is very hard to debug.
Other problems:
Forget about getInstance(Context c). (actually, you had to write this because of the
static context reference)
Your performNetworkTask method has a "value" attribute that is only used in one of the tasks. Its confusing, noone would know what "value" is, only the person who wrote this code. What if you would need other parameters for other requests? Append them as parameters like "value"? It would be a mess. You should instead create an abstract Request class, and derive a new class for every request type. That way it would be much easy to understand what happens, and would be easy to extend the functionality also.
I guess your boss is trying to say that you should not wire the callbacks into your helper class. The networking can be handled by a separate class (optimally by a Service, or by the async http client), but the callback should definately be in the Activity, since that is where the reaction happens. How would you react in different ways for a request from separate Activities? With your implementation you can not, because all the callbacks are wired. Take a look at the site of the library, it has a relatively good example on how to achieve this:
http://loopj.com/android-async-http/#recommended-usage-make-a-static-http-client
And to answer your questions:
My boss claims that this is a bad practice, and that this class will
become a mess when I finish it.
Yes, it will become a mess (and will become even worse as you start to add/modify functionalities).
More over he claims that this class should be a stupid class that
all he knows how to do is to configure the AsyncHttpClient object and
return it so I could use it and perform the https tasks inside the
relevant Activity.
It should be stupid. In my opinion, you can keep the performNetworkTask method, BUT you need to have callback parameters to let callers react different.
public void performNetworkTask(Request request, JsonHttpResponseHandler handler);
You should also drop int networkTaskType. My suggestion is to use an abstract Request class. However if you prefer this way, then switching to Enum is a minimum.
Basically he says the the https calls themselves should be located in
the Activity classes.
"Https calls" can mean anything. I think he meant the callbacks should be passed by the caller.
I like this way better and think it makes my activities more cleaner
and nicer to read.
This way you definately have less code in your Activity, but you can support only one Activity! Basically, you just moved the missing code from the Activity to this helper class. Not to mention that it would be a pain to modify the helper in any way.
He on the other hand says that this way its harder
to debug and that this class combines a part of Controller and View
functionality which it should not do. So who is right? is it really a
bad practice to create a class like this?
I agree on that you try to wire the View part to a helper class.
I don't know what he means about "hard to debug", so the best would
be to ask him.
And yes, it is a bad practise. You should refactor.
Look at this presentation from Google IO 2010 about Android REST client apps -I think its complete answer for Your question.
https://www.youtube.com/watch?v=xHXn3Kg2IQE
Cheers :)
You keep static reference to your Activity - This is bad practice! What if the phone is rotated during long-running network operation.
The most robust and clean solution described in Google I/O presentation.
Your solution has some disadvantages:
singleton is not thread safe
it uses Activity context - you can easily get memory leak or call dead activity UI
UI and network calls are tightly linked
it supports only one Activity
As per your question - yes call AsyncHttpClient in place will result in more cleaner code that you suggests.
Perhaps this link from Misko Hevery:
http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars/
and its follow-up:
http://misko.hevery.com/2008/08/21/where-have-all-the-singletons-gone/
will help. He specifically deals with singletons in a credit card app and it has implicit MVC. MVC is ubiquitous. You usually need a good reason to depart from it.
You also have a large switch() in your logic. Usually that is handled by Polymorphism in OO. Methods are good at encapsulating functionality in a unit-test-friendly way.
I totally agree with your boss about that he said, it should be a dumb class. Instead of a Helper class you most likely need an Executor or Worker kind of a class. Singletons are most of time evil after all also you will leak your context eventually and end up memory leaks.
There is a way better approach, use self retained Fragments.
Now consider this, turn your helper class into a self retained Fragment which should be the connection holder for your system and should be able to execute some tasks over that connection. Also lose that switch...case and turn your tasks some kind of Runnable or Callable like classes.
Now you have a Fragment based executor like class and a bunch of task that can execute. This approach will bring you this advantages:
Since our fragment is self retained, it will act like a singleton,
You can use this fragment between activities, which is something you can't do when using a singleton,
Since fragment itself is a android component, android will provide you a context whenever you need it so you will not have to worry about leaking context or memory.
Even if you lose the current context(lets say user used home button) when some of your task is running, you can simple wait till another context is available(fragment re-attached to some activity)
By the way you can also use a Service for this instead of Fragments, same principles but might be a little bit harder to implement because Service-Activity communications adds another layer of complication.
Try this approach for your network related task:
1. Create HttpHelper class in which initialize http object and write helper methods to execute http requests.
2. Create separate AsyncTask for all your http requests and from this class initialize HttpHelper and perform network operations which gives 2 benefits:
a) Your code remains readable and modular which is easier to maintain and debug.
b) As the network requests are independant, it gives freedom to utilize this class anywhere within the application.
3. Communicating back from AsyncTask Interface, Handler can be used or you can directly pass the activity object but this way you can leak the context which can lead to memory leak.
Answer the following questions:
Will you need to repeat some logic in each of the methods of the
class? If yes - then most probably you need to split it into more
classes, in order to prevent code duplication.
Will your class have both customer-facing output (View), internal business logic (Controller) and data access/persistence logic
(Model) - if it would need to have any both of these, then you would
probably like to split it in order to separate presentation from
business logic and data access (MVC).
Does your class contain logic that could be potentially useful in other use cases/integrations/etc.? If yes, you might want to make this logic into a separate class (which to inherit or include as a property in your actual class.
It's always a good practice to make code more readable and easier to maintain. Encapsulating responsabilities is one of the best ways of doing both.
And a good argument to your boss would be: The connection between your View and Server STARTS at the View and END in the server, but the one who get it done (and is RESPONSABLE for it) is this class. Doesn't matter to the Activity or the Server how it's done, only the outcome.
Some modifications based on the code you've shared that i would suggest:
Remove the constructor and getInstance()
Create a initiator() method and initiate AsyncHttpClient, Gson and other params
Create other classes that extends your helper and implement their own especific performNetworkTask() (they could and shoud have their specific method name as well)
UseperformNetworkTask() statically
Start handling callbacks (the View is responsable for this)
Would be something like this:
public class HttpsClientAsyncHelper {
Acitivity activity;
AsyncHttpClient client;
Gson gson;
// Other params
public static void initiator(Context context)
{
activity = (Activity) context;
client = new AsyncHttpClient().setCookieStore(CookieUtil.getInstance(activity)
.getPersistentCookieStore());
gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create();
}
// Other methods
}
And create 10 more classes like this :
public class RemoveCreditCardAsyncTask extends HttpsClientAsyncHelper {
public static void performNetworkTask(Context context)
{
initiator();
// Do what you did in this specific case
}
}
Usage:
RemoveCreditCardAsyncTask.performNetworkTask(this);
// specific method names
AutoRenewAsyncTask.enableAutoRenew(this);
AutoRenewAsyncTask.disableAutoRenew(this);
// even more specific
AutoRenewAsyncTask.enable(this);
AutoRenewAsyncTask.disable(this);
I practice this myself in an even more generic class called Network that handles all the communication with my WebService:
serverPostRequest(String controller, String action, JSONObject jsonParameters) throws Exception {}
fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {}
Bonus: A probably usefull method for you:
public static boolean isAvailable(Context context)
{
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}

Related Links

Android - Placing one layout on top of another layout
handling token changes in notification groups fcm
Multiple colors in Toolbar title?
Intent overlap the dialog box?
Calling a Fragment Method in an Activity causes a Null Pointer Exception
How can obfuscate Sqlite Database in android
Snackbar Not working on API 17
How can I edit the header in a blank activity?
How do i access local area code in Android from locale?
Image from Android to PC
Charles Proxy + Android HTTPS
ScaleGestureDetector and GestureDetector in same view
Is there a performance impact on changing color of vector drawables programmatically?
Android web view does not load specific url
Attempt to invoke virtual method 'void android.widget.CompoundButton.setOnCheckedChangeListener on a null object reference on TabLayout [duplicate]
Retrieve all data from mysql database and display in listview android

Categories

HOME
url-redirection
asp.net-web-api
ng-idle
forms
pyqt
spring-data-redis
xbee
google-admin-sdk
crystal-reports-2013
categorical-data
octave
dkim
jxl
supervisor
renjin
plink
eip
symbol
glyphicons
fileserver
google-container-registry
selinux
rubymotion
libigl
backpack-for-laravel
dataflow-diagram
interpreter
game-theory
opentk
appirater
8051
bringtofront
left-join
testbed
angularjs-material
rselenium
laravel-eloquent
papaparse
gnat-gps
carriage-return
imx6
magento-1.9.2.4
salesforce-communities
release
skylink
attributeerror
datacachefactory
wmp
glm-math
contract
flask-login
magento-2.0.7
linter
configurationmanager
context-switch
gpg-signature
gameplay-kit
mathcad
django-1.10
centrifuge
file-uri
uicolor
greatest-n-per-group
cfchart
stream-processing
deep
query-by-example
findfirst
asyncdisplaykit
sapi
xenomai
rate-limiting
zen-cart
typemock
uncompress
java-security
bcache
gamekit
code-documentation
openbabel
primitive-types
ssis-data-flow
appserver
human-computer-interface
ss7
functional-java
paste
authlogic
websphere-esb
script#
scringo
gpars
nevron
css-reset
dolby-audio-api
video-codecs
insert-id
redis-py
uimanageddocument
veracity
objectquery
koken
kqueue
user-tracking
matlab-load
vs-android
request-headers
strtod
unmanagedresources
great-circle

Resources

Mobile Apps Dev
Database Users
javascript
java
csharp
php
android
MS Developer
developer works
python
ios
c
html
jquery
RDBMS discuss
Cloud Virtualization
Database Dev&Adm
javascript
java
csharp
php
python
android
jquery
ruby
ios
html
Mobile App
Mobile App
Mobile App