如何抽象出Android库代码中的依赖项?

时间:2016-03-29 20:58:01

标签: android android-activity dependency-injection abstraction

这是我的情景。

我有一个Android活动,我想在其中抽象我的I / O依赖项。依赖关系由此接口表示(为简洁起见而编辑):

public interface ITimeDataServer {
    TimeRecord[] get(int userID);
    void save(TimeRecord record);
}

我想要的是我的活动能够调用这些接口方法,并让实现由调用代码提供。 (相当标准,我认为)。

ITimeDataServer myServer;
int myUserID;

void loadRecords() {
    TimeRecord[] records = myServer.get(myUserID);
    // etc...
}

我的困难是,如何确保设置myServer

这似乎是一个常见问题,但我无法找到一个干净的解决方案。

我的第一个想法是myServer将通过构造函数传递,但Android活动并没有真正用构造函数实例化。

我已经提出了几种解决方案,但它们在某种程度上都很狡猾:

Icky Solution 1

创建一个静态方法来启动活动类,该活动类接受ITimeDataServer参数并将其存储在活动可以访问它的静态变量中:

private static ITimeDataSource theDataSource;
public static void launch(Activity currentActivity, ITimeDataSource dataSource) {
    theDataSource = dataSource;
    Intent intent = new Intent(currentActivity, MainActivity.class);
    currentActivity.startActivity(intent);
}

这很icky因为(a)数据源是静态的,实际上并不与实例相关联,并且(b)消费者可以通过标准活动API而不是静态方法启动活动,这将导致NullPointerException。 / p>

Icky Solution 2

我可以创建一个Provider类,它提供ITimeDataSource的单例实例,需要在使用之前由调用库初始化:

public class TimeDataSourceProvider {

    private static ITimeDataSource myDataSource = null;

    public void initialize(ITimeDataSource dataSource) {
        myDataSource = dataSource;
    }

    public ITimeDataSource get() {
        if (myDataSource == null)
            throw new NullPointerException("TimeDataSourceProvider.initialize() must be called before .get() can be used.");
        else
            return myDataSource;
    }
}

这似乎有点不那么狡猾,但它仍然有点icky因为活动的依赖性并不明显,并且由于可能有很多路径可以启动它,所以很有可能他们中的一些人会忘记拨打TimeDataSourceProvider.initialize()

Icky解决方案3

作为#2的变体,创建一个静态IODependencyProvider类,必须在app启动时使用所有依赖项进行初始化。

public class IODependencyProvider {

    static ITimeDataSource myTimeData;
    static IScheduleDataSource myScheduleData; // etc

    public static void initialize(ITimeDataSource timeData, IScheduleDataSource scheduleData /* etc */) {
        myTimeData = timeData;
        myScheduleData = scheduleData;
        //etc
    }

    public static ITimeDataSource getTimeData() {
        if (myTimeData == null)
            throw new NullPointerException("IODependencyProvider.initialize() must be called before the getX() methods can be used.");
        else
            return myTimeData;
    }

    // getScheduleData(), etc
}

这似乎优于#1和#2,因为初始化失败将更难以偷偷摸摸,但它也会在数据类型之间产生相互依赖性,否则这些数据类型就不存在了。

......以及该主题的其他icky变种。

使这些解决方案变得蹩脚的常见主题:

  • 需要使用静态字段将不可序列化的信息传递给活动
  • 缺乏执行这些静态字段初始化的能力(以及随后的偶然性)
  • 无法清楚地识别活动的依赖关系(由于依赖于静态)

什么是一个不起眼的Android开发人员?

2 个答案:

答案 0 :(得分:1)

只要这些依赖项正确实现了Parcelable,您就应该能够将它们添加到您的意图中,然后将它们解压缩为ITimeDataServer并获取正确的类。

答案 1 :(得分:0)

我在最不喜欢的答案中找到了一个很好的解决方案here

我将库活动定义为抽象,没有默认构造函数,但是接受一个接口的构造函数,如下所示:

public abstract class TimeActivity extends AppCompatActivity {

    private ITimeDataSource myTimeDataSource;
    public TimeActivity(@NonNull ITimeDataSource dataSource) {
        myTimeDataSource = dataSource;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_time);

        // do stuff with myTimeDataSource!
    }
}

然后,调用代码可以创建一个具体的子类,其中选择的实现 具有无参数构造函数。没有静态成员,轻松自如!

这可以让你抽象并注入各种疯狂的行为! Woooo!

(请注意,具体的子类活动需要手动添加到AndroidManifest.xml,就像所有活动一样,或者应用程序在尝试启动时会崩溃。)