Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot resolve method 'getAllUserGroupByName()' of different flavor android studio

Consider I am having two product flavor in my android project named 'local' & 'international'. I am just giving a skeleton of my situation, so kindly ignore other mistakes. Consider I have already different source Set for different flavor.

Now in my 'local' flavor I just need the one method as mentioned below.

// local flavor file
interface UserDao {
    List<User> getAllUser();
}

While in my international flavor I also need additional feature to get the user in a group by its name also. So having two methods.

// international flavor file
interface UserDao {
    List<User> getAllUser();
    List<User> getAllUserGroupByName();
}

But the problem is, I am having a single activity from where I am calling that method. When I am calling getAllUserGroupByName() method from activity it will compile if current flavor is 'international' but it will not compile on 'local' flavor as it does not contain that method.

I tried calling this method by wrapping in if condition like below,

// BuildUtils is my helper class to get current flavor check easily.
if (BuildUtils.isInternational()) {
      getAppDatabase().UserDao().getAllUserGroupByName();
}

But it's not working too.

I don't want the following solution.

  • Two activity for each flavors.
  • I already solved it by combining both interface in a single interface. (which is also valid as it does not contain any implementation).

Updated

After some research and answer given by @manouti

The issue is that Java is a statically typed language, so placing an if condition does not mean that the compiler will avoid checking the code with the getAllUserGroupByName invocation.

So is there any way that build system can ignore the particular code according to flavor for code compilation?

OR

any Android studio IDE tool or feature that will discard the particular code according to flavor for compilation.

like image 809
Moinkhan Avatar asked Dec 27 '17 06:12

Moinkhan


2 Answers

There are several ways to do it. Of course you can write two different Activity classes as the other answer mentioned, and place them in each flavor’s source set, and that would work perfectly fine. But I assume you don’t really want to do this because it would be an overkill to duplicate the code with possibly very little change between the two versions.

Here are two ways I can think of that allow you to keep only one Activity class (i.e. in the main source set):

  1. Use reflection to call the method getAllUserGroupByName if it is defined. The details of this approach are very well illustrated in this post. The issue is that Java is a statically typed language, so placing an if condition does not mean that the compiler will avoid checking the code with the getAllUserGroupByName invocation. With this solution, you can use reflection to check if the method is defined, and if so you can call it reflectively.

  2. Another solution which IMO is cleaner than using reflection code is to encapsulate the logic of retrieving user group information in its own class and in the same package, say UserGroupLoader, and define two versions of this class, one for each flavor. In the local flavor, the implementation would return a dummy result (e.g. an empty list of users) and in the international flavor it would use the actual code of loading the users:

    public class UserGroupInfoLoader {
        public List<User> getAllUserGroupByName() {
            // for international flavor, call UserDao.getAllUserGroupByName
            // for local flavor, return empty list (you may even use the Optional class as a return type instead of List<User>)
        }
    }
    

You can also control the scope of the logic to encapsulate in this class. For example, instead of just returning the user group list, you can have it return the combined result of both all users (first method in UserDao) and all users by group name (only applies to the implementation for international flavor). This makes the code more readable, because just having a method that returns dummy data for one flavor may seem to be counter-intuitive for readers.

like image 96
M A Avatar answered Nov 15 '22 17:11

M A


You could create interface containing only getAllUserGroupByName() method and make international version extend it.

With this you can do instanceof check on userDao.

Interface with getAllUserGroupByName() method (it should be put in common source set):

interface InternationalInterface {
    List<User> getAllUserGroupByName();
}

Make UserDao in international version extend it:

// international flavor file
interface UserDao extends InternationalInterface {
    List<User> getAllUser();
}

And in your activity check if UserDao is instance of InternationalInterface. If statement is going to be true only for the application built with international flavor. Now you dont even need to use BuildUtils anymore.

UserDao userDao = getAppDatabase().UserDao();
if (userDao instanceof InternationalInterface) {
    ((InternationalInterface)userDao).getAllUserGroupByName();
}
like image 36
Mateusz Avatar answered Nov 15 '22 17:11

Mateusz