[英]How to prevent client from seeing internal private classes in Android library ?
我有一个包含多个包的库-
让我们说
套餐一;
包 b;
包内 a 我有公共 a_class
在包 b 里面我有公共 b_class
a_class 使用 b_class。
我需要从中生成一个库,但我不希望客户端看到 b_class。
我所知道的唯一解决方案是将我精美易懂的包扁平化为单个包,并为 b_class 使用默认包访问。 还有另一种方法吗? 也许使用接口或某种形式的设计模式?
如果您拒绝将代码移动到单独的受控服务器,那么您所能做的就是在尝试使用您的 API 时阻碍客户端程序员。 让我们开始将良好实践应用到您的设计中:
对于您想要“隐藏”的每个类:
public interface MyInterface {...}
public class MyFactory { public MyInterface createObject(); }
到目前为止,您的包现在是松散耦合的,并且实现类现在是私有的(正如您已经说过的良好实践所宣扬的那样)。 尽管如此,它们仍然可以通过接口和工厂使用。
那么,如何避免“陌生人”客户端执行您的私有 API? 接下来是一个创造性的、有点复杂但有效的解决方案,基于阻碍客户端程序员:
修改您的工厂类:向每个工厂方法添加一个新参数:
public class MyFactory
{
public MyInterface createObject(Macguffin parameter);
}
那么,什么是Macguffin
? 这是您必须在应用程序中定义的新接口,至少有一个方法:
public interface Macguffin
{
public String dummyMethod();
}
但不提供此接口的任何可用实现。 在您需要提供Macguffin
对象的代码的每个地方,通过匿名类创建它:
MyFactory.getObject(new Macguffin(){
public String dummyMethod(){
return "x";
}
});
或者,更高级的是,通过动态代理对象,因此即使客户端程序员敢于反编译代码,也不会找到此实现的“.class”文件。
你从中得到什么? 基本上是劝阻程序员不要使用需要未知、未记录、无法理解的对象的工厂。 工厂类应该只关心不接收空对象,并调用虚拟方法并检查返回值它也不为空(或者,如果您想要更高的安全级别,请添加未记录的密钥规则)。
因此,此解决方案依赖于对 API 的微妙混淆,以阻止客户端程序员直接使用它。 Macguffin 接口及其方法的名称越模糊越好。
我需要从中生成一个库,但我不希望客户端看到 b_class。 我所知道的唯一解决方案是将我精美易懂的包扁平化为单个包,并为 b_class 使用默认包访问。 还有另一种方法吗?
是的,使b_class
包私有(默认访问)并通过反射实例化它以在a_class
使用。
既然你知道完整的类名,反射加载类:
Class<?> clz = Class.forName("b.b_class")
找到要调用的构造函数:
Constructor<?> con = clz.getDeclaredConstructor();
通过使其可访问来允许自己调用构造函数:
con.setAccessible(true);
调用构造函数来获取你的b_class
实例:
Object o = con.newInstance();
b_class
,现在你有了一个b_class
的实例。 但是,您不能在Object
的实例上调用b_class
的方法,因此您有两个选择:
b_class
的方法(没什么好玩的,但足够简单,如果你只有几个参数很少的方法可能没问题)。b_class
实现一个您不介意客户端看到的接口并将您的b_class
实例b_class
为该接口(我怀疑您可能已经拥有这样的接口?)。您肯定希望使用选项 2 来减少您的痛苦,除非它让您再次回到原点(用您不想向客户端公开的类型污染命名空间)。
为了全面披露,有两个注意事项:
1) 使用反射与直接实例化和调用有一个(小)开销。 如果您转换到接口,您只需支付实例化的反射成本。 在任何情况下,除非您在紧密循环中进行数十万次调用,否则这可能不是问题。
2) 没有什么可以阻止一个坚定的客户找出类名并做同样的事情,但如果我正确理解你的动机,你只想公开一个干净的 API,所以这真的不是一个担心。
如果我理解正确,您是在询问是否要在不公开部分来源的情况下发布您的库供 3rd 方使用? 如果是这种情况,您可以使用proguard ,它可以混淆您的库。 默认情况下,所有内容都将被排除/混淆,除非您指定要从混淆/排除中排除的内容。
如果您想分发 [部分] 代码而客户端根本无法访问它,这意味着客户端也将无法执行它。 :-O
因此,您只有一个选择:将代码的合理部分放入公共服务器并分发代理来访问它,这样您的代码将保留并在您的服务器中执行,并且客户端仍然可以通过代理,但不直接访问它。
您可以使用 servlet、Web 服务、RMI 对象或简单的 TCP 服务器,具体取决于代码的复杂程度。
这是我能想到的最安全的方法,但它也值得付出代价:除了使您的系统变得复杂之外,它还会为每个远程操作引入网络延迟,这可能会很重要,具体取决于性能要求。 此外,您应该保护服务器本身,以避免黑客入侵。 如果您已经拥有可以利用的服务器,这可能是一个很好的解决方案。
使用 Kotlin 时,您可以对库类使用internal
修饰符。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.