Java 如何防止客户端在Android库中看到内部私有类?

Java 如何防止客户端在Android库中看到内部私有类?,java,android,jar,android-library,Java,Android,Jar,Android Library,我有一个带几个软件包的图书馆- 让我们说 a包 b包 在a包内,我有一个公共a_类 在b包内,我有公共b_类 a_类使用b_类 我需要从中生成一个库,但我不希望客户端看到b_类 我所知道的唯一解决方案是将我的可理解的包扁平化为单个包,并对b_类使用默认包访问。 还有别的办法吗?可能使用接口或某种形式的设计模式???如果我理解正确,您要求发布库供第三方使用,而不披露部分源代码?如果您可以使用这种情况,则可能会混淆您的库。默认情况下,所有内容都将被排除/模糊处理,除非您指定了要排除模糊处理/模糊处理

我有一个带几个软件包的图书馆-

让我们说
a包
b包

在a包内,我有一个公共a_类
在b包内,我有公共b_类
a_类使用b_类

我需要从中生成一个库,但我不希望客户端看到b_类

我所知道的唯一解决方案是将我的可理解的包扁平化为单个包,并对b_类使用默认包访问。
还有别的办法吗?可能使用接口或某种形式的设计模式???

如果我理解正确,您要求发布库供第三方使用,而不披露部分源代码?如果您可以使用这种情况,则可能会混淆您的库。默认情况下,所有内容都将被排除/模糊处理,除非您指定了要排除模糊处理/模糊处理的内容。

如果您希望在客户端无法访问代码的情况下分发代码的[部分],则意味着客户端也将无法执行代码。:-O

因此,您只有一个选择:将代码的合理部分放入公共服务器,并分发一个代理来访问它,这样您的代码将保留在服务器中并在服务器中执行,客户机仍然能够通过代理执行代码,但无需直接访问它

根据代码的复杂程度,可以使用servlet、webservice、RMI对象或简单的TCP服务器


这是我能想到的最安全的方法,但它也值得付出代价:除了使系统复杂化之外,它还会为每个远程操作引入网络延迟,这可能会很大程度上取决于性能要求。此外,您还应该对服务器本身进行安全保护,以避免黑客入侵。如果您已经有了一个可以利用的服务器,这可能是一个很好的解决方案。

如果您拒绝将代码移动到单独的受控服务器,那么您所能做的就是在尝试使用API时阻碍客户机程序员。让我们开始将良好实践应用于您的设计:

  • 让您的包按现在的方式组织
  • 对于每个要“隐藏”的类:

    • 让它成为非公开的
    • 将其公共API提取到新的公共接口:
    公共接口MyInterface{…}

    • 创建公共工厂类以获取该接口类型的对象
    公共类MyFactory
    {
    公共MyInterface createObject();
    }

  • 到目前为止,您已经有了松散耦合的包,实现类现在是私有的(正如良好实践所宣扬的,您已经说过的)。尽管如此,它们仍然可以通过接口和工厂使用

    那么,如何避免“陌生人”客户端执行您的私有API呢?接下来是一个创造性的、有点复杂但有效的解决方案,其基础是阻碍客户端程序员:

    修改factory类:为每个factory方法添加一个新参数:

    public class MyFactory
    {
        public MyInterface createObject(Macguffin parameter);
    }
    
    那么,什么是
    Macguffin
    ?它是您必须在应用程序中定义的新接口,至少有一种方法:

    public interface Macguffin
    {
        public String dummyMethod();
    }
    
    但不提供此接口的任何可用实现。在需要提供
    Macguffin
    对象的代码的每个地方,通过匿名类创建它:

    或者,更高级的是,通过动态代理对象,因此即使客户端程序员敢于反编译代码,也不会找到此实现的“.class”文件

    你从中得到了什么?基本上是劝阻程序员不要使用需要未知、未记录、无法理解的对象的工厂。工厂类应该只关心不接收null对象,调用dummy方法并检查返回值是否为null(或者,如果您想要更高的安全级别,请添加一个未记录的密钥规则)

    因此,此解决方案依赖于对API的细微混淆,以阻止客户端程序员直接使用它。Macguffin接口及其方法的名称越模糊越好

    我需要从中生成一个库,但我不希望客户端看到b_类。我所知道的唯一解决方案是将我的可理解的包扁平化为单个包,并对b_类使用默认包访问。还有别的办法吗

    是,将
    b_类
    包设为私有(默认访问),并通过反射将其实例化,以便在
    a_类
    中使用

    因为您知道完整的类名,所以可以反射式加载该类:

    Class<?> clz = Class.forName("b.b_class")
    
    调用构造函数以获取您的
    b_类
    实例:

    Object o = con.newInstance();
    
    好极了,现在您有了
    b_类的一个实例。但是,不能对
    对象的实例调用
    b_类的方法,因此有两个选项:

  • 使用反射来调用
    b_类
    的方法(虽然不是很有趣,但是很简单,如果只有几个方法和几个参数,可能还可以)
  • b_类
    实现一个您不介意客户看到的接口,并将您的
    b_类
    实例投射到该接口(我怀疑您可能已经有了这样的接口?) 您肯定希望使用选项2来最小化您的痛苦,除非它让您再次回到原点(用您不想向客户机公开的类型污染名称空间)

    关于全面披露,请注意以下两点:

    1) 与直接实例化和调用相比,使用反射有(很小的)开销。如果您强制转换到一个接口,您只需支付实例化的反射成本。在任何情况下,这都不是问题,除非您在一个紧密的循环中进行成千上万次调用

    2) 没有什么可以阻止已确定的客户端查找类名并执行相同的精简操作
    Constructor<?> con = clz.getDeclaredConstructor();
    
    con.setAccessible(true);
    
    Object o = con.newInstance();