7
\$\begingroup\$

I am using the Android library Retrofit for networking in my app. The library calls for creating a RestAdapter for making service calls. I want to use this same instance of the RestAdapter for all of my service calls. How is my setup for this scenario?

Extending Application:

public class CustomApplication extends Application {
    private RestClient restClient = null;

    public RestClient getRestClient() {
        return restClient;
    }

    public void initRestClient() {
        if (restClient == null) {
            restClient = new RestAdapter.Builder().setEndpoint(BASE_URL).build().create(RestClient.class);
        }
    }
}

Initializing instance and making sure I have access to the instance in all my Activities, without explicitly having to get it for each Activity:

public class BaseActivity extends Activity {
    protected RestClient restClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ((CustomApplication) this.getApplication()).initRestClient();
        restClient = ((CustomApplication) this.getApplication()).getRestClient();
    }
}

Then all my Activities will extend BaseActivity.

\$\endgroup\$
5
\$\begingroup\$

This is a common problem, and a common solution is dependency injection. From Wikipedia:

Dependency injection is a software design pattern in which one or more dependencies (or services) are injected, or passed by reference, into a dependent object (or client) and are made part of the client's state. The pattern separates the creation of a client's dependencies from its own behavior, which allows program designs to be loosely coupled and to follow the dependency inversion and single responsibility principles.

One dependency framework for Android is Dagger:

Dependency injection isn't just for testing. It also makes it easy to create reusable, interchangeable modules. You can share the same AuthenticationModule across all of your apps. And you can run DevLoggingModule during development and ProdLoggingModule in production to get the right behavior in each situation.

Beside's @janos's great point about inheritance being for is-a relationships, you can imagine BaseActivity slowly getting cluttered with methods and fields that some activities need, and other don't. Eventually it all becomes a mess.

With dependency injection, an activity's reliance upon a RestAdapter can be made clear:

class SomeActivity extends Activity {
  private final RestAdapter restAdapter;

  @Inject
  public SomeActivity(final RestAdapter restAdapter) {
    this.restAdapter = restAdapter;
  }

  ...
}

And now you can unit test SomeActivity with other RestAdapters.

Making the RestAdapter a singleton requires only an attribute:

@Provides @Singleton RestAdapter provideRestAdapter() {
  return new RestAdapter.Builder()
      .setEndpoint(BASE_URL)
      .build()
      .create(RestClient.class);
}
\$\endgroup\$
  • \$\begingroup\$ Thanks for the dependency injection suggestion. I've spend the past few days researching DI/Dagger, and this is the solution I'm going with. \$\endgroup\$ – Adam Johns Oct 23 '14 at 3:28
6
\$\begingroup\$

I agree with both @mjolka and @kyle-falconer. In fact the two points are very similar, because dependency injection frameworks usually work by giving you singletons by default. Mjolka's suggestion of adopting Dagger will be good long-term solution and definitely worth the time investment. Kyle's suggestion is more of a direct, simple solution you can implement right now.

If you go with Kyle's approach, I would point out the modern way of implementing singletons in Java using an enum, that's simpler and more robust:

enum SingletonRestClient {
    INSTANCE;

    private final RestClient restClient;

    private SingletonRestClient() {
        restClient = new RestAdapter.Builder().setEndpoint(BASE_URL).build().create(RestClient.class);
    }

    public RestClient getRestClient() {
        return restClient;
    }
}

You could use this in an activity like this:

public class SomeActivity extends Activity {
    private RestClient restClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        restClient = SingletonRestClient.INSTANCE.getRestClient();
    }
}

Of course, you will have to do this in all your activities that need the rest client. But this is still better than forcing all your activities to extend a base activity.

Don't use inheritance just to share data. The right reason to use inheritance is when there is an is-a relationship. Can you say that SomeActivity is a BaseActivity? This sounds inevitably wrong, because the very nature of a "base activity" is that there is just one such activity. A good example would be an EmployeeActivity inheriting from a PersonActivity, which could make sense, since an employee really is a person.

We don't have more details about your context. If there is no is-a relationship between the classes involved, then don't use inheritance, and prefer composition instead: let each class that needs a rest client get it from the singleton.

\$\endgroup\$
4
\$\begingroup\$

The approach of extending Application is what I also have been recommending many times. It is recommended though to read this whole Stack Overflow question.

As for your current code, I have one warning: It could theoretically create two objects if called in a multithreaded environment (although I doubt this will happen)

As for your current code, I feel that both these lines are a bit overkill:

((CustomApplication) this.getApplication()).initRestClient();
restClient = ((CustomApplication) this.getApplication()).getRestClient();

You could change your getRestClient to:

public RestClient getRestClient() {
    if (restClient == null) {
        restClient = new RestAdapter.Builder().setEndpoint(BASE_URL).build().create(RestClient.class);
    }
    return restClient;
}

And then this line would be enough:

restClient = ((CustomApplication) this.getApplication()).getRestClient();

Although it would be better to just initialize the restClient once by overriding the Application.onCreate() method.

\$\endgroup\$
3
\$\begingroup\$

I don't think you need all other Activities to extend BaseActivity, though that is one way to do it. What you're looking to do is make your RestAdapter a singleton, which can be implemented by having some static singleton class for wrapping your RestAdapter.

Here's an example of what that might look like, which was adapted from Akylah's answer which referred to this gist of a sample singleton:

public class SingletonRestAdapter {
    private static RestAdapter mInstance = null;

    public static RestAdapter getInstance(){
        if(mInstance == null)
        {
            mInstance = new RestAdapter.Builder().setEndpoint(BASE_URL).build().create(RestClient.class);
        }
        return mInstance;
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.