I'm writing a custom view that directly extends android.view.View. If I try to access fields mScrollX or mScrollY, I see an error that the field "cannot be resolved or is not a field." The source code for android.view.View has mScrollX, mScrollY, and similar variables declared protected. How is it that my direct subclass cannot access protected fields of its parent class? (Classes like ScrollView apparently can.)
P.S. I realize that I can call getScrollX(), but I want to update these fields; calling setScroll() has side effects that I don't want.
It's because they are not part of the Android SDK.
Here is the source code for mScrollX:
/**
* The offset, in pixels, by which the content of this view is scrolled
* horizontally.
* {#hide}
*/
#ViewDebug.ExportedProperty(category = "scrolling")
protected int mScrollX;
You will notice the #hide annotation. That means this is not part of the Android SDK. The part of the build process that creates the Android SDK will not include this data member in the stub edition of android.view.View that is in the android.jar file that you are compiling against.
The #hide annotation is used for things that for internal purposes needed to be public or protected but are not considered something SDK developers should be using.
Please find other solutions for whatever problem you are experiencing.
It's very straight forward: notice the #hide annotation above these variables.
It's an Android-specific annotation that hides the fields/methods from the public SDK. That's why you can't access them directly.
Romain Guy mentioned it in this post.
You could try setting the fields with reflection:
import java.lang.reflect.Field;
// ...
try {
Field scrollXField = View.class.getDeclaredField("mScrollX");
scrollXField.setAccessible(true);
scrollXField.set(this, myNewValue);
} catch (Exception ex) {
// oops, android changed the implementation. sucks to be you.
}
Note, however, that you're relying on undocumented and unsupported behavior when you do this, and so you should be prepared for things to break on some devices or in future versions.
Related
I have a quick question regarding modifying a final public class. Based on some researches, it seems like final public class cannot be inherited or implemented. My goal is to change one final static variable within this final public class.
class name is: public final class Utils
private static final Set<String> DISALLOWED_HEADERS_SET = Set.of(
"authorization", "connection", "cookie", "content-length",
"date", "expect", "from", "host", "origin", "proxy-authorization",
"referer", "user-agent", "upgrade", "via", "warning");
I want to get rid of authorization field from this DISALLOWED_HEADERS_SET. Is there any ways to doing this?
I heard reflection is one way to modify classes. This is a Apress/java-9-revealed to a github that seems to reveal what's inside of the class
This thread (question) has been identified as XY problem. I will try to explain with more information on why I want a solution for the above problem. Before digging into the reason that drove me to ask this question, I will cover the current situation of where this problem is at as of now.
It is important to understand that this problem has been already posed by Clevertap to Oracle. If you follow Oracle link, you can see that this problem has been acknowledged and updated to Jdk 11. Hopefully, Oracle applies this fixed source code to the coming Java 10, but it is highly unlikely given the fact that Jdk 9 represents Java 9. This being said, only solution there is to use reflection which the open thread in clevertap suggests.
Now, I will briefly explain what I have achieved and am trying to figure out. I have been working on a framework that I have been developing for sending Push Notification to APNs using Java language. Everything works except one functionality.
[I will share this framework through GitHub in the near future for those trying to send notification to APNs without depending on third party frameworks, such as Jetty, Netty, or okhttp.]
The problem rises when I try to use token as a way of authentication to send notification. I have successfully created token following through the instruction provided by Apple. All I have to do is to set request header with authorization key and bearer <token> value. However, when I use .setHeader derived from jdk9.incubator.httpclient module to set these values, it automatically omits this field. As aforementioned, authorization is part of DISALLOWED_HEADERS_SET and it is clearly not allowed. If a user attempts to set "authorization" as a key value for the request header field, it is deleted. If you have any suggestions to work around this problem. It will be awesome and helpful for others facing the same problem.
Bad news folks... jdk 9.0.4 removed setSystemHeader method, so if you want to use reflection, you need to use Jdk 9.0.1
As promised before, I created java library for sending notification using pure java code and pushed it to the github. I used older version that was based on jdk10 for my published app. Older version only supported tls connection. Now the current version based on jdk11 is supporting both tls and token based authentication for sending push notification to APNs.
Will just removing that value from the set work for you? Something like this:
Field f = Utils.class.getDeclaredField("DISALLOWED_HEADERS_SET");
f.setAccessible(true);
#SuppressWarnings("unchecked")
Set<String> headers = (Set<String>)f.get(null);
headers.remove("authorization");
There is no clear for me what are you going achieve by inheritance since this value is static.
But if you possible to recompile your application, have you considered of object composition? This can be an alternate to inheritance and is often used to simulate polymorphic behavior in case of final class or 'multiply' inheritance.
If you can 'inject' the new class instead of original one (which is final), you can compose a new class that contains instance of final class and delegates its methods to them. There is no need to create same final static variable.
If you wish to bank upon reflection, this might help -
Class reqClz = request.getClass();
Method setHeaderMethod = reqClz.getDeclaredMethod("setSystemHeader", String.class, String.class);
setHeaderMethod.setAccessible(true);
setHeaderMethod.invoke(request, "authorization", "<token>");
for which you might have to open the incubator module's package with the use of VM arg:-
--add-opens jdk.incubator.httpclient/jdk.incubator.http=<your-package x.y.z>
OR
Was also thinking that, another way here possibly could be patch the module content
--patch-module <module>=<file>(<pathsep><file>)*
for the jdk.incubator.http.internal.common.Utils class but as recommended not more than testing or debugging purpose.
The following code allows you to change the value of private static fields (non-final):
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class SetFinalField {
#SuppressWarnings("unchecked")
public final static void main(String[] args) throws Exception {
System.out.println("elements before: " + SetTarget.DISALLOWED_HEADERS_SET);
Field f = SetTarget.class.getDeclaredField("DISALLOWED_HEADERS_SET");
f.setAccessible(true);
ArrayList<String> l = new ArrayList<String>();
l.addAll((Set<String>) f.get(null));
l.remove("authorization");
HashSet<String> hs = new HashSet<>();
hs.addAll(l);
f.set(null, Collections.unmodifiableSet(hs));
System.out.println("elements after: " + SetTarget.DISALLOWED_HEADERS_SET);
}
public final static class SetTarget {
private static Set<String> DISALLOWED_HEADERS_SET;
static {
HashSet<String> l = new HashSet<>();
Collections.addAll(l, new String[]{"authorization", "connection", "cookie", "content-length",
"date", "expect", "from", "host", "origin", "proxy-authorization",
"referer", "user-agent", "upgrade", "via", "warning"});
DISALLOWED_HEADERS_SET = Collections.unmodifiableSet(l);
}
}
}
If you change the field to final this will be prevented, so to answer your question: It's not possible to do what you currently try to do using reflection. There is a way, though by using JNI, but you don't want to go that way. Whatever you try to accomplish you should try to find a different solution, e.g. by checking the class that is using this header if it can be configured with an alternate set of disallowed header values or go the way #nullpointer described.
This is confusing. I'm look at the Android 2.2.2_r1 source code for the NotificationManager class, and I see the method getService() which is defined as public and static. However, eclipse is telling me:
The method getService() is undefined for the type NotificationManager
on the line
Object o = NotificationManager.getService();
My project is building against Android 2.2/ API Level 8. I tried to use reflection to see the method names and modifiers, and sure enough, I got back
public static getService
Am I missing something here? Why would eclipse tell me this method doesn't exist?
You will find a very detailed answer in this post.
In short: Because you compile against the android.jar, which has all hidden methods (like the one you are trying to access) removed. They will only be there on runtime, for internal android usage.
But since you perhaps also need it. The right way to access the NotificationManager is via the getSystemService method of a context:
NotificationManager nm = (NotificationManager)ctx.getSystemService(Context.NOTIFICATION_SERVICE);
With context being a valid context (like your current activity).
So, here's an example. I have a library in the package HTTP. I define sub-sections of the library in e.g. the package HTTP.TCPProtocol. Now I want to use TCPProtocol from the HTTP package, which means I have to make the TCPProtocol functionality public. At the same time, this functionality should not be exported to users of the library.
How do I do this? I don't want to shove my whole library into one package, as I feel the separate sub-packages really make the code more structured and navigation easier in eclipse. But browsing around, I couldn't find a method to expose functions within my project, but not export them outside my project.
EDIT: In light of me being able to come up with a better example, I'm updating the OP.
One simplistic approach is to whitelist your 'utility' methods so they take a caller instance of a certain type only.
package gameengine;
interface Whitelisted {} // marker
Then your method:
public void myMethod(Whitelisted caller, String arg)
And to invoke:
package gameengine.network;
class Foo implements Whitelisted {
...
Someclass.myMethod(this, "foo");
Check the caller's class to lock out all unwanted callers. The caller's class can be obtained from the stacktrace. In the example below, only instances of Bar will trigger the system.out.println, all all other will get an exception. You can even do package-level checks this way. Make sure that all allowed caller classes methods are not public, or they can call the doSomething method indirectly. You can even do deeper checks, by inspecting the stacktrace further.
Be aware though, that a skilled develper can circumvent anything you try do do in this matter. No solution is really "secure".
package one.two;
import one.Bar;
public class Foo {
public void doSomething() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
StackTraceElement stackTraceElement = stackTrace[2];
String className = stackTraceElement.getClassName();
if (Bar.class.getName().equals(className)) {
System.out.println("jay!");
} else {
throw new RuntimeException("not allowed");
}
}
}
package one;
import one.two.Foo;
public class Bar {
void makeCall() {
new Foo().doSomething();
}
public static void main(String[] args) {
new Bar().makeCall();
}
}
Without seeing your dependencies, the only real advice the community can give you is to refactor your code. If something in your networking package needs to know about your game engine, it seems like you have a leaky abstraction. Hard to say without seeing your code.
Eclipse's default template for new types (Window > Preferences > Code Style > Code Templates > New Java Files) looks like this:
${filecomment}
${package_declaration}
${typecomment}
${type_declaration}
Creating a new class, it'll look something like this:
package pkg;
import blah.blah;
public class FileName {
// Class is accessible to everyone, and can be inherited
}
Now, I'm fervent in my belief that access should be as restricted as possible, and inheritance should be forbidden unless explicitly permitted, so I'd like to change the ${type_declaration} to declare all classes as final rather than public:
package pkg;
import blah.blah;
final class FileName {
// Class is only accessible in package, and can't be inherited
}
That seems easier said than done. The only thing I've found googling is a 2004 question on Eclipse's mailing list which was unanswered.
So, the question in short: How can I change the default class/type modifiers in Eclipse?
I'm using Eclipse Galileo (3.5) if that matters.
Looks like it is not possible. The ${type_declaration} is internal stuff.
What you can do is to click everytime the final checkbox in the "New Java Class"-Dialog. But that's not something you want to.
Just check the appropriate access modifier when creating the new class with the New Class Wizard.
New Java Class Wizard
Okay, i think there isn't any cool answer, so what about that "hack"?
${filecomment}
${package_declaration}
${typecomment}
import invalid;/* ${type_declaration} */
final class ${type_name} { }
If you now hit Control + Shift + O to organize imports, the old type declaration disappears. You could also add organize imports to save action to automate.
I know it's bad, but it does what you want.
Maybe this will help you?
eclipse\plugins\org.eclipse.jdt.ui_*.jar\templates\
Eclipse custom variable for java code templates
Here is my workaround:
Edit the template:
${filecomment}
${package_declaration}
${typecomment}
final class ${type_name} {
/* ${type_declaration} //delete */
/**
* #see {#link Object#toString()}
* #return String representation of this instance.
*/
public String toString() {
return "some impl";
}
}
Comment out the ${type_declaration} because it is required. You have comment to delete, but requirement is achieved. Sorry if this is a 2 year old thread, but to me it is still relevant.
From the perspective of a cross application/applet java accessibility service, how would you link to a package but only optionally execute an action based on existence/availability of a package (being already loaded) at runtime?
I think what I'm interested in here is a way to resolve the class identity crisis but rather than the issue being between 2 apps sharing objects, being a service loaded at a higher level of the class loaders.
It seems like reflection is the way to go, but I am not sure how or if I can implement a derived class this way. I need to add a specific listener derived from the specific optional classes, I can load the listener using the applet class loader but the internals still fail. Say you wanted to add an JInternalFrameListener, but Swing wasn't guaranteed to be available, using reflection you can find the method to add the listener, but how can you create and have the frame listener work if it cannot find any of the related classes because they can't be found in the base classloader! Do I need to create a thread and use setContextClassLoader to the classloader that knows about swing so that I can get the class to be loaded reliably? simply trying to set the class loader on my existing thread didn't seem to work.
Earlier description of issues
Sorry, I'm not quite sure what to ask or how to make this clear, so it rambles on a bit.
Say a class uses some feature of another, but the other class may not always be available - say finding the website from JNLP if this is a JNLP app.
At one stage I thought that simply compiling against JNLP would mean that my class would not load unless JNLP was available, and so to identify this optional section I simply wrapped a try{} catch( NoClassDefFoundError ) around it.
Later something changed (perhaps changing jdk or ?? I don't recall) and it seemed that I should also use a try{} catch( ClassNotFoundException ).
Now I wanted to extend this idea to other optional features, but it doesn't seem to work consistently.
Say I wanted to add some feature to do something more advanced in a JRE1.6 runtime using the same jar and classes as I run in a JRE1.3, or say I want to handle some controls in a specific gui toolkit which may not always be used like SWT or oracle.forms.
Is there some way of doing this more reliably? It just seems wrong to cause an exception and catch it to ignore it all the time.
The current issue comes down to being able to compile against oracle.forms but then the accessibility component installed in ext is unable to access the oracle.forms classes even though objects from the oracle.forms package have been created. If I throw the frmall.jar into the ext directory to test then the accessibility component works up to the point that the whole lot gets flakey because of the different versions of the same package.
I seem to be caught up on an issue with the class loader not being the right one or something (??). How do I find the right one?
Edit:
The answers so far are kindof interesting but not quite getting me where I want to be.
In the case of the gui components I currently compile in the form of a factory something like...
import oracle.forms.ui.*;
import java.awt.*;
static public IComponentNode newNode( INode parent, Component component ) {
System.out.println( component.getClass().toString() );
try{
if( component instanceof FormDesktopContainer )
... does stuff here like return new FormDesktopNode( parent, (FormDesktopContainer) component )
} catch ( NoClassDefFoundError a ) {
System.out.println( a.getMessage() );
}
where it prints out class oracle.forms.ui.FormDesktopContainer and then throws and exception on the instanceof call with NoClassDefFound thus printing out oracle/forms/ui/FormDesktopContainer
So how can it have an instance of a class yet not be able to find it?
How about this? messy, but it ought to work:
public boolean exists(String className){
try {
Class.forName(className);
return true;
}
catch (ClassNotFoundException){
return false;
}
}
You can check the availability of a class by calling
ClassLoader.getSystemClassLoader().loadClass("my.package.MyClass")
if it throws a ClassNotFoundException, it's not available. If you get the Class object, it is. You can then choose behaviour based on whether or not the class is available.
I suggest compiling the majority of your code against your minimum target. Have code that uses particular optional libraries clearly separated, but dependent upon the bulk of your code. Dynamically load the code that uses optional libraries once. The main class should do something that checks for the presence of the required library/version in its static initialiser.
In the case of JNLP, your JNLP main class load the JNLP dependent code statically.
(Note that attempting to catch class loading related exceptions from normally linked code is unreliable.)
getSystemClass loader was not useful for this purpose as there where multiple possible class loaders to interact with based on which applet the given window was in. The accessibility components being loaded at a more base class loader cannot see the applet specific classes.
To interact with the objects reflection does the job, though it does add so much more to maintain.
// statically linking would be
return component.getText();
// dynamically is
try {
return (String)component.getClass().getMethod("getText", new Class [] {}).invoke(component, new Object [] {});
} catch (Throwable e) {
e.printStackTrace();
}
The trickier bit is in writing a class derived from an interface that is not directly accessible, using the Proxy service allows this to be accomplished, providing the proxy service the applet specific class loader and the dynamically loaded class for the interface.
public void addListener(Container parent) {
if (parent == null) { return; }
if ("oracle.forms".equals(parent.getClass().getName())) {
// Using the class loader of the provided object in the applet
// get the "class" of the interface you want to implement
Class desktopListenerClass = Class.forName( "oracle.DesktopListener"
, true, parent.getClass().getClassLoader());
// Ask the proxy to create an instance of the class,
// providing your implementation through the InvocationHandler::invoke
Object desktopListener = Proxy.newProxyInstance(
parent.getClass().getClassLoader()
, new Class[] { desktopListenerClass }, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if ("functionName".equals(method.getName())) {
// do stuff
}
return null;
}
});
// do something with your new object
Method addDesktopListener = parent.getClass().getMethod("");
addDesktopListener.invoke(parent, desktopListener);
}
}
examples cut down to show general method