2

I'm new to Android programming, and I'd like to create a central database service class which will take care of user data exchange with an external database. For this, I created a service which is started after successful login. I created another class that extends AsyncTask to do the data retrieval. Now, I wanted the methods for the data retrieval to be stored in the service. I would fire intents to the service from different activities, and with .setAction() I would determine which method to call, or which data to retrieve.

I also created an interface class for handling the AsyncTask results. Now, from this question I thought that it would be possible to have multiple listeners to one and the same AsyncTask result. But now this seems impossible to achieve: I'd like to retrieve the AsyncTask results in the MainMenuActivity, but I can't create an instance of AsyncUserData there as a delegate for the UserData class. In my example below, the missing piece is a valid instance of AsyncUserData for the UserData class to work with. How could I do it?

Here's the example: MainMenuActivity

public class MainMenuActivity extends ActionBarActivity implements AsyncUserData {
  TextView tvUsername;

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main_menu);
    tvUsername =
      (TextView) findViewById(R.id.tvUsername);
    TelephonyManager tManager = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
    String uid = tManager.getDeviceId();
    getDataFromUserSessionService(this, uid);
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main_menu, menu);
    return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();
    if (id == R.id.action_settings) {
      return true;
    }
    return super.onOptionsItemSelected(item);
  }

  @Override
  public void retrieveResult(String result) throws JSONException {
    JSONObject jsonObject = new JSONObject(result);
    String joName;
    joName = jsonObject.getJSONObject("name").toString();
    user.setName(joName);
    tvUsername.setText(joName);
  }

  public void getDataFromUserSessionService(Context context, String uid) {
    Intent intent = new Intent(context, UserSession.class);
    intent.setAction(UserSession.ACTION_FETCH_USER_DATA);
    intent.putExtra(UserSession.UID, uid);
    context.startService(intent);
  }

UserSession Service

public class UserSession extends IntentService {
  public static final String ACTION_FETCH_USER_DATA = "com.example.blahblah.services.action.read_user_data";

  @Override
  protected void onHandleIntent(Intent intent) {
    if (intent != null) {
      utils = new Utils(this);
      final String action = intent.getAction();
      uid = intent.getStringExtra(UID);
      if (ACTION_FETCH_USER_DATA.equals(action)) {
        handleUserDataFetch(uid);
      }
    }
  }

  private void handleUserDataFetch(String uid) {
    String[] parameters = new String[2];
    parameters[0] = uid;
    parameters[1] = Constants.USER_DATA_FETCH;
    UserData userData = new UserData(this);
    userData.execute(parameters);
  }

UserData AsyncTask Class (the Utils class just has another post method):

public class UserData extends AsyncTask < String, Void, String > {
  public AsyncUserData delegate = null;
  private Context myContext;

  public UserData(Context context) {
    myContext = context;
  }

  @Override
  protected String doInBackground(String...params) {
    String serverResponse = "";
    String uid = params[0];
    Utils utils = new Utils(myContext);
    String phpName = params[1];
    List < NameValuePair > nameValuePairs = new ArrayList < NameValuePair > ();
    nameValuePairs.add(new BasicNameValuePair("uid", uid));

    try {

      serverResponse = utils.passDataToServer(phpName, nameValuePairs);
    } catch (IOException e) {
      e.printStackTrace();
    }
    return serverResponse;
  }

  protected void onPostExecute(String result) {
    try {
      delegate.retrieveResult(result);
    } catch (JSONException e) {
      e.printStackTrace();
    }
  }

};

And the AsyncUserData interface:

public interface AsyncUserData {
  void retrieveResult(String result) throws JSONException;
}
Community
  • 1
  • 1
cirko
  • 211
  • 2
  • 13
  • offtopic but why do you have a line break between `@` and `Override`? – Epicblood Jun 02 '15 at 23:09
  • sorry, that was the "tidy" function in code insertion here, did I use that wrong? – cirko Jun 02 '15 at 23:09
  • you used the code snippet tags, those are meant for javascript, html etc. – Epicblood Jun 02 '15 at 23:12
  • I'm not sure why you are using both an `IntentService` and an `AsyncTask` you can use the `IntentService` directly to run asynchronous tasks. Take a look at the answer to [this question](http://stackoverflow.com/questions/9088315/start-intentservice-from-activity-and-refresh-activity-when-intentservice-is-fin) – Titus Jun 03 '15 at 00:31
  • My intention was to have a good structure in the code by having all necessary merhods in the service and by putting the asynctasks in their own respective classes. But then again, I'm not experienced at all, and there may be better solutions, so I'll be glad to hear any different ideas. – cirko Jun 03 '15 at 10:11

2 Answers2

2

You can use a Singleton that stores a reference to the activity

    public class ServiceToActivity
    {

                public ActionBarActivity mainactivity = null;
                private static ServiceToActivity singleton = null;
                public Class<?> cl = null;

                private ServiceToActivity()
                {

                }

                public static ActionBarActivity getSingleton()
                {
                    if(singleton==null)
                           return null;
                    return singleton.mainactivity;
                }

               public static Class<?> getSingletonClass()
                {
                    if(singleton==null)
                           return null;
                    return singleton.cl;
                }

                public static void setSingleton(ActionBarActivity mainactivity, Class<?> cl)
                {
                      if(singleton==null)
                             singleton = new ServiceToActivity();
                      singleton.mainactivity = mainactivity;
                      singleton.cl = cl;
                }
    }

Then create the singleton before the service is started

  public void getDataFromUserSessionService(Context context, String uid) {
Intent intent = new Intent(context, UserSession.class);
intent.setAction(UserSession.ACTION_FETCH_USER_DATA);
intent.putExtra(UserSession.UID, uid);
ServiceToActivity.setSingleton(this,this.getClass());   //create Singleton to store a reference to the activity
context.startService(intent);
}

In UserData retrieve data to the main activity by:

 protected void onPostExecute(String result) {
try {
  Class<?> cl = ServiceToActivity.getSingletonClass();
  Method met = cl.getMethod("retrieveResult", String); //String because result is of type String: you can use result.getClass() instead
  met.invoke(cl.cast(ServiceToActivity.getSingleton()), result); // compare it to this ServiceToActivity.getSingleton().retrieveResult(result);
} catch (JSONException e) {
  e.printStackTrace();
}
}
  • great answer, works like a charm. never had heard of singletons before. just for later development: now i have set the singleton to have the mainmenuactivity as param. when i change activities, i'll set the singleton to that and the results will get there. and - i don't need the delegate anymore. correct? – cirko Jun 04 '15 at 21:49
  • @cirko yes that's correct. You no longer need a delegate. Can you upvote and vote it as an answer since it solves the question. Thank you. –  Jun 05 '15 at 10:31
  • In the ServiceToActivity, how do I define more than one possible return classes in the `getSingleton()` method? I can set another class with another `setSingleton()`method, but I can only define one getter method under the same name. Should I return an "ActionBarActivity" object instead, as all my classes extend it? Or is there a way to return just the class which is set in `setSingleton()`? – cirko Jun 09 '15 at 13:46
  • @cirko you cannot use ActionBarActivity since it doesn't have a method called retrieveResult() –  Jun 09 '15 at 18:31
1

It sounds like you might want to use an event bus such as otto

Tim Mutton
  • 756
  • 8
  • 17