I'm making an AsyncTask extension that communicates with a service using different actions and returns different types of results depending on the action used. Each action has a listener, made of interfaces extending one interface called OnCommunicationFailedListener. The thing is, I want to have only one variable to save the listener, OnCommunicationFailedListener type, and make the user be able to save all "sub-interfaces" in that variable. They can save them just as is, but I cannot call the methods that the "sub-interface" introduces.
class CommunicationTask extends AsyncTask<Void, Void, String> {
private Action action;
private OnCommunicationFailedListener listener;
enum Action {
LOGIN,
LOGOUT,
SEARCH
}
interface OnCommunicationFailedListener {
void onCommunicationFailed();
}
interface OnLoginAchievedListener extends OnCommunicationFailedListener {
void onLoggedIn(String result);
void onLoginError(String error);
}
interface OnLogoutAchievedListener extends OnCommunicationFailedListener {
void onLoggedOut(String result);
void onLogoutError(String error);
}
interface OnSearchAchievedListener extends OnCommunicationFailedListener {
void onSearchAchieved(String[] results);
}
#Override protected String doInBackground(Void... voids){
try {
// Try to communicate with a query, resulting in some result.
return result;
} catch(IOException e) {
e.printStackTrace();
}
return null;
}
#Override protected void onPostExecute(String result){
super.onPostExecute(result);
if(result == null) {
listener.onCommunicationFailed();
return;
}
// ALL OF THESE GIVE ERRORS, SINCE THE METHODS DO NOT EXIST ANYMORE
switch(action){
case LOGIN:
if(<check if result is ok>){
listener.onLoggedIn(result);
} else listener.onLoginError(<some error>);
break;
case LOGOUT:
if(<check if result is ok>){
listener.onLoggedOut(result);
} else listener.onLogoutError(<some error>);
break;
case SEARCH:
String[] results = <process result>;
listener.onSearchAchieved(results);
break;
}
}
void login(String user, String password, OnLoginAchievedListener listener){
this.listener = listener;
action = Action.LOGIN;
//Build the query...
execute();
}
void logout(OnLogoutAchievedListener listener){
this.listener = listener;
action = Action.LOGOUT;
//Build the query...
execute();
}
void search(String query, OnSearchAchievedListener listener){
this.listener = listener;
action = Action.SEARCH;
//Build the query...
execute();
}
}
The idea is that another class calls the methods login(), logout() or search() and gives them a listener of the action's type. When the communication is finalized and the result is acquired, the action's listener methods are called (in my code, this happens in the switch statement).
The thing is, these methods cannot be called since the passed listener has been casted to OnCommunicationFailedListener.
Is there any way to save a subclass in a variable of its superclass type without being casted to it? I don't want to have actual variables for every type of listener.
Related
In the MessageFeedActivity onCreate method it load feeds by calling getMessageTypes method of CTFeedAPI class.
public class MessageFeedActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Setting the listener
CTFeedAPI ctFeedAPI = new CTFeedAPI(new CTFeedAPI.CTFeedAPIListener() {
#Override
public void feedAPISuccessListener(Object object) {
// Handle Success
}
#Override
public void feedAPIErrorListener(int error) {
// Handle Error
}
});
ctFeedAPI.getMessageTypes();
}
#Override
protected void onDestroy() {
super.onDestroy();
}
}
and wait for CTFeedAPIListener response. And CTFeedAPI class make network request by calling performRequest method of NetworkRequest class as
public class CTFeedAPI implements NetworkListener {
private CTFeedAPIListener apiListener;
public CTFeedAPI(CTFeedAPIListener feedAPIListener) {
apiListener = feedAPIListener;
}
public void getMessageTypes() {
Map < String, String > params = new HashMap < > ();
params.put("f", "GetMessageTypes");
NetworkRequest networkRequest = new NetworkRequest(this);
networkRequest.performRequest();
}
public interface CTFeedAPIListener {
void feedAPISuccessListener(Object object);
void feedAPIErrorListener(int error);
}
}
and wait for NetworkListener response
public class NetworkRequest {
private NetworkListener mListener;
public interface NetworkListener {
void networkReqSuccessListener(String cacheKey, String tag, String response);
void networkReqErrorListener(String tag, int error);
}
public NetworkRequest(NetworkListener listener) {
this.mListener = listener;
}
public void performRequest(
// Perform Network Requests and respond as
if (mListener != null) {
if (success) {
mListener.networkReqSuccessListener(getUrl(), getTag(), response);
} else {
mListener.networkReqErrorListener(getTag(), err_msg);
}
}
}
When users press back key, before destroy the MessageFeedActivity, the system call 'onDestroy' method. And Unfortunately, because the background thread (performRequest method in NetworkRequest class) is still keep a reference to it, leak occurs.
So how to implement CTFeedAPIListener reference in MessageFeedActivity to remove leak.
In this design not only you will leak memory but also your code would be highly coupled and very hard to test; prone to bugs that are hard to detect. I would suggest you implement MVP or similar architecture. Your activity should never know anything about your network layer. Add a presenter layer that is responsible to request something on behalf of your activity and use interface to update your activity. Your presenter should access a business entity that is mapped from the response of repository layer, that is responsible for network or Db access and return values to the client presenter. This way your presenter and business logic layers would be decoupled and easy to test independently. In the future if business requirements change, your changes don't affect other layers. Please see this article for more information on the subject.
Weak reference objects, which do not prevent their referents from
being made finalizable, finalized, and then reclaimed. Weak references
are most often used to implement canonicalizing mappings.
Suppose that the garbage collector determines at a certain point in
time that an object is weakly reachable. At that time it will
atomically clear all weak references to that object and all weak
references to any other weakly-reachable objects from which that
object is reachable through a chain of strong and soft references. At
the same time it will declare all of the formerly weakly-reachable
objects to be finalizable. At the same time or at some later time it
will enqueue those newly-cleared weak references that are registered
with reference queues.
You can use Weak Reference:
import java.lang.ref.WeakReference;
public class NetworkRequest {
public interface NetworkListener {
void networkReqSuccessListener(String cacheKey, String tag, String response);
void networkReqErrorListener(String tag, int error);
}
private WeakReference<NetworkListener> mListener;
public NetworkRequest(NetworkListener listener) {
this.mListener = new WeakReference<NetworkListener>(listener);
}
public void performRequest(){
// Perform Network Requests and respond as
NetworkListener listener = mListener.get();
if (listener != null) {
if (success) listener.networkReqSuccessListener(getUrl(), getTag(), response);
else listener.networkReqErrorListener(getTag(), err_msg);
}
}
}
public class CTFeedAPI implements NetworkListener {
private WeakReference<CTFeedAPIListener> apiListener;
public CTFeedAPI(CTFeedAPIListener feedAPIListener) {
apiListener = new WeakReference<>(feedAPIListener);
}
public void getMessageTypes() {
Map < String, String > params = new HashMap < > ();
params.put("f", "GetMessageTypes");
NetworkRequest networkRequest = new NetworkRequest(this);
networkRequest.performRequest();
}
public interface CTFeedAPIListener {
void feedAPISuccessListener(Object object);
void feedAPIErrorListener(int error);
}
}
save CTFeedAPI and CTFeedAPIListener as instance variable of MessageFeedActivity to prevent GC collecting them when activity is presented:
public class MessageFeedActivity extends AppCompatActivity {
private CTFeedAPI ctFeedAPI = null;// keeping a reference to CTFeedAPI
private CTFeedAPIListener listener = null;// keeping a reference to listener
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Setting the listener
listener = new CTFeedAPI.CTFeedAPIListener() {
#Override
public void feedAPISuccessListener(Object object) {
// Handle Success
}
#Override
public void feedAPIErrorListener(int error) {
// Handle Error
}
});
ctFeedAPI = new CTFeedAPI(listener);
ctFeedAPI.getMessageTypes();
}
I know there are similar questions but it still doesn't answer my question in the manner I need for my current situation.
I have three activity presenters that each need to call a certain data remotely which will therefore call the activity presenter back when data arrives. To create this data listener I created an interface listener and since all three Presenters ask for the same data and retrieve it, all three presenters implement this interface listener.
Interface listener:
interface ListenerInterface {
onGotData();
}
Presenter one:
class PresenterOne implements ListenerInterface{
public void getData() {
DataManager dataManager = new DataManager();
dataManager.getData(this);
}
#Override
public void onGotData(Data data) {
//Do something with data
}
}
Presenter two very similar to presenter one:
class PresenterTwo implements ListenerInterface{
public void getData() {
DataManager dataManager = new DataManager();
dataManager.getData(this);
}
#Override
public void onGotData(Data data) {
//Do something with data
}
}
Assume Presenter three is exactly the same as the previous. The data manager class:
class DataManager {
public void getData(final ListenerInterface listener) {
//Gets data
addOnCompleteListener(new OnCompleteListener<Data data > () {
#Override
public void onComplete (#NonNull DataCall < Data > dataCall) {
listener.onGotData(dataCall.getResults());
}
});
}
}
Would doing so someone call all three presenters since the interface is the one doing the calling or only call the presenter that is passed? Is there anything I should worry about if I followed way? If anyone who knows the Android framework well could provide a detailed answer so I could learn from it more that would be great.
The reason I want to do this is I want to communicate through interfaces between classes.
Sorry if this question is simple for some people but I am still learning.
Thank you very much in advance.
you can use RxBus implementation to make global event (e.g. your onGotData).
First you have to create RxBus class.
public class RxBus {
private static RxBus instance;
private PublishSubject<Event> subject = PublishSubject.create();
public static synchronized RxBus getInstance() {
if (instance == null) {
instance = new RxBus();
}
return instance;
}
private RxBus(){}
public void postEvent(Event event){
subject.onNext(event);
}
public Observable<Event> getEvents(){
return subject;
}
}
And now, you should subscribe to it in BaseActivity or something like this (depends or your project structure).
private RxBus rxbus;
private Subscription rxBusSubscription;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rxBus = RxBus.getInstance();
}
#Override
protected void onResume() {
super.onResume();
if (shouldSubscribeRxBus()) {
rxBusSubscription = rxBus.getEvents()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(event -> {
if (event.getEventType() == Event.EventType.DATA_RECEIVED) {
onGotData(data);
}
});
}
}
Now implement you onGotData as you want.
When you catch data received call:
class DataManager {
public void getData(final ListenerInterface listener) {
//Gets data
addOnCompleteListener(new OnCompleteListener<Data data > () {
#Override
public void onComplete (#NonNull DataCall < Data > dataCall) {
RxBus.getInstance().postEvent(new GotDataEvent(dataCall.getResults()));
}
});
}
}
You can create your Event classes structure as you want.
I would like to send and email on the background of my app.
I followed this example and implemented it
http://javapapers.com/android/android-email-app-with-gmail-smtp-using-javamail/
public class SendMailTask extends AsyncTask {
private ProgressDialog statusDialog;
private Activity sendMailActivity;
public SendMailTask(Activity activity) {
sendMailActivity = activity;
}
protected void onPreExecute() {
statusDialog = new ProgressDialog(sendMailActivity);
statusDialog.setMessage("Getting ready...");
statusDialog.setIndeterminate(false);
statusDialog.setCancelable(false);
statusDialog.show();
}
#Override
protected Object doInBackground(Object... args) {
try {
Log.i("SendMailTask", "About to instantiate GMail...");
publishProgress("Processing input....");
GMail androidEmail = new GMail(args[0].toString(),
args[1].toString(), (List) args[2], args[3].toString(),
args[4].toString());
publishProgress("Preparing mail message....");
androidEmail.createEmailMessage();
publishProgress("Sending email....");
androidEmail.sendEmail();
publishProgress("Email Sent.");
Log.i("SendMailTask", "Mail Sent.");
} catch (Exception e) {
publishProgress(e.getMessage());
Log.e("SendMailTask", e.getMessage(), e);
}
return null;
}
#Override
public void onProgressUpdate(Object... values) {
statusDialog.setMessage(values[0].toString());
}
#Override
public void onPostExecute(Object result) {
statusDialog.dismiss();
}
}
The code is working fine
However, i have a presentation and i need to explain the code.
In the code, SendMailTask extends AsyncTask without any extra parameters not even Void
I m stuck in this point because i searched and no one is using this way.
Can anyone explain it to me?
without the parameters, AsyncTask will assume the default class (which is Object)
public class SendMailTask extends AsyncTask <Object1, Object2, Object3> {
#Override
protected Object doInBackground(Object1... args) {
...
//publishProgress calls "onPublishProgress" method
//e.g. publishProgress("email sent.");
publishProgress(object2);
//last line returns to "onPostExecute" method
//e.g. return null;
return object3;
}
#Override
public void onProgressUpdate(Object2... values) {
...
}
#Override
public void onPostExecute(Object3 result) {
...
}
}
Object1 is the array of parameters you pass in when initializing the asyncTask
new SendMailTask(SendMailActivity.this).execute(fromEmail,
fromPassword, toEmailList, emailSubject, emailBody);
so fromEmail, fromPassword, etc, etc all goes into the array of Object1.
you access them in doInBackground method using arg[ index ].
If you not specify any Generic parameter , You will see it will take Object as a type (Base Class for all Classes)
So in case of using only AsyncTask
You are actually dealing with Object
eg- See the parameter of doInBackground() in the given link
Honestly, the way they've extended AsyncTask isn't terribly smart. It causes a number of annoying warnings in Android Studio, and it would have been just as easy to extend AsyncTask<Object, Object, Object>, it's the same effect, but it removes the warnings.
All of that said, in order to use the class, you can simply call the following:
Object[] args = new Object[]{
arg1,
arg2,
etc
};
new SendMailTask(activity).execute(args);
You just need to check the GMail constructor to see how you should order your args.
i've got something blowing my mind all day long.
The question is, I have an AsyncTask that returns me an User Object, with all its attributes. I know that I have to return from the doInBackground method and receive in the OnPostExecute to work with my data. The fact is that i want to extract that user out of the AsyncTask method because i have to work with it in my main thread.
My AsyncTask class is placed in the MainActivity.class.
i've heard about using interfaces to get my value back but i can't understand the way to do it.
public class FetchUserDataAsyncTask extends AsyncTask<Void, Void, User> {
RequestHandler rh = new RequestHandler(); //this is the class i use to do de server conection
User user;
User ret_user;
public FetchUserDataAsyncTask(User user){
this.user = user;
}
#Override
protected void onPostExecute(User user) {
super.onPostExecute(user);
//I WANT THIS USER IN MY MAIN THREAD, TO WORK WITH ITS ATTRIBUTES
}
#Override
protected User doInBackground(Void... params) {
try {
HashMap<String, String> dataToSend = new HashMap<>();
dataToSend.put("username", user.username);
dataToSend.put("password", user.password);
ret_user = rh.sendGetRequest("myadresstophp.php", dataToSend);
} catch (Exception e) {
e.printStackTrace();
}
return ret_user;
}
and the call (when user press the log in button), a few lines above.
new FetchUserDataAsyncTask(userk).execute();
I was hoping to do something like that: (i know its not the way to do it)
User user = new FetchUserDataAsyncTask(userk).execute();
Thank you all, have a nice day!
At first declare an Interface in your project somewhere having the required functions, then implement that interface in the (AsyncTask)calling class ,then declare one Interface object in the AsyncTask. Create the constructor of AsyncTask as follows:
public FetchUserDataAsyncTask(User user,InterfaceClass object){
this.user = user;
this.interfaceObject=object;
}
And then do the following in onPostExecute:
#Override
protected void onPostExecute(User user) {
super.onPostExecute(user);
interfaceObject.function(user); //call the function of the calling class
}
You can create an interface, pass it toAsyncTask (in constructor), and then call method in onPostExecute()
For example:
Your interface:
public interface OnTaskCompleted{
void onTaskCompleted();
}
Your Activity:
public class YourActivity implements OnTaskCompleted{
// your Activity
}
And your AsyncTask:
public class YourTask extends AsyncTask<Object,Object,Object>{
//change Object to required type
private OnTaskCompleted listener;
public YourTask(OnTaskCompleted
listener){
this.listener=listener;
} // required methods
protected void onPostExecute(Object
o){
// your stuff
listener.onTaskCompleted();
}
}
Here you go, this is in general how it would work. This is based on this answer and modified to be specific to your existing code. Basically assign your member variable based on what the user entered, and compare that value to the one you get from the server:
public class MyClass extends Activity {
//member variable for the username the user enters:
User userEnteredUser;
public class FetchUserDataAsyncTask extends AsyncTask<Void, Void, User> {
RequestHandler rh = new RequestHandler(); //this is the class i use to do de server conection
User user;
User ret_user;
public FetchUserDataAsyncTask(User user){
this.user = user;
}
#Override
protected void onPostExecute(User user) {
super.onPostExecute(user);
//I WANT THIS USER IN MY MAIN THREAD, TO WORK WITH ITS ATTRIBUTES
processValue(user); //added
}
#Override
protected User doInBackground(Void... params) {
try {
HashMap<String, String> dataToSend = new HashMap<>();
dataToSend.put("username", user.username);
dataToSend.put("password", user.password);
ret_user = rh.sendGetRequest("myadresstophp.php", dataToSend);
} catch (Exception e) {
e.printStackTrace();
}
return ret_user;
}
}
private void getValue()
{
EditText et = (EditText) findViewById(R.id.username);
userEnteredUser.username = et.getText().toString(); //something like this.... replace with your syntax
new FetchUserDataAsyncTask(userEnteredUser).execute();
}
private void processValue(User userFromServer)
{
if (userEnteredUser.equals(userFromServer)) {
//Users match! Yay!
}
}
}
onPostExecute runs on the UI thread by default (main thread). Do what ever you need to be do on the main thread in onPostExecute
I suggest you read up more from the link provided below for a better understanding on AsyncTask
http://developer.android.com/reference/android/os/AsyncTask.html
I'm uncertain what you mean by "main thread". If you mean "UI Thread", which is the primary thread used to execute in your app, then Shashank's reply is correct.
However, I suspect from the context of the question that you actually mean that you want the results returned to the code that initiated the AsyncTask. Let's call that the "invoking object" In that case, what I would do is define a callback in your invoking object whose only purpose is to receive the result of this AsyncTank. You could call it onUserDataFetched(User user).
So, to do this using Interfaces, you could define an Interface that contains a single method:
public interface FetchUserDataListener {
public void onUserDataFetched(User user);
}
Then make sure that your InvokingObject implements that interface, and pass your InvokingObject (which implements FetchUserData, and thus can be considered that object type) to your AsyncTask when you instantiate it.
So, you invoking object code would look like this:
public class InvokingObject implements FetchUserData {
public void someMethodToInvokeFetchUserData() {
//....
new FetchUserDataAsyncTask(userk, this).execute();
//....
}
#Override
public void onUserDataFetched(User user) {
//Compare the result to the data in your invoking object
}
}
Then pass that callback to your AsyncTask when you construct it:
And your AsyncTask Code would look like this:
public class FetchUserDataAsyncTask extends AsyncTask<Void, Void, User> {
FetchUserDataListener mFetchUserDataListener;
public FetchUserDataAsyncTask(User user, FetchUserDataListener listner){
this.user = user;
mFetchUserDataListener = listener
}
//...
#Override
protected void onPostExecute(User user) {
super.onPostExecute(user);
listener.onUserDataFetched(user)
}
//...
}
I am trying to unzip a folder using Android's AsyncTask. The class (called Decompress) is an inner class of Unzip where Unzip itself is a non-Activity class. The pseudo-code is:
public class Unzip {
private String index;
private String unzipDest; //destination file for storing folder.
private Activity activity;
private boolean result; //result of decompress.
public void unzip(String loc) {
Decompress workThread = new Decompress(loc, activity);
workThread.execute();
if(unzip operation was successful) {
display(index);
}
//Class Decompress:
class Decompress extends AsyncTask<Void, Integer, Boolean> {
private ProgressDialog pd = null;
private Context mContext;
private String loc;
private int nEntries;
private int entriesUnzipped;
public Decompress(String location, Context c) {
loc = location;
mContext = c;
nEntries = 0;
entriesUnzipped = 0;
Log.v(this.toString(), "Exiting decompress constructor.");
}
#Override
protected void onPreExecute() {
Log.v(this.toString(), "Inside onPreExecute.");
pd = new ProgressDialog(mContext);
pd.setTitle("Unzipping folder.");
pd.setMessage("Unzip in progress.");
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
Log.v(this.toString(), "Showing dialog and exiting.");
pd.show();
}
#Override
protected Boolean doInBackground(Void... params) {
//unzip operation goes here.
unzipDest = something; //unzip destination is set here.
if(unzip operation is successful) {
result = true;
index = url pointing to location of unzipped folder.
} else {
result = false;
}
}
#Override
protected void onPostExecute(Boolean result) {
if(result) {
if(pd != null) {
pd.setTitle("Success");
pd.setMessage("folder is now ready for use.");
pd.show();
pd.dismiss();
pd = null;
Log.v(this.toString(), "Unzipped.");
index = unzipDest + "/someURL";
Log.v(this.toString(), "index present in: " + index);
}
} else {
pd = ProgressDialog.show(mContext, "Failure", "Cannot unzip.");
pd.dismiss();
}
}
}
Problems I am facing:
1. The value of unzipDest and index, updated in doInBackground, remain null to Unzip and all its objects. How can I ensure that the values remain updated?
2. I know that doInBackground occurs in a thread separate from the main UI thread. Does that mean that any values updated in the new thread will be lost once that thread returns?
How can I ensure that the values remain updated?
They will be updated since they are member variables. However, since AsyncTask is asynchrounous, they might not be updated yet when you check them. You can use an interface to create a callback when these values are updated. This SO answer covers how to do this
Does that mean that any values updated in the new thread will be lost once that thread returns?
No they shouldn't be "lost". They probably just haven't been changed in the AsyncTask when you check them.
Since this isn't your actual code I can't see when you are trying to access them but you can use the interface method or call the functions that need these values in onPostExecute(). You also can do a null check before trying to access them. It just depends on the functionality and flow that you need as to which is the best way. Hope that helps.
Edit
In the answer I linked to, you tell the Activity that you will use that interface and override its method(s) with implements AsyncResponse in your Activity declaration after creating the separate interface class
public class MainActivity implements AsyncResponse{
then, in your Activity still, you override the method you declared in that class (void processFinish(String output);)
#Override
void processFinish(String output){ // using same params as onPostExecute()
//this you will received result fired from async class of onPostExecute(result) method.
}
then this is called in onPostExecute() when the listener sees that it is done with delegate.processFinish(result); delegate is an instance of AsyncResponse (your interface class)
public class AasyncTask extends AsyncTask{
public AsyncResponse delegate=null;
#Override
protected void onPostExecute(String result) {
delegate.processFinish(result);
}
Interface example taken from linked answer above and adjusted/commented for clarity. So be sure to upvote that answer if it helps anyone.