First off - I'm rather novice at Java so if the question makes no sense do let me know.
Basically I'm making an Android app which communicates with my web service and so I've made a separate class to deal with the communication, which also includes the AsyncTask (I've removed a lot from the code here just for preview):
public class api {
private String caller = null;
Context that = null;
api(Context that) {
this.that = that;
this.caller = that.getClass().getSimpleName();
}
void call(String action) {
/* .... */
}
new back().execute(param1, param2);
}
void callback(String action, String result){
that.callback(action, result);
}
public class back extends AsyncTask<String, Void, String> {
public String response = null;
protected String doInBackground(String... params) {
response = connection.executeRequest(params[1]);
return response;
}
protected void onPostExecute(String result) {
callback("a", "b");
}
}
}
And when I use the class from some part of the app (let's say SomeClass.class), I do:
api WS = new api(this);
WS.call("....");
And it's supposed to execute the function 'callback' which is in SomeClass.
But the key problem here is this line:
that.callback(action, result);
Eclipse makes me add the name of the "caller" class in the cast:
(SomeClass) that.callback(action, result);
But that doesn't work for me, because I use the 'api' class from many different classes, so ideally I need to put a variable in the cast. I do get the name of the "caller" class here:
this.caller = that.getClass().getSimpleName();
//obviously this won't work:
(this.caller) that.callback(action, result);
Is there anyway to do that, or am I doing something fundamentally wrong?
Thank you.
Currently your api class accepts a Context object in its default constructor. It would make more sense to extend Context with a new class which contains a callback method which you can then override in subclasses such as SomeClass, that would negate the need for casting in your api class. e.g:
public class APIContext extends Context
{
public void callback( String action, String result )
{
/* ... */
}
}
public class SomeClass extends APIContext
{
#Override
public void callback( String action, String result )
{
/* ... */
}
}
public class api
{
private APIContext callerContext = null;
public api( APIContext context )
{
this.callerContext = context;
}
public void callback( String action, String result )
{
callerContext.callback( action, result );
}
}
Related
i have this code :
public class WeatherLoader extends AsyncTaskLoader {
/** Tag for log messages */
private static final String LOG_TAG = WeatherLoader.class.getName();
private String mUrl;
private int mDataWeatherType;
public WeatherLoader(Context context, String url , int dataWeatherType) {
super(context);
mUrl = url;
mDataWeatherType = dataWeatherType;
}
#Override
public Object loadInBackground() {
Log.i(LOG_TAG , "TEST : loadInBackground() called ...");
if(mUrl == null){
return null;
}
if( mDataWeatherType == 1) {
CurrentWeather currentWeather = QueryUtils.fetchCurrentWeatherData(mUrl);
return currentWeather;
}else if(mDataWeatherType == 2) {
List<HourForecast> hourlyForecastsList = QueryUtils.fetchHourlyForecastsData(mUrl);
return hourlyForecastsList;
}else {
List<DayForecast> dailyForecastsList= QueryUtils.fetchDailyForecastsData(mUrl);
return dailyForecastsList;
}
}
}
in the main activity :
#Override
public Loader<List<HourForecast>> onCreateLoader(int id, Bundle args) {
return new WeatherLoader(this,mUrl,HOURLY_FORECASTS);
}
#Override
public void onLoadFinished(Loader<List<HourForecast>> loader, List<HourForecast> data) {
mHourForecastAdapter.clear();
mHourForecastAdapter.addAll(data);
}
#Override
public void onLoaderReset(Loader<List<HourForecast>> loader) {
mHourForecastAdapter.clear();
}
in the AsyncTaskLoader i do not specify generic type, and in the LoaderManager.LoaderCallbacks<List<HourForecast>> i specify generic type,
the code work correctly.
Could someone explain me how the result of loadInBackground gets passed on to onLoadFinished? I'm asking this as loadInBackground returns an Object and onLoadFinished accepts a List<HourForecast> and not an Object.
In java using generics removes the need for cast by the programmer and object in java can be anything, since its OOP every class extends Object by default.
In you case AsyncTaskLoader has a generic that extends Loader. If you do not specify the object with generic, the return object is Object.
Which means in the method
Loader<List<HourForecast>> onCreateLoader(int id, Bundle args) {
return new WeatherLoader(this,mUrl,HOURLY_FORECASTS);
You already are creating WeatherLoader you cast it to Loader (which is superclass of AsyncTaskLoader) And you cast it to Loader<List<HourForecast>> there for you get you list when you call
#Override
public Object loadInBackground()
However, this is a very bad example of generics you have there. Generics are made to eliminate runtime errors, and your example just makes more places to have a runtime error. And Please don't use AsyncTasks :) They are the evil. Read some basic books on android programming, it teaches you to use handlers. The ultimate solution to your threading would be RxJava, but its more for advanced programmers.
I am new to java generics and below is the code that has created a confusion for me to pass generics class as an argument to the method.
I have created an android project where i have used Volley library to handle server calls.Below is the code
Advanced Connection Util : this class returns the JacksonRequest object
public class AdvancedConnectionUtil<T> {
private String requestType;
private ServerListener listener;
public AdvancedConnectionUtil(String requestType , ServerListener<T> listener){
this.listener = listener;
this.requestType = requestType;
}
public JacksonRequest getRequest(){
//This gives compile error while while passing DataList.class in the argument
return new JacksonRequest<T>(Request.Method.GET, HttpRequestConstant.JACKSON_FETCH, null ,DataList.class, new Response.Listener<T>() {
#Override
public void onResponse(T response) {
listener.onDataReceived(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
listener.onErrorReceived(error.getMessage());
}
});
}
public interface ServerListener<T> {
public void onDataReceived(T data);
public void onErrorReceived(String errorMsg);
}
}
Custom JacksonRequest class : this class handles the server call and the success call backs
public class JacksonRequest<T> extends JsonRequest<T> {
private Class<T> responseType;
/**
* Creates a new request.
* #param method the HTTP method to use
* #param url URL to fetch the JSON from
* #param requestData A {#link Object} to post and convert into json as the request. Null is allowed and indicates no parameters will be posted along with request.
* #param responseType
* #param listener Listener to receive the JSON response
* #param errorListener Error listener, or null to ignore errors.
*/
public JacksonRequest(int method, String url, Object requestData, Class<T> responseType, Response.Listener<T> listener, Response.ErrorListener errorListener) {
super(method, url, (requestData == null) ? null : Mapper.string(requestData), listener, errorListener);
this.responseType = responseType;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return super.getHeaders();
}
#Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
return Response.success(Mapper.objectOrThrow(json, responseType), HttpHeaderParser.parseCacheHeaders(response));
} catch (Exception e) {
return Response.error(new ParseError(e));
}
}
}
Here is My activity class that creates a request and pass it to other method to make a server call
public class CustomJacksonRequestActivity extends SuperActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showProgressBar();
JacksonRequest jacksonRequest = new AdvancedConnectionUtil<DataList>(null, httpListener).getRequest();
//This commented code works fine when i create a request this way
/* JacksonRequest<DataList> jacksonRequest = new JacksonRequest<DataList>(Request.Method.GET, HttpRequestConstant.JACKSON_FETCH, null, DataList.class, new Response.Listener<DataList>() {
#Override
public void onResponse(DataList response) {
hideProgressBar();
Log.e("ANSH", "onResponse : " + response.getPicture());
// fillListWithIndex(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
hideProgressBar();
Log.e("ANSH", "onErrorResponse : " + error.getLocalizedMessage());
}
});*/
onExecute(jacksonRequest);
}
#Override
protected void internetAvailable() {
}
#Override
public void setTitle(CharSequence title) {
super.setTitle("CustomJacksonActivity");
}
private AdvancedConnectionUtil.ServerListener httpListener = new AdvancedConnectionUtil.ServerListener<DataList>() {
#Override
public void onDataReceived(DataList data) {
Log.e("ANSH", "onResponse : " + data.getPicture());
}
#Override
public void onErrorReceived(String errorMsg) {
Log.e("ANSH", "onResponse : " + errorMsg);
}
};
Now the problem is i am not able to pass DataList.class(This is the response model class) as a parameter in the constructor of JacksonRequest Class inside the getRequest method of AdvancedConnectionUtil class though i am able to do that when i was creating the request object in the activity(see the commented code in the activity).
How can i pass the DataList.class to the constructor of JacsonRequest?
The updated code is in git hub now
github link to the project
Your code is a little confusing (you could do with a simpler example), but I'll give it a shot...
The JacksonRequest class takes one generic type parameter <T> and the type of 4th constructor parameter references this type: , Class<T> responseType,. That means that when an instance of JacksonRequest is instantiated (with T being a real type), the 4th parameter passed must guarantee to be of type T.class.
When you call the constructor...
return new JacksonRequest<T>(Request.Method.GET, blah, null ,DataList.class, ...
... you are calling it with a generic type argument <T>. The compiler must always be able to match generic type arguments with generic type parameters, but with the 4th parameter you are asking it to match T with DataClass. Since the compiler does not know what T is (or more precisely, it cannot guarantee that when it is instantiated, T will actually be DataClass), it gives an error.
Generally speaking, you cannot mix generic type parameters and real type values - you must consistently choose one or the other. There are options which allow you to specify that a generic type parameter will derive from a class or implement an interface (<T super DataClass> or <T extends DataClass>, but it's a bit much to explain here.
The problem with your code is that :
Your JacksonRequest<T> is declared to take a listener that must be parameterized on the same type as your responseType
However with your AdvancedConnectionUtil<T>.getRequest(...), there is no guarantee that the JacksonRequest created will meet the above requirement. Because you can always write code like:
new AdvancedConnectionUtil<String> (null, httpListener).getRequest();
So the parameter you passed into the constructor of JacksonRequest will be a DataList.class (of type Class<DataList>) and a listener of type Listener<String>
Sadly there is no way to do T.class in Java, although that indeed feels like what you need. Usually in such cases we will declare the AdvancedConnectionUtil as:
class AdvancedConnectionUtil<T> {
private final Class<T> responseType;
private final ServerListener<T> serverListener;
private final String requestType;
public AdvancedConnectionUtil (String requestType , Class<T> responseType, ServerListener<T> listener) {
this.requestType = requestType;
this.responseType = responseType;
this.serverListener = listener;
}
public JacksonRequest<T> getRequest(){
return new JacksonRequest<T>(0, "", null ,responseType, new Response.Listener<T>(){
...
}
}
You need pass your responseType of Class<T> into AdvancedConnectionUtil and keep it as a member field. This way the AdvancedConnectionUtil instance, when created, is strictly limited to provide JacksonRequest bounded to a specific response type.
You could in theory declare your getRequest method as getRequest(Class<T> responseType). But your AdvancedConnectionUtil does not seem to gain anything from that
Currently developing a weather application . I wanna strictly observe 2 rules:
MVC Design pattern
Multithreading when I deals with network.
The problem is combining these things into a whole, here are parts of my code:
Weather Class (represent a Weather object) :
public class Weather {
private int mTimeInSeconds;
private String mTimeZone;
private int mTemperature;
private String mSummary;
public Weather(int timeInSeconds, String timeZone, int temperature, String summary) {
mTimeInSeconds = timeInSeconds;
mTimeZone = timeZone;
mTemperature = temperature;
mSummary = summary;
}
public String getTime() {
SimpleDateFormat formatter = new SimpleDateFormat("h:mm a");
formatter.setTimeZone(TimeZone.getTimeZone(mTimeZone));
Date dateTime = new Date(mTimeInSeconds * 1000);
String timeAsString = formatter.format(dateTime);
return timeAsString;
}
public int getTemperature() {
return mTemperature;
}
public String getSummary() {
return mSummary;
}
}
Worker Class (do all "dirty" work):
public class Worker {
private final OkHttpClient mClient = new OkHttpClient();
private String apiKey = "Secret!11";
private double latitude = 37.8267;
private double longitude = -122.423;
private String forecastUrl = "https://api.forecast.io/forecast/"
+apiKey
+"/"
+latitude
+","
+longitude;
public void getCurrentWeather() throws Exception {
final Request request = new Request.Builder()
.url(forecastUrl)
.build();
mClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
Log.v(MainActivity.TAG, e.getMessage());
}
#Override
public void onResponse(Response response) throws IOException {
try {
JSONObject weatherData = new JSONObject(response.body().string());
JSONObject currentlyWeather = weatherData.getJSONObject("currently");
Log.v(MainActivity.TAG, currentlyWeather.toString());
} catch (JSONException e) {
Log.v(MainActivity.TAG, e.getMessage());
}
}
});
}
}
Based on my understanding of MVC I put all data and logic around that data in Model (Worker and Weather classes). I wanna to achieve something like this in
MainActivity.java:
...
Worker mWorker = new Worker();
Weather mWeather = mWorker.getWeatherData();
...
2 questions:
Is this the correct design of MVC? (I mean, that all code which somehow work with data separated from the controller which only update view's)
If yes, how I can implement this? I need to return Weather object
from Worker, but I can't do this because it's happen on separate thread, I wanna return Weather object to main thread, but have no idea how to implement this.
As far as I know, your Model is your Weather.java class, your MainActivity (and additional added Activities) are your Controller and
finally your layout.xml files are your View. So AFAIK yes,
this is correctly implemented followed the MVC pattern.
It sounds as your Worker.java class should be of an AsyncTask
implementation.
An example using AsyncTask (This is just a stub implementation, see the links below to see fully working examples):
private class WorkerTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
//Network calls go here. This will be done in the background,
//in a separate thread
//Finally, you return your result here to onPostExecute
return result;
}
#Override
protected void onPostExecute(String result) {
//This method runs on the main UI thread. Here you update your UI o return data to your caller class.
}
#Override
protected void onPreExecute() {
//Called before execution
}
#Override
protected void onProgressUpdate(Void... values) {
//If you want to update your UI with your current progress, that code goes here
}
}
Finally, you will execute your AsyncTask like this:
new WorkerTask().execute("your_url");
More examples using AsyncTask here, here and here.
Official documentation is found here.
Regarding AsyncTask and the MVC design pattern
A downside of this is that the AsyncTask has to be implemented in your MainActivity (or wherever you want to make use of it) which contradicts the MVC design.
A solution to this would be to create a "HTTP caller class" which does the network calls, and which is called in your AsyncTask doInBackground method.
Using this approach, you would be good to go. You would have your controller (MainActivity) executing the AsyncTask (but not doing any actual network calls in MainActivity) creating a new HttpCallerClass, then calling a method which handles the networking. Hence, MVC is preserved.
Given the following abstract class:
public abstract class BaseVersionResponse<T extends BaseVO> {
public abstract void populate(T versionVO);
}
and the following child class:
public class VersionResponseV1 extends BaseVersionResponse<VersionVOV1>
{
protected String testFieldOne;
protected String testFieldTwo;
public String getTestFieldOne() {
return testFieldOne;
}
public void setTestFieldOne(String value) {
this.testFieldOne = value;
}
public String getTestFieldTwo() {
return testFieldTwo;
}
public void setTestFieldTwo(String value) {
this.testFieldTwo = value;
}
#Override
public void populate(VersionVOV1 versionVO) {
this.setTestFieldOne(versionVO.getFieldOne());
this.setTestFieldTwo(versionVO.getFieldTwo());
}
I desire to do something like this from a calling method:
public void getVersionInfo(String version) {
BaseVO versionVO = null;
BaseVersionResponse<? extends BaseVO> baseVersionResponse = null;
baseVersionResponse = createVersionResponse(version);
versionVO = createVersionVO(version);
baseVersionResponse.populate(versionVO);
}
where createVersionResponse(...) and createVersionVO(...) look like this:
public BaseVersionResponse<? extends BaseVO> createVersionResponse(String version) {
BaseVersionResponse<? extends BaseVO> specificVersionResponse = null;
if (version.equalsIgnoreCase("V1")) {
specificVersionResponse = new VersionResponseV1();
} else if (version.equalsIgnoreCase("V2"))
specificVersionResponse = new VersionResponseV2();
return specificVersionResponse;
}
public BaseVO createVersionVO(String version) {
BaseVO versionVO = null;
if (version.equalsIgnoreCase("V1")) {
versionVO = new VersionVOV1();
} else if (version.equalsIgnoreCase("V2"))
versionVO = new VersionVOV2();
return versionVO;
}
and VersionVOV1 looks like this:
public class VersionVOV1 extends BaseVO {
private String fieldOne = null;
private String fieldTwo = null;
private String fieldThree = null;
public String getFieldOne() {
return fieldOne;
}
public void setFieldOne(String fieldOne) {
this.fieldOne = fieldOne;
}
public String getFieldTwo() {
return fieldTwo;
}
public void setFieldTwo(String fieldTwo) {
this.fieldTwo = fieldTwo;
}
public String getFieldThree() {
return fieldThree;
}
public void setFieldThree(String fieldThree) {
this.fieldThree = fieldThree;
}
}
My problem arises when I try to compile this line of code:
baseVersionResponse.populate(versionVO);
in getVersionInfo(...). I'm getting a message that looks like this:
The method populate(capture#3-of ?) in the type BaseVersionResponse is not applicable for the arguments (BaseVO)
on the populate method above.
My thought was (which is apparently incorrect) that since the baseVersionResponse is, at this point in the code, actually a specific child instance, that the class would know exactly which populate method to call from that specific child class.
What am I doing wrong here? Is there a better way to do this if this isn't the correct approach?
Thank you for your time!
Ok, I took a better look at this today. The problem is that the wildcard, while the right way to go, precludes you from doing:
BaseVO versionVO = createVersionVO(version);
Because the populate call wants an extension of BaseVO, not an actual BaseVO, which doesn't qualify. That means you can't pass that versionVO variable directly.
So, to keep the type checking in place, which I think is good because you'll always want an implementation, leave pretty much everything as-is above, and change your BaseVersionResponse class to something like:
public abstract class BaseVersionResponse<T extends BaseVO> {
public T getVersion(BaseVO versionVO) {
try {
return (T) versionVO;
} catch (ClassCastException e) {
throw new IllegalArgumentException();
}
}
public abstract void populate(BaseVO versionVO);
}
So, populate method now takes a BaseVO, and there's a new getVersion method to do some explicit casting for us. This should be ok since we know that the factory will always supply the right thing, but if another caller doesn't, an IllegalArgumentException is thrown.
Now, in your response class implementation, change the populate method accordingly:
public void populate(BaseVO version) {
VersionVOV1 versionVO = getVersion(version);
this.setTestFieldOne(versionVO.getFieldOne());
this.setTestFieldTwo(versionVO.getFieldTwo());
}
So, we've changed the populate method to take BaseVO, and the getVersion method does the casting for us. All the other type checks still apply, and we're good to go.
The casting makes it feel not as clean, but for the factory approach you're using, it's really the only way (I can think of) to keep the guarantees made by the type declarations and the code pattern in tact.
Hope that helps!
If you just take out the capture of type (the "<?>"), and leave it unchecked, it should work just fine. Even using type Object would have compiled.
But, given your specific example, what you probably want is the method:
public BaseVersionResponse<?> createVersionResponse(String version)
Changed to:
public BaseVersionResponse<? extends BaseVO> createVersionResponse(String version)
Then, instead of using
BaseVersionResponse<?>
use
BaseVersionResponse<? extends BaseVO>
Since you know that the return type will be one of those things that implements the interface/class.
I'm new in Android. It's been a few weeks since I started working in this project. Reading the code already made I see a lot of private async tasks doing basically the same (call an API) So I wonder if there is a better solution, my idea would be create a public async task called ApiCaller which will return a JSONObject and the responsible for parsing the JSON would be the class calling the ApiCaller:
public class ApiCaller extends AsyncTask<String, String, JSONObject> {
private static final String TAG = "ApiCall";
private final String apiVersion = "v1";
private final String baseURL = "http://my.api.com/";
private String URL = null;
/**
* Generates the URL to call the API.
*
* #param params List with the params to call the API.
*/
public ApiCaller(ArrayList<NameValuePair> params){
String apiURL = this.baseURL + this.apiVersion + "/?";
String paramsList = URLEncodedUtils.format(params, "utf-8");
this.URL = apiURL + paramsList;
}
#Override
protected JSONObject doInBackground(String ... params) {
Log.i(TAG, "API:");
Log.i(TAG, this.URL);
JSONManager jParser = new JSONManager();
JSONObject jsonObject = jParser.getJSONFromUrl(this.URL);
return jsonObject;
}
Is there a way to return that JSONObject outside of the class so I can do something like:
JSONObject js = apiCaller.execute();
Or any other solution to avoid creating new asynctasks every time I need to call the API? With my current code I can't get it but I don't know what is missing? maybe returning it in onPostExecute?
Some time ago I've asked a similar question
One AsyncTask for multiple Activities
and the solution I found was answered in another question:
Common class for AsyncTask in Android?
Basically, what you need is an interface.
I've going to explain the basics, although you should check the original answer by #SirDarius.
You could create an interface like this:
interface AsyncTaskCompleteListener<T> {
public void onTaskComplete(T result);
}
And implements that interface in all classes you need to use the AsynTask, then, in your generic Asynstask you need to have a callback AsyncTaskCompleteListener and call it from your onPostExecute
class B implements AsyncTaskCompleteListener<JSONObject> {
public void onTaskComplete(JSONObject result) {
// do whatever you need
}
public void launchTask(String url) {
ApiCaller a = new ApiCaller(context, ArrayList<NameValuePair> params, this);
ApiCaller.execute(url);
}
}
class ApiCaller extends AsyncTask<String, Void, String> {
private AsyncTaskCompleteListener<String> callback;
public ApiCaller(Context context, ArrayList<NameValuePair> params, AsyncTaskCompleteListener<String> cb) {
this.context = context;
this.callback = cb;
}
protected void onPostExecute(String result) {
finalResult = result;
progressDialog.dismiss();
System.out.println("on Post execute called");
callback.onTaskComplete(result);
}
}
because the asynchronous AsyncTask you can not call a method and obtain the return value;
you could use a AsyncTask on your inline code and get on postExecute your JSON object:
new AsyncTask<String, String, JSONObject>(){
#Override
protected Void doInBackground(String... params) {
//...
}
#Override
protected void onPostExecute(JSONObject result) {
// ...
}
}.execute(...);
You should implement a listener that you will pass to the async task. When the asynctask finishes, it will call the listener's method:
First, make a listener in a new java file:
public interface onTaskDoneListener{
void onTaskDone(JSONObject result);
}
Pass it to your AsyncTask:
private onTaskDoneListner donelistner;
public ApiCaller(ArrayList<NameValuePair> params,onTaskDoneListener donelistener){
String apiURL = this.baseURL + this.apiVersion + "/?";
String paramsList = URLEncodedUtils.format(params, "utf-8");
this.URL = apiURL + paramsList;
this.donelistener = donelistener;
}
Then, inside the onPostExecute method of your AsyncTask
this.donelistener.onTaskDone(result)
Usage:
new ApiCaller(params,new onTaskDoneListener() {
#Override
public void onTaskDone(JSONObject result) {
//This will be called when the asynctask finishes
//Do something with the result
});
Step 1 : Define an Interface.
public interface WebServiceListener {
public void onLoginActionComplete (ArrayList<String> arrayList);
}
Step 2: Implement the WebServiceListener in your activity class.
public class LoginActivity extends Activity implements WebServiceListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login);
/* Calling the AsyncTask here,you could call in Login Button Click... */
WebServiceClient httpclient = new WebServiceClient(this);
httpclient.execute(ssoURLStr);
}
#Override
public void onLoginActionComplete (ArrayList<String> arrayList) {
// TODO Auto-generated method stub
/* Read the response */
String Response = arrayList.get(0);
}
}
Step 3 : Your AsynTask class code here.
public class WebServiceClient extends
AsyncTask<String, Integer, ArrayList<String>> {
WebServiceListener listener = null;
/* Constructor added WebServiceListener here */
public WebServiceClient ( WebServiceListener listener)
{
this.listener = listener;
}
#Override
protected ArrayList<String> doInBackground(String... params) {
// TODO Auto-generated method stub
ArrayList<String> arrayList = null;
" write your http code here and get the response and update the
arrayList <String> here"
return arrayList;
}
#Override
protected void onPostExecute(ArrayList<String> arrayList) {
// Returns the contents of the HTML page
listener.onLoginActionComplete (arrayList);
}
}
I have also asked this question. Maybe this link will help