eclipse 最佳实践:扩展或覆盖 Android 库项目类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9951610/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best practice: Extending or overriding an Android library project class
提问by mjama
We're using an Android Library Projectto share core classes and resources across different builds (targets) of our Android application. The Android projects for each specific target reference the Core library project(behind the scenes, Eclipse creates and references a jar from the referenced library project).
我们正在使用Android 库项目在我们的 Android 应用程序的不同构建(目标)之间共享核心类和资源。每个特定目标的 Android 项目都引用了 Core 库项目(在幕后,Eclipse 从被引用的库项目中创建并引用了一个 jar)。
Overriding resources such as images and XML layouts is easy. Resource files placed in the target project, such as the app icon or an XML layout, automatically override the core library's resources with the same name when the app is built. However, sometimes a class needs to be overridden to enable target-specific behavior. For example, the Amazon target preferences screen cannot contain a link to the Google Play app page, requiring a change in the Amazon project's preferences.xml and preferences Activity class.
覆盖图像和 XML 布局等资源很容易。放置在目标项目中的资源文件,例如应用程序图标或 XML 布局,在构建应用程序时会自动覆盖核心库的同名资源。但是,有时需要重写一个类以启用特定于目标的行为。例如,亚马逊目标偏好屏幕不能包含指向 Google Play 应用程序页面的链接,需要更改亚马逊项目的首选项.xml 和首选项 Activity 类。
The goal is to reduce the amount of duplicate code among target projects while removing as much target-specific code from the Core library as possible. We've come up with a couple of approaches to implement logic specific to different targets:
目标是减少目标项目之间的重复代码量,同时从 Core 库中删除尽可能多的特定于目标的代码。我们提出了几种方法来实现特定于不同目标的逻辑:
- Write the target-specific functions within Core library classes and use if/switch blocks to select behavior based on product SKU. This approach is not very modular and bloats the Core library codebase.
- Extend the particular Core class in a target project and override the base (Core) class functions as needed. Then keep a reference to the base-class object in the Core library and instantiate it with an extended class object (from How to override a class within an Android library project?)
- 在核心库类中编写特定于目标的函数,并使用 if/switch 块根据产品 SKU 选择行为。这种方法不是很模块化,并且会使核心库代码库膨胀。
- 扩展目标项目中的特定 Core 类并根据需要覆盖基 (Core) 类函数。然后保留对 Core 库中基类对象的引用,并使用扩展类对象对其进行实例化(来自How to override a classin an Android library project?)
Are there other strategies to override or extend an Android library project class? What are some of the best practices for sharing and extending common classes among Android app targets?
是否有其他策略可以覆盖或扩展 Android 库项目类?在 Android 应用程序目标之间共享和扩展公共类的最佳实践有哪些?
采纳答案by Borislav Gizdov
Library project is referenced as a raw project dependency (source-based mechanism), not as a compiled jar dependency (compiled-code based library mechanism).
库项目被引用为原始项目依赖项(基于源的机制),而不是作为编译的 jar 依赖项(基于编译代码的库机制)。
@yorkw this is not true for the latest versions of ADT Plugin for Eclipse http://developer.android.com/sdk/eclipse-adt.html
@yorkw 对于 Eclipse http://developer.android.com/sdk/eclipse-adt.html的 ADT 插件的最新版本,这不是真的
From version 17 Change log
从版本 17 更改日志
New build features Added feature to automatically setup JAR dependencies. Any .jar files in the /libs folder are added to the build configuration (similar to how the Ant build system works). Also, .jar files needed by library projects are also automatically added to projects that depend on those library projects. (more info)
新的构建功能 添加了自动设置 JAR 依赖项的功能。/libs 文件夹中的任何 .jar 文件都会添加到构建配置中(类似于 Ant 构建系统的工作方式)。此外,库项目所需的 .jar 文件也会自动添加到依赖于这些库项目的项目中。(更多信息)
More info http://tools.android.com/recent/dealingwithdependenciesinandroidprojects
更多信息http://tools.android.com/recent/dealingwithdependenciesinandroidprojects
Before that, update overwriting of the Activity from Library project was easy, just exclude the class. Now the library is included as jar file, and there is no way to exclude class file from jar dependency.
在此之前,更新覆盖 Activity from Library 项目很容易,只需排除该类即可。现在库是作为 jar 文件包含的,并且无法从 jar 依赖项中排除类文件。
EDIT:
编辑:
My solution to overwrete/extend Activity from library jar:
我从库 jar 覆盖/扩展 Activity 的解决方案:
I created a simple util class:
我创建了一个简单的 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);
}
}
In order to overwrite a library's "SampleActivity" class it a the project which depends on that library, create a new class with the name SampleActivityExtended in the project in the same package and add the new activity to your AndroidManifest.xml.
为了覆盖库的“SampleActivity”类,它是依赖于该库的项目,在同一个包中的项目中创建一个名为 SampleActivityExtended 的新类,并将新活动添加到您的 AndroidManifest.xml。
IMPORTANT: all intents referencing overwritten activities should be created through the util class in the following manner:
重要提示:所有引用覆盖活动的意图都应通过 util 类以下列方式创建:
Intent intent = ActivityUtil.createIntent(MainActivity.this, SampleActivity.class);
...
startActivity(intent);
回答by yorkw
behind the scenes, Eclipse creates and references a jar from the referenced library project.
在幕后,Eclipse 从引用的库项目中创建并引用一个 jar。
This is not quite accurate. Library project is referenced as a raw project dependency (source-based mechanism), not as a compiled jar dependency (compiled-code based library mechanism). Currently Android SDK does not support exporting a library project to a self-contained JAR file. The library project must always be compiled/built indirectly, by referencing the library in the dependent application and building that application. When build dependent project, the compiled source and raw resources that need to be filtered/merged from Library project are copied and properly included in the final apk file. Note that Android team had started revamping the whole Library Project design (move it from ource-based mechanism to compiled-code based library mechanism) since r14, as mentioned in this earlier blog post.
这并不十分准确。库项目被引用为原始项目依赖项(基于源的机制),而不是作为编译的 jar 依赖项(基于编译代码的库机制)。目前,Android SDK 不支持将库项目导出为独立的 JAR 文件。必须始终通过引用依赖应用程序中的库并构建该应用程序来间接编译/构建库项目。在构建依赖项目时,需要从库项目中过滤/合并的编译源和原始资源被复制并正确包含在最终的 apk 文件中。请注意,从 r14 开始,Android 团队已经开始改进整个库项目设计(将其从基于 ource 的机制移动到基于编译代码的库机制),如之前的博客文章中所述。
What are some of the best practices for sharing and extending common classes among Android app targets?
在 Android 应用程序目标之间共享和扩展公共类的最佳实践有哪些?
The solution given by Android is Library Project.
The solution given by Java is Inheritanceand Polymorphism.
Come together, the best practice IMO is the second option you mentioned in the question:
Android 给出的解决方案是Library Project。
Java给出的解决方案是继承和多态。
一起来,IMO的最佳实践是您在问题中提到的第二个选项:
2.Extend the particular Core class in a target project and override the base (Core) class functions as needed. Then keep a reference to the base-class object in the Core library and instantiate it with an extended class object (from Android library project - How to overwrite a class?)
2.扩展目标项目中的特定核心类,并根据需要覆盖基(核心)类功能。然后在 Core 库中保留对基类对象的引用,并使用扩展类对象对其进行实例化(来自 Android 库项目 - 如何覆盖类?)
From my personal experience, I always use Android Library Project (Sometimes with Regular Java Project, for implementing/building common-lib.jar that contains only POJO) manage common code, for instance SuperActivity or SuperService, and extends/implements proper classes/interfaces in the dependent project for Polymorphism.
根据我的个人经验,我总是使用 Android 库项目(有时使用常规 Java 项目,用于实现/构建仅包含 POJO 的 common-lib.jar)管理公共代码,例如 SuperActivity 或 SuperService,并扩展/实现适当的类/接口在多态性的依赖项目中。
回答by IvonLiu
Solution based on PoisoneR's solution and Turbo's solution.
基于 Poisoner 的解决方案和 Turbo 的解决方案的解决方案。
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;
}
}
The benefits of this is that
这样做的好处是
The extended class can be in the project's package, not the library's package. Thanks to Turbo for this part.
By taking a
String
as an argument instead of aClass
object, this method is able to be used even with ProGuard.getName()
is where the problem is with ProGuard, as that will return something like "a" instead of the name of the original class. So in the original solution instead of looking for ClassExtended it will look for aExtended instead, something which does not exist.
扩展类可以在项目的包中,而不是库的包中。感谢 Turbo 在这部分。
通过将 a
String
作为参数而不是Class
对象,该方法甚至可以与 ProGuard 一起使用。getName()
是 ProGuard 的问题所在,因为它会返回类似“a”的东西,而不是原始类的名称。因此,在原始解决方案中,不是寻找 ClassExtended,而是寻找 aExtended,而不是寻找不存在的东西。
回答by DiscDev
I was inspired by PoinsoneR's answer to create a Utility class to do the same thing for Fragments - override a fragment in an android Library. The steps are similar to his answer so I won't go into great detail, but here is the class:
我受到 PoinsoneR 的回答的启发,创建了一个 Utility 类来为 Fragments 做同样的事情 - 覆盖 android 库中的一个片段。步骤与他的回答相似,所以我不会详细介绍,但这是课程:
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;
}
}
Usage:
用法:
FragmentUtilities.getFragment(MySpecialFragment.class)
回答by Dori
You can also use an Activity factory if you need to provide extended activitys for differnt build variants and have your library deal with the abstract factory alone. This can be set in your build variants Application file.
如果您需要为不同的构建变体提供扩展活动并让您的库单独处理抽象工厂,您也可以使用活动工厂。这可以在您的构建变体应用程序文件中设置。
回答by Alex
Could you, please, clarify what is different in Kindle and regular Android? I think - they are the same. What you need is different resources for Kindle and other devices. Then use appropriate resource. For example I use 2 links to store:
请你澄清一下 Kindle 和普通 Android 有什么不同?我认为 - 他们是一样的。您需要的是 Kindle 和其他设备的不同资源。然后使用适当的资源。例如,我使用 2 个链接来存储:
<string name="appStore"><a href=http://market.android.com/details?id=com.puzzle.jigsaw>Android Market</a> or <a href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw>Amazon Appstore</a> <br>http://market.android.com/details?id=com.puzzle.jigsaw <br>href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw</string>
<string name="appStore_amazon"><a href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw>Amazon Appstore</a> <br>href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw</string>
and use appStore for all none Amazone product and appStore_amazon for Kindle.
并为所有无 Amazone 产品使用 appStore,为 Kindle 使用 appStore_amazon。
How to determine where are you on run time - that would be another question which was answered here many times.
如何确定您在运行时的位置 - 这将是另一个在这里多次回答的问题。
回答by Luminger
What about using a callback
approach here? (Okay, callback is a little bit misleading but I currently have no other word for it:
callback
在这里使用一种方法怎么样?(好吧,回调有点误导,但我目前没有其他说法:
You could declare an interface in every Activity which should/may be expanded by the user. This interface will have methods like List<Preference> getPreferences(Activity activity)
(pass whatever parameters you need here, I would use an Activity
or at least a Context
to be futureproof).
您可以在每个 Activity 中声明一个应该/可以由用户扩展的接口。此接口将具有类似的方法List<Preference> getPreferences(Activity activity)
(在此处传递您需要的任何参数,我将使用 anActivity
或至少使用 aContext
以备将来使用)。
This approach could give you what you want when I have understood it correctly. While I haven't done this before and don't know how other people handle this I would give it a try and see if it works.
当我正确理解它时,这种方法可以给你你想要的东西。虽然我以前没有这样做过,也不知道其他人如何处理这个问题,但我会尝试一下,看看它是否有效。