繁体   English   中英

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

[英]How do I abstract away dependencies in Android library code?

这是我的情况。

我有一个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活动实际上并未使用构造函数实例化。

我已经提出了几种解决方案,但是它们在某些方面都很棘手:

棘手的解决方案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);
}

这很棘手,因为(a)数据源是静态的且实际上未与实例关联,并且(b)使用者可以通过标准活动API而不是此静态方法来启动活动,这将导致NullPointerException。

棘手的解决方案2

我可以创建一个提供者类,该类提供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;
    }
}

这似乎不那么棘手,但仍然有点棘手,因为该活动的依赖性并不明显,并且由于可能有许多启动它的路径,因此其中一些人很可能会忘记调用TimeDataSourceProvider.initialize()

棘手的解决方案3

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

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更好,因为初始化失败会更容易被偷偷摸摸,但是它还会在原本不需要的数据类型之间建立相互依赖性。

...以及该主题的其他引人注目的变化。

使这些解决方案崩溃的常见主题:

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

Nooby Android开发人员该做什么?

只要这些依赖关系正确实现了Parcelable,您就应该能够将它们添加到您的意图中,然后将它们作为ITimeDataServer取消打包并获取正确的类。

我发现了一个很好的解决方案在这里 ,在最喜爱的答案。

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

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!
    }
}

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

这使您可以抽象并注入各种疯狂的行为!

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM