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

时间:2015-01-26 08:53:53

标签: java android jar android-library

我有一个包含多个包的库 -

让我们说 包a;
包b;

内包a我有公共a_class
在包内b我有公共b_class
a_class使用b_class。

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

我所知道的唯一解决方案是将我精美易懂的软件包展平为单个软件包,并使用b_class的默认软件包访问。 还有另一种方法吗?也许使用接口或某种形式的设计模式??

5 个答案:

答案 0 :(得分:11)

如果拒绝将代码移动到受控服务器上,那么在尝试使用API​​时,您只能阻止客户端程序员。让我们开始将好的做法应用到您的设计中:

  1. 让你的包裹按原样安排。
  2. 对于您想要“隐藏”的每个课程:

    • 让它不公开。
    • 将其公共API解压缩到新的公共界面:

    public interface MyInterface {...}

    • 创建公共工厂类以获取该接口类型的对象。

    public class MyFactory { public MyInterface createObject(); }

  3. 到目前为止,你现在已经将你的软件包松散耦合了,而且实现类现在是私有的(正如良好实践所说的那样,你已经说过了)。尽管如此,它们仍然可以通过接口和工厂获得。

    那么,如何避免“陌生人”客户端执行您的私有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”文件。

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

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

答案 1 :(得分:6)

  

我需要从中生成一个库,但我不希望客户端看到b_class。我所知道的唯一解决方案是将我精美易懂的软件包展平为单个软件包,并使用b_class的默认软件包访问。还有另一种方法吗?

是的,使b_class package-private(默认访问)并通过反射对其进行实例化,以便在a_class中使用。

由于你知道完整的类名,反过来加载类:

Class<?> clz = Class.forName("b.b_class")

找到要调用的构造函数:

Constructor<?> con = clz.getDeclaredConstructor();

允许自己通过使构造函数可访问来调用构造函数:

con.setAccessible(true);

调用构造函数以获取b_class实例:

Object o = con.newInstance();

Hurray,现在你有一个b_class的实例。但是,您无法在b_class的实例上调用Object方法,因此您有两种选择:

  1. 使用反射来调用b_class的方法(不是很有趣,但很容易,如果你只有一些参数很少的方法,可能没问题。)
  2. b_class实现一个您不会介意客户看到并将b_class的实例投射到该界面的界面(在我怀疑您可能已经拥有此类界面的行之间读取) ?)。
  3. 你肯定希望选择2来减少你的痛苦,除非它让你再次回到正方形(用你不想暴露客户端的类型污染命名空间)。

    完整披露,有两个注释:

    1)使用反射与直接实例化和调用存在(小)开销。如果你转换为界面,你只需支付实例化的反射成本。在任何情况下,除非您在紧密的循环中进行数十万次调用,否则它可能不是问题。

    2)没有什么可以阻止一个坚定的客户找到类名并做同样的事情,但如果我理解你的动机你只是想要暴露一个干净的API,所以这不是一个真正的担心

答案 2 :(得分:1)

如果我理解正确您要求发布图书馆以供第三方使用,而不会泄露您的部分来源?如果是这种情况,您可以使用proguard,这可能会混淆您的库。默认情况下,除非您指定要排除混淆/排除的内容,否则将排除/混淆所有内容。

答案 3 :(得分:1)

如果您希望分发[部分]代码而客户端根本无法访问它,则意味着客户端也无法执行它。 :-O

因此,您只需要一个选项:将代码的合理部分放入公共服务器并分发代理以访问它,以便将代码保存并执行到您的服务器中客户端仍然可以通过代理执行它,但不能直接访问它。

您可以使用servlet,Web服务,RMI对象或简单的TCP服务器,具体取决于代码的复杂程度。

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

答案 4 :(得分:0)

使用 Kotlin 时,您可以对库类使用 internal 修饰符。