Android.dll

androidmono

Picture above represents the results of 3 sleepless nights. No, I didn’t rewrite the Android framework. I generated a bunch of proxy classes to access them from Mono though. Rough idea of how it works:

  1. Java application reflects through Android framework SDK jar and outputs an XML file that represents the object model.
  2. C# application generates thousands of code files of said object model. (This is similar to jni4net, but that doesn’t work on anything but Windows).
  3. Tag all the methods with the [MethodImpl(MethodImplOptions.InternalCall)] attribute.
  4. Implement the method calls at runtime with a combination of dynamically created expression tree delegates and mono_add_internal_call.

The class contents actually have no code, so the total dll size is pretty small (800k). Here’s what the LinearLayout “implementation” looks like:

namespace android.widget 
{ 
    public class LinearLayout : android.view.ViewGroup
    { 
        public class LayoutParams : android.view.ViewGroup.MarginLayoutParams
        { 
            [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
            public virtual extern java.lang.String debug(java.lang.String arg0); 
            [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
            public extern LayoutParams(android.content.Context arg0, android.util.AttributeSet arg1); 
            [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
            public extern LayoutParams(int arg0, int arg1); 
            [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
            public extern LayoutParams(int arg0, int arg1, float arg2); 
            [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
            public extern LayoutParams(android.view.ViewGroup.LayoutParams arg0); 
            [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
            public extern LayoutParams(android.view.ViewGroup.MarginLayoutParams arg0); 
            [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
            internal extern LayoutParams(); 
        } 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        public virtual extern void setGravity(int arg0); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        protected override extern void onLayout(bool arg0, int arg1, int arg2, int arg3, int arg4); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        public override extern int getBaseline(); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        protected override extern void onMeasure(int arg0, int arg1); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        protected override extern bool checkLayoutParams(android.view.ViewGroup.LayoutParams arg0); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        public virtual extern new android.widget.LinearLayout.LayoutParams generateLayoutParams(android.util.AttributeSet arg0); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        protected virtual extern new android.widget.LinearLayout.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams arg0); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        protected virtual extern new android.widget.LinearLayout.LayoutParams generateDefaultLayoutParams(); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        public virtual extern bool isBaselineAligned(); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        public virtual extern void setBaselineAligned(bool arg0); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        public virtual extern int getBaselineAlignedChildIndex(); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        public virtual extern void setBaselineAlignedChildIndex(int arg0); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        public virtual extern float getWeightSum(); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        public virtual extern void setWeightSum(float arg0); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        public virtual extern void setOrientation(int arg0); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        public virtual extern int getOrientation(); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        public virtual extern void setHorizontalGravity(int arg0); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        public virtual extern void setVerticalGravity(int arg0); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        public extern LinearLayout(android.content.Context arg0, android.util.AttributeSet arg1); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        public extern LinearLayout(android.content.Context arg0); 
        [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] 
        internal extern LinearLayout(); 
    } 
} 

A year of on and off hacking coming to fruition soon…

33 comments:

Lluis said...

Awesome! any plans to .net-ify the api? that is, convert getX methods to properties, or use pascal case for method and property names.

Ammaar Limbada said...

This is awesome work. I can't wait to start using C# for Android development. Keep it up. :)

cooldavee said...

This is totally awesome! Fantastic job! :-)

Koush said...

Lluis: Yep. I'll definitely be converting get/set methods to properties.

j said...

Wow, this is so great Koush! Thanks a lot!

Aaron C said...

fantastic. Keep it up!

Casper Bang said...

Thumb up Koush! Can't wait to hear more about it.

Anonymous said...

Very exciting, great work!

Flow84 said...

Nice! :) You should be able to get the size of Android.dll smaller by using "using" for namespaces that is widly used. Like: Using cs = system.runtime.compilerservice

Sandy said...

@Flow84 That is not true. Those using aliases do not have any impact on the compiled code.

Flow84 said...

@Sandy
Oooh, then i have learned something today to. Thanks :)

Jon said...
This comment has been removed by the author.
Jon said...

So this means for every new release of the SDK, you can just generate a new .NET wrapper that corresponds to the SDK? Sweet. I can't wait to see this once the library is .net-ified!

Simon said...

Awesome work! Waiting like a kid on christmas for this to go "workable"!

Jarrett said...

Keep it up Koush! I'd love to see some more performance numbers as well.

Anonymous said...

Instead of using reflection to sniff out the Android Java APIs you can generate your stubs from the API XML files directly. The SDK android.jar is built using the XML file, and there is one for each API revision. They're stored in frameworks/base/api. See http://android.git.kernel.org/?p=platform/frameworks/base.git;a=tree;f=api;h=95963e2d0ba894e53bea5e67a562eddf0f082401;hb=eclair.

Koush said...

Wow, that's awesome! That's almost the exact format of XML file that I ended up generating.

Toni said...

Hi,
thanks alot for your hardwork!!!!

I hope this will help unity3d.com to port unity3d to android ...that would be AWESOME!!

whitemice said...

I can't wait to be able to develop Android apps in Monodevelop. This is awesome! I hope it hits release soon.

Jae said...

Are you thinking of collaborating with the jni4net folks to push for a generalized OSS java to .net bridge?

Koush said...

I've talked with Pavel. There is some code I can reuse, specifically the P/Invokes to JNI and marshaling.
But, for the purposes of Android, I can't reuse as much code as I would like. Also, not being runtime agnostic like jni4net (Microsoft's CLR vs Mono) allows me to do some really cool things when embedding Mono.

Anonymous said...

Koush, I am so glad you moved onto the Android platform. I am a .NET developer & have been spoiled. The Java / Dalvik APIs are (IMO) unnatural / alien / too much like hard work. If this becomes as big as I hope I would be delighted. Keep up the hard work. I hope you succeed in all your en devours.

psgivens said...

Koushik,

I'm really glad that there is someone out there like you to take up this cause. The end game for me is to be able to write one component that can be used from both ASP.net and Android.

Phillip

Casa said...

Can't wait to build Android application with C# !!! Keep it UP !

KiD0M4N said...

Great job Koushik! As a avid .NET developer, this is indeed great news for me. If you need a guinea pig for helping you test stuff, I am all up for it :)

Anonymous said...

Hi koush. Any news on progress? Really looking forward to doing .net on android.

Stefan Rusek said...

Koush: When do you think there might be a beta version of support for writing apps in C#?

Scott said...

I'm very much looking forward to this being released! Like many Windows / ASP.NET developers, I'm interested in developing for Android... just not interested enough to write code in Java!

Keep up the good work!

Nathan Clevenger said...

Are you planning on putting your Android.dll code on a collaborative open source environment like Github so other members of the community can participate in building out Mono on Android?

This is very exciting work - I'm waiting anxiously ;-)

Kirk Davis said...

I just wanted to add my "Kudos, Koush" for working on this. As a long-time .Net developer who has been playing around with the Android SDK - and I actually like the XML-based layout for Android UI, it reminds me of Silverlight/WPF development) - I would LOVE LOVE LOVE if there was a way to leverage my C#/.Net experience to build apps for Android.

I like both Eclipse and Monodevelop (even if I'm a lot more used to VS), and if you manage to get a workable version of Mono support on Android that doesn't involve too many hours of developer-tweaking for each compile-test iteration, I would gladly pay/donate for the tools.

Best of luck, and in the meantime, I'll keep struggling to learn the Android API and Java's idiosyncrasies.

delfinof said...

Hi,
Has the project gone forward? I am not seeing any progress since a while on your blog about Android...

Anonymous said...

It is such a shame but this seems to be abandoned. I hope I'm wrong.

Dylan said...

Hi Koushik, I am develing a compiler for the .NET and Mono runtimes and thanks to you I can not just put it on Linux,Win32 and Mac but also Android. THanks very very much.