简体   繁体   English

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

[英]How to prevent client from seeing internal private classes in Android library ?

I have a library with several packages-我有一个包含多个包的库-

lets say让我们说
package a;套餐一;
package b;包 b;

inside package a I have public a_class包内 a 我有公共 a_class
inside package b I have public b_class在包 b 里面我有公共 b_class
a_class uses b_class. a_class 使用 b_class。

I need to generate a library from this , but I do not want the Client to see b_class.我需要从中生成一个库,但我不希望客户端看到 b_class。

The only solution I know of is to flatten my beautifully understandable packages to single package and to use default package access for b_class.我所知道的唯一解决方案是将我精美易懂的包扁平化为单个包,并为 b_class 使用默认包访问。 Is there another way to do so ?还有另一种方法吗? maybe using interfaces or some form of design pattern ??也许使用接口或某种形式的设计模式?

If you reject to move the code to an individual, controlled server, all you can do is to hinder the client programmer when trying to use your APIs.如果您拒绝将代码移动到单独的受控服务器,那么您所能做的就是在尝试使用您的 API 时阻碍客户端程序员 Let's begin applying good practices to your design:让我们开始将良好实践应用到您的设计中:

  1. Let your packages organized as they are now.让您的包裹像现在一样井然有序。
  2. For every class you want to "hide":对于您想要“隐藏”的每个类:

    • Make it non-public.使其非公开。
    • Extract its public API to a new, public interface:将其公共 API 提取到一个新的公共接口:

    public interface MyInterface {...}

    • Create a public factory class to get an object of that interface type.创建一个公共工厂类以获取该接口类型的对象。

    public class MyFactory { public MyInterface createObject(); }

So far, you have now your packages loosely coupled, and the implementation classes are now private (as good practices preach, and you already said).到目前为止,您的包现在是松散耦合的,并且实现类现在是私有的(正如您已经说过的良好实践所宣扬的那样)。 Still, they are yet available through the interfaces and factories.尽管如此,它们仍然可以通过接口和工厂使用。

So, how can you avoid that "stranger" clients execute your private APIs?那么,如何避免“陌生人”客户端执行您的私有 API? What comes next is a creative, a little complicated, yet valid solution, based on hindering the client programmers:接下来是一个创造性的、有点复杂但有效的解决方案,基于阻碍客户端程序员:

Modify your factory classes: Add to every factory method a new parameter:修改您的工厂类:向每个工厂方法添加一个新参数:

public class MyFactory
{
    public MyInterface createObject(Macguffin parameter);
}

So, what is Macguffin ?那么,什么是Macguffin It is a new interface you must define in your application, with at least one method:这是您必须在应用程序中定义的新接口,至少有一个方法:

public interface Macguffin
{
    public String dummyMethod();
}

But do not provide any usable implementation of this interface.不提供此接口的任何可用实现。 In every place of your code you need to provide a Macguffin object, create it through an anonymous class:在您需要提供Macguffin对象的代码的每个地方,通过匿名类创建它:

MyFactory.getObject(new Macguffin(){
    public String dummyMethod(){
        return "x";
    }
});

Or, even more advanced, through a dynamic proxy object , so no ".class" file of this implementation would be found even if the client programmer dares to decompile the code.或者,更高级的是,通过动态代理对象,因此即使客户端程序员敢于反编译代码,也不会找到此实现的“.class”文件。

What do you get from this?你从中得到什么? Basically is to dissuade the programmer from using a factory which requires an unknown, undocumented, ununderstandable object.基本上是劝阻程序员不要使用需要未知、未记录、无法理解的对象的工厂。 The factory classes should just care not to receive a null object, and to invoke the dummy method and check the return value it is not null either (or, if you want a higher security level, add an undocumented secret-key-rule).工厂类应该只关心不接收空对象,并调用虚拟方法并检查返回值它也不为空(或者,如果您想要更高的安全级别,请添加未记录的密钥规则)。

So this solution relies upon a subtle obfuscation of your API, to discourage the client programmer to use it directly.因此,此解决方案依赖于对 API 的微妙混淆,以阻止客户端程序员直接使用它。 The more obscure the names of the Macguffin interface and its methods, the better. Macguffin 接口及其方法的名称越模糊越好。

I need to generate a library from this , but I do not want the Client to see b_class.我需要从中生成一个库,但我不希望客户端看到 b_class。 The only solution I know of is to flatten my beautifully understandable packages to single package and to use default package access for b_class.我所知道的唯一解决方案是将我精美易懂的包扁平化为单个包,并为 b_class 使用默认包访问。 Is there another way to do so ?还有另一种方法吗?

Yes, make b_class package-private (default access) and instantiate it via reflection for use in a_class .是的,使b_class包私有(默认访问)并通过反射实例化它以在a_class使用。

Since you know the full class name, reflectively load the class:既然你知道完整的类名,反射加载类:

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

Find the constructor you want to invoke:找到要调用的构造函数:

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

Allow yourself to invoke the constructor by making it accessible:通过使其可访问来允许自己调用构造函数:

con.setAccessible(true);

Invoke the constructor to obtain your b_class instance:调用构造函数来获取你的b_class实例:

Object o = con.newInstance();

Hurrah, now you have an instance of b_class . b_class ,现在你有了一个b_class的实例。 However, you can't call b_class 's methods on an instance of Object , so you have two options:但是,您不能在Object的实例上调用b_class的方法,因此您有两个选择:

  1. Use reflection to invoke b_class 's methods (not much fun, but easy enough and may be ok if you only have a few methods with few parameters).使用反射来调用b_class的方法(没什么好玩的,但足够简单,如果你只有几个参数很少的方法可能没问题)。
  2. Have b_class implement an interface that you don't mind the client seeing and cast your instance of b_class to that interface (reading between the lines I suspect you may already have such an interface?).b_class实现一个您不介意客户端看到的接口并将您的b_class实例b_class为该接口(我怀疑您可能已经拥有这样的接口?)。

You'll definitely want to go with option 2 to minimise your pain unless it gets you back to square one again (polluting the namespace with types you don't want to expose the client to).您肯定希望使用选项 2 来减少您的痛苦,除非它让您再次回到原点(用您不想向客户端公开的类型污染命名空间)。

For full disclosure, two notes:为了全面披露,有两个注意事项:

1) There is a (small) overhead to using reflection vs direct instantiation and invocation. 1) 使用反射与直接实例化和调用有一个(小)开销。 If you cast to an interface you'll only pay the cost of reflection on the instantiation.如果您转换到接口,您只需支付实例化的反射成本。 In any case it likely isn't a problem unless you make hundreds of thousands of invocations in a tight loop.在任何情况下,除非您在紧密循环中进行数十万次调用,否则这可能不是问题。

2) There is nothing to stop a determined client from finding out the class name and doing the same thing, but if I understand your motivation correctly you just want expose a clean API, so this isn't really a worry. 2) 没有什么可以阻止一个坚定的客户找出类名并做同样的事情,但如果我正确理解你的动机,你只想公开一个干净的 API,所以这真的不是一个担心。

If I understand correctly you are asking about publishing your library for 3rd party usage without disclosing part of your source?如果我理解正确,您是在询问是否要在不公开部分来源的情况下发布您的库供 3rd 方使用? If that's the case you can use proguard , which can obfuscate your library.如果是这种情况,您可以使用proguard ,它可以混淆您的库。 By default everything will be excluded/obfuscated, unless you specify things you want to exclude from being obfuscated/excluded.默认情况下,所有内容都将被排除/混淆,除非您指定要从混淆/排除中排除的内容。

If you want to distribute [part of] your code without the client being able to access it at all, that means that the client won't be able to execute it either.如果您想分发 [部分] 代码而客户端根本无法访问它,这意味着客户端也将无法执行它。 :-O :-O

Thus, you just have one option: Put the sensible part of your code into a public server and distribute a proxy to access it, so that your code would be kept and executed into your server and the client would still be able to execute it through the proxy but without accessing it directly.因此,您只有一个选择:将代码的合理部分放入公共服务器并分发代理来访问它,这样您的代码将保留并在您的服务器中执行,并且客户端仍然可以通过代理,但不直接访问它。

You might use a servlet, a webservice, a RMI object, or a simple TCP server, depending on the complexity level of your code.您可以使用 servlet、Web 服务、RMI 对象或简单的 TCP 服务器,具体取决于代码的复杂程度。

This is the safest approach I can think of, but it also deserves a price to pay: In addition to complexing your system, it would introduce a network delay for each remote operation, which might be big deal depending on the performance requirements.这是我能想到的最安全的方法,但它也值得付出代价:除了使您的系统变得复杂之外,它还会为每个远程操作引入网络延迟,这可能会很重要,具体取决于性能要求。 Also, you should securize the server itself, to avoid hacker intrussions.此外,您应该保护服务器本身,以避免黑客入侵。 This could be a good solution if you already have a server that you could take advantage of.如果您已经拥有可以利用的服务器,这可能是一个很好的解决方案。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM