views:

242

answers:

7

Hi, I have been trying to use reflection for a specifiec Field in the android.os.build class, the MANUFACTURER field... I have tried by using this code :

try
    {
        Class myBuildClass = android.os.Build.class;
        Field m1 = Build.class.getDeclaredField("MANUFACTURER");
        validField = true;

        manufacturer = Build.MANUFACTURER;
    }
    catch(Exception ex)
    {
        manufacturer = Build.PRODUCT;
        System.err.println("getDeviceSpecifics, got an exception during getting Field : " + ex.toString());
    }

I am gettign the following errors :

06-01 11:26:37.639: WARN/dalvikvm(7342): VFY: unable to resolve static field 2 (MANUFACTURER) in Landroid/os/Build;
06-01 11:26:37.639: WARN/dalvikvm(7342): VFY:  rejecting opcode 0x62 at 0x0048

06-01 11:26:37.639: WARN/dalvikvm(7342): VFY:  rejected Lmypackage/android/managementModule/Management;.getDeviceSpecifics ()V
06-01 11:26:37.639: WARN/dalvikvm(7342): Verifier rejected class Lmypackage/android/managementModule/Management;

And when debugging I noticed that InvocationtargetException is continuesly thrown, so I am guessing I haven't been implementing the whole Reflection principle correctly... Any idea where things are going wrong or otherwise on how to implement Refelction for a single Field correctly ?

+1  A: 

This field is public. You dont need reflection to read it.

But there are several problems in your code which cause this error:

Class myBuildClass = android.os.Build.class;
Field m1 = Build.class.getDeclaredField("MANUFACTURER");

You defining myBuildClass but later you're never using it. The same for field m1. And after that your just directly accessing it by calling

Build.MANUFACTURER;

Then what is the whole refleciton stuff used?

If it would be private you do it the following way:

Field manufacturer = android.os.Build.class.getDeclaredField("MANUFACTURER");
manufacturer.setAccessible(true);
int myManufacturer = manufacturer.getInt(android.os.Build.class);

Note: manufacturer.getInt(android.os.Build.class) is only possible because its a static field. Otherwise you would need to pass an actual object of this class.

I saw in your comments that you want to check if the field is available. The field will always be available, but maybe it has different values from time to time. Why shouldn't it be available?

Ok now i also saw in your comments that you want to do it for 1.6. Take a look at the documentation and you will see hat this field was added at API level 1.4.

Your code may work in 1.6, but i'll have to mention that i think its really unnecessary and partly also wrong.

Roflcoptr
I just want to check if that Field is available...Even though it's public if I try to read it straight away my whole app will just die with the error :unable to resolve static field 2 (MANUFACTURER) in Landroid/os/Build;
TiGer
hmm why Landroid??? its called android. I think your Build.class references the false BUild class
Roflcoptr
no idea, I haven't actually done that... He also has done that with my package name, so I guess it's an IDE thing which is applied to packages ?btw the code works just fine on the 1.6 platform but I wanted to make it backwards compatible to the 1.5 platform...
TiGer
i dont know exactly why, but first your using android.os.Build.class and later just Build.class. And does my code still throws an exception?
Roflcoptr
Landroid/os/Build; is the standard JVM signature for a reference type. Reference type representations start with an L and end with a semicolon.
JRL
oki just tried your latest batch of code :Pseems to work fine, it checks if it's available and if not the other field (compatible with 1.5) is used... thx !
TiGer
I'm not getting it : my normal code works in 1.6 but when tried in 1.5 it wouldn't work (because of the Build.MANUFACTURER field), so thats why I wanted to use reflection, so that my 1.6 code wouldn't simply just crash on a 1.5 device... so no : the field will NOT always be available, it wont on a 1.5 device...shortly : my code had dto become backwards-compatible, I think you were thinking it had to become forwards-compatible, which is not the problem I was having :D
TiGer
I would recommend to use Build.VERSION.SDK_INT instead. Then you dont need this reflection stuff
Roflcoptr
well my previous code was something likeif(Build.VERSION.RELEASE < 1.6)manufacturer = Build.PRODUCT;elsemanufacturer = Build.MANUFACTURER;but that was giving errors as well, so I am guessing your Build.VERSION.SDK_INT wont be any better...
TiGer
just read the documentation. Build.VERSION.RELEASE returns a String. Therefore you can0t compare it in the way you did. But Build.VERSION.SDK_INT returns the SDK version number.
Roflcoptr
ok, that's why I said "something like", as in "that's not specifically the used code".. don't worry I know how to look into documentation, I do have two eyes AND the capability of reading... I did notice half the responses you get here are somewhat smart-asses responses with nearly-irritating nit-picking attitudes... So if that's the way people have to behave here I'd answer : just read my comment, "something like"... That means you should not take it literally...i am still missing a sensible answer from you Sebi... So the Build.VERSION.RELEASE option would be handy for what ?
TiGer
I think you're here because you want to learn something. I'm trying to tell you that I don't think you're approach to use reflection (which was additionally implemented very strange) isn't a very good strategy to solve your problem.
Roflcoptr
Ok, I'm not doubting your good intentions... Still I can't make up what you are trying to say with your post about recomending to use Build.VERSION.SDK_INT instead... Should I be using that to determine the SDK version, and according to that try to retrieve the Build.MANUFACTURER or not (in case of a 1.5 device) ? But how is that different from the code I posted before (the Build.VERSION.RELEASE code) ? As in won't I be getting exactly the same errors ?
TiGer
The problem with release is that it won't return just the number of version but also text like for example for android 2.1 revision 1 it will return 2.1-update1. So its more difficult to distinguish than just SDK_INT. See also this question http://stackoverflow.com/questions/2901722/how-do-i-determine-the-revision-number-of-an-android-build/2902842#2902842
Roflcoptr
oki I tried it :version = Build.VERSION.SDK_INT; boolean validField = false; if(version < 4) { manufacturer = Build.PRODUCT; } else { manufacturer = Build.MANUFACTURER; }Now I am getting the same error as in the beginning : 06-01 16:11:14.392: WARN/dalvikvm(9951): VFY: unable to resolve static field 2 (SDK_INT) in Landroid/os/Build$VERSION;Which, to be honest, was what I expected.. After all I wanted to implement reflection so that the call to Build.MANUFACTURER woulnd't make the app crash on a 1.5 device...
TiGer
oh yes. SKD_INT is only above lvl 3... sry for that. you could just use Build.VERSION.SDK. its depracted but usable from level 1.
Roflcoptr
Tried that one as well, but still the same error... I think it simply doesn't want a reference or call to that Field in 1.5, independent if it gets in a piece of code which will (or will not) call it... The crash actually occurs the moment my Class get's called , so it doesn't even get to the version = Build.VERSION.SDK; part, let alone the if-else... To be honest reflection seems the only way to go to me...
TiGer
can you post the whole code? i just don't see it why it shouldn't work...
Roflcoptr
A: 

If you look at the API docs, you'll see that Build.Manufacturer is available since API level 4 only. That means that if you run your code on an Android device of API version 3 or lower, it will fail since that field does not exist.

JRL
well it's not like I wnated to use reflection as a hobby ;)I was well aware of this fact, hence using reflection to be able to ascertain if the field is available or not :D
TiGer
A: 

From reading the comments to the other questions, I now understand better why you can not just directly access the field. Because your code may run an a pre version 4.0 Android.

Still, I do not think reflection is necessary. How about

try
{
     Build . MANUFACTURER ;
     // do stuff with Build . MANUFACTURER 
}
catch ( AppropriateException )
{
     // pre version 4.0 Contingency Plan
}
emory
sorry won't work on Android 1.5 as well...and yes the whole point is that I want to be able to run my Android 1.6 code on 1.5 devices as well, which don't have that Field... Thus using reflection and returning another Field if the MANUFACTURER Field is not available...
TiGer
A: 

Wouldn't it be easier to check the SDK version first rather then looking for the field via reflection?

if (Build.VERSION.SDK_INT >= 4) {
    return Build.MANUFACTURER;
} else {
    return Build.PRODUCT;
}
jeepers
hehe I would need reflection for that call also, Build.VERSION.SDK_INT being a Field available from API level 4, thus UNAVAILABLE on Android 1.5...
TiGer
Ha, fair enough, but the Build.VERSION.SDK string has been there since level 1. Looks like the bigger problem is that the Dalvik VM is verifying that the field exists even before your code tries to access it, unlike the JVM which defers the check until execution.
jeepers
A: 

@Sebi, hereby the whole code, I have placed it as an "answer" because posting code in those comments is pretty unreadable

String manufacturer;
try //reflection
        {
            Field manufacturerField = android.os.Build.class.getDeclaredField("MANUFACTURER");
            manufacturerField.setAccessible(true);
            int myManufacturer = manufacturerField.getInt(android.os.Build.class);
            manufacturer = Build.MANUFACTURER;
        }
        catch(Exception ex)
        {
            manufacturer = Build.PRODUCT;

        System.err.println("getDeviceSpecifics, got an exception during getting Field : " + ex.toString());
    }

This WON'T work on my 1.5 device. That's because the line

manufacturer = Build.MANUFACTURER;

is there, so if that one is ommitted as in :

 try    //reflection
    {
        Field manufacturerField = android.os.Build.class.getDeclaredField("MANUFACTURER");
        manufacturerField.setAccessible(true);
        int myManufacturer = manufacturerField.getInt(android.os.Build.class);
    }
    catch(Exception ex)
    {
        manufacturer = Build.PRODUCT;

        System.err.println("getDeviceSpecifics, got an exception during getting Field : " + ex.toString());
    }

It actually will work, the problem obviously is that I also want that single line in my code, so that on a 1.6 Device I can retrieve manufacturer = Build.MANUFACTURER;

btw, I just tested all suggestions on a 1.5 device AND 1.5 emulator and they both throw errors, the funny part is that even the shortest code within a try-catch construction will still throw an

06-02 09:26:54.741: ERROR/AndroidRuntime(782): Uncaught handler: thread main exiting due to uncaught exception
06-02 09:26:54.791: ERROR/AndroidRuntime(782): java.lang.VerifyError: mypackage.android.Reflection.reflectiontest
TiGer
A: 

I've had a simular issue, here is the relevant snippet I played with:

try {
    return android.os.Build.class.getField("MANUFACTURER").get(null).toString();
} catch (Exception e) {
    return "???";
}
Eric Grange
+1  A: 

Perfect solution

try // reflection
  {
   Field manufacturerField = android.os.Build.class
     .getDeclaredField("MANUFACTURER");
   manufacturerField.setAccessible(true);
   String manufacturerName = manufacturerField.get(
     android.os.Build.class).toString();
   System.out.println("manufacturerName: " + manufacturerName);

  } catch (Exception ex) {

   System.err
     .println("getManufacturerName, got an exception during getting Field : "
       + ex.toString());
  }
Rashmi