最佳实践:扩展或覆盖Android库项目类

我们正在使用Android库项目来共享Android应用程序的不同构build(目标)中的核心类和资源。 针对每个特定目标的Android项目引用Core库项目 (在幕后,Eclipse创build并引用来自所引用的库项目的jar)。

覆盖图像和XML布局等资源很容易。 放置在目标项目中的资源文件(例如应用程序图标或XML布局)会在构build应用程序时自动覆盖具有相同名称的核心库资源。 然而,有时候需要重写一个类来实现特定于目标的行为。 例如,Amazon目标首选项屏幕不能包含指向Google Play应用程序页面的链接,需要更改Amazon项目的preferences.xml和首选项Activity类。

目标是减less目标项目中重复代码的数量,同时尽可能多地从Core库中删除特定于目标的代码。 我们想出了几种方法来实现特定于不同目标的逻辑:

  1. 在Core库类中编写特定于目标的函数,并使用if / switch块根据产品SKUselect行为。 这种方法不是非常模块化,而且会使Core库代码库膨胀。
  2. 在目标项目中扩展特定的Core类并根据需要覆盖基类(Core)类的function。 然后保持对Core库中的基类对象的引用,并用扩展类对象(从如何覆盖Android库项目中的类)实例化它?

是否有其他策略来重写或扩展Android库项目类? 什么是在Android应用程序目标之间共享和扩展公共类的最佳实践?

Solutions Collecting From Web of "最佳实践:扩展或覆盖Android库项目类"

库项目被引用为原始项目依赖项(基于源的机制),而不是编译的jar依赖项(基于编译代码的库机制)。

@yorkw这不是最新版本的ADT Eclipse插件http://developer.android.com/sdk/eclipse-adt.html

从版本17更改日志

新的构buildfunction添加了自动设置JAR依赖关系的function。 / libs文件夹中的任何.jar文件都被添加到构buildconfiguration中(类似于Ant构build系统的工作方式)。 此外,库项目所需的.jar文件也会自动添加到依赖于这些库项目的项目中。 (更多信息)

更多信息http://tools.android.com/recent/dealingwithdependenciesinandroidprojects

在此之前,更新活动从库项目的覆盖很容易,只是排除类。 现在库被包含为jar文件,并且无法从jar依赖项中排除类文件。

编辑:

我的解决schemeoverwrete /扩展库jar中的活动:

我创build了一个简单的util类:

public class ActivityUtil { private static Class getActivityClass(Class clazz) { // Check for extended activity String extClassName = clazz.getName() + "Extended"; try { Class extClass = Class.forName(extClassName); return extClass; } catch (ClassNotFoundException e) { e.printStackTrace(); // Extended class is not found return base return clazz; } } public static Intent createIntent(Context context, Class clazz) { Class activityClass = getActivityClass(clazz); return new Intent(context, activityClass); } } 

为了覆盖库的“SampleActivity”类,它是依赖于该库的项目,在同一个包中的项目中创build一个名为SampleActivityExtended的新类,并将新的活动添加到您的AndroidManifest.xml中。

重要提示:所有引用被覆盖的活动的意图都应通过util类以下列方式创build:

 Intent intent = ActivityUtil.createIntent(MainActivity.this, SampleActivity.class); ... startActivity(intent); 

Eclipse在后台创build并引用引用的库项目中的jar。

这不太准确。 库项目被引用为原始项目依赖项(基于源的机制),而不是编译的jar依赖项(基于编译代码的库机制)。 目前Android SDK不支持将库项目导出为自包含的JAR文件。 库项目必须始终通过引用依赖应用程序中的库和构build应用程序来间接编译/构build。 当依赖于构build项目时,需要从库项目中过滤/合并的已编译源代码和原始资源将被复制并正确包含在最终的apk文件中。 请注意,Android团队已经开始对r14以来的整个Library Projectdevise(从基于ource的机制转移到基于编译代码的库机制)进行修改 ,正如在此前的博客文章中提到的那样 。

什么是在Android应用程序目标之间共享和扩展公共类的最佳实践?

Android提供的解决scheme是Library Project 。
Java给出的解决scheme是inheritance和多态 。
走到一起,最好的做法海事组织是你在问题中提到的第二个选项:

2.在目标项目中扩展特定的Core类,并根据需要覆盖基类(Core)类的function。 然后保留对Core库中的基类对象的引用,并用扩展类对象(从Android库项目 – 如何覆盖类)实例化它?

从我个人的经验来看,我总是使用Android库项目(有时用普通Java项目来实现/构build仅包含POJO的common-lib.jar)来pipe理通用代码,例如SuperActivity或SuperService,并扩展/实现适当的类/接口在多态的依赖项目中。

基于PoisoneR解决scheme和Turbo解决scheme的解决scheme。

 public static Class<?> getExtendedClass(Context context, String clsName) { // Check for extended activity String pkgName = context.getPackageName(); Logger.log("pkgName", pkgName); String extClassName = pkgName + "." + clsName + "Extended"; Logger.log("extClassName", extClassName); try { Class<?> extClass = Class.forName(extClassName); return extClass; } catch (ClassNotFoundException e) { e.printStackTrace(); // Extended class is not found return base return null; } } 

这样做的好处是

  1. 扩展类可以在项目的包中,而不是在图书馆的包中。 感谢Turbo的这个部分。

  2. 通过将String作为参数而不是Class对象,即使使用ProGuard也可以使用此方法。 getName()是ProGuard出现问题的地方,因为它会返回类似于“a”的内容,而不是原始类的名称。 所以在原来的解决scheme,而不是寻找ClassExtended它会寻找一个扩展,而不是存在的东西。

那么在这里使用callback方法呢? (好吧,callback是有点误导,但我现在没有其他的话:

你可以在每个活动中声明一个接口,这个接口应该/可以由用户扩展。 这个接口将有像List<Preference> getPreferences(Activity activity) (在这里传递你需要的任何参数,我会使用一个Activity或至less一个Context来面向未来)的方法。

这种方法可以给你想要的东西,当我正确地理解它。 虽然我之前没有这样做过,也不知道别人怎么处理这个问题,我会试一下,看看它是否有效。

请问,请您澄清一下Kindle和普通Android有什么不同? 我认为 – 他们是一样的。 您需要的是Kindle和其他设备的不同资源。 然后使用适当的资源。 例如,我使用2个链接来存储:

 <string name="appStore">&lt;a href=http://market.android.com/details?id=com.puzzle.jigsaw>Android Market&lt;/a> or &lt;a href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw>Amazon Appstore&lt;/a> &lt;br>http://market.android.com/details?id=com.puzzle.jigsaw &lt;br>href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw</string> <string name="appStore_amazon">&lt;a href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw>Amazon Appstore&lt;/a> &lt;br>href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw</string> 

并使用appStore所有没有Amazone产品和appStore_amazon的Kindle。

如何确定你在哪里跑步 – 这将是另一个在这里被多次回答的问题。

我的灵感来自于PoinsoneR的答案,创build一个工具类来为碎片做同样的事情 – 覆盖android库中的一个片段。 这些步骤与他的答案类似,所以我不会详细说明,但这里是class级:

 package com.mysweetapp.utilities; import android.support.v4.app.Fragment; public class FragmentUtilities { private static Class getFragmentClass(Class clazz) { // Check for extended fragment String extClassName = clazz.getName() + "Extended"; try { Class extClass = Class.forName(extClassName); return extClass; } catch (ClassNotFoundException e) { e.printStackTrace(); // Extended class is not found return base return clazz; } } public static Fragment getFragment(Class clazz) { Class fragmentClass = getFragmentClass(clazz); Fragment toRet = null; try { toRet = (Fragment)fragmentClass.newInstance(); return toRet; } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } return toRet; } } 

用法:

 FragmentUtilities.getFragment(MySpecialFragment.class) 

如果您需要为不同的构build变体提供扩展的活动,并让您的库单独处理抽象工厂,则还可以使用活动工厂。 这可以在您的构build变体应用程序文件中设置。