Sunday, June 5, 2011

Adding Aspect to Android

Following post done a nice job at explaining how to introduce aspects in android world.

Pune GTUG: Adding Aspect to Android

I gave it a try and it worked like a charm with some modifications. I have listed my modifications below just to save them safely. Two thumbs up for the original authors !

To make this work,

  • I installed AspectJ 1.6 version and added "/Users/nehameht/aspectj1.6/bin" to my PATH.
  • renamed all .properties files and build.xml file with .bk extensions to save their contents for later use.
  • Executed "android update project" command to generate build.xml.
./android update project --name HelloWorld --target 8 --path /Users/nehameht/Downloads/android-aspectj
  • Verified (and modified if needed) .properties files to point to my android sdk and AspectJ install locations
local.properties:
sdk.dir=/android-sdk-mac_86

build.properties:
aspectj.home=/Users/nehameht/aspectj1.6

default.properties:
target=Google Inc.:Google APIs:8
  • Added following sections (whole XML nodes) from the saved build.xml.bk file to new generated build.xml.
<property name="aop.output" value="bin/aop"/>
<taskdef resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties">
   <classpath>
       <pathelement location="${aspectj.home}/lib/aspectjtools.jar"/>
   </classpath>
</taskdef>

<property name="external.libs.dir" value="libs"/>
<property name="external.libs.absolute.dir" location="${external.libs.dir}"/>
<!-- extension targets. Uncomment the ones where you want to do custom work
     in between standard targets -->
<!--
    <target name="-pre-build">
    </target>
    <target name="-pre-compile">
    </target>

    [This is typically used for code obfuscation.
     Compiled code location: ${out.classes.absolute.dir}
     If this is not done in place, override ${out.dex.input.absolute.dir}]
    <target name="-post-compile">
    </target>
-->
<target name="-post-compile">
<echo message="Weaving aspects to .class files before dex converts .class files to .dex file"/>
   <iajc destDir="${aop.output}" Xlintwarnings="true" showWeaveInfo="true" target="1.5"
    source="1.5">
       <argfiles>
           <pathelement location="trace.lst"/>
       </argfiles>
       <inpath>
           <pathelement location="${out.classes.absolute.dir}"/>
       </inpath>
       <classpath>
           <pathelement location="${aspectj.home}/lib/aspectjrt.jar"/>
           <fileset dir="${external.libs.absolute.dir}" includes="*.jar"/>
           <fileset dir="${extensible.libs.classpath}" includes="*.jar"/>
           <path refid="android.target.classpath"/>
       </classpath>
   </iajc>
<move file="${out.classes.absolute.dir}" todir="bin/classes.old"/>
<move file="bin/aop/com" todir="bin/classes"/>
</target>
  • After this "ant install" worked just fine. And I was able to verify the traces thru logcat.

13 comments:

  1. Thanks for this post! I tried it but I get this error when I try to build the app:

    BUILD FAILED
    /Users/user/Documents/workspace/Android-Aspect/build.xml:68: Problem: failed to create task or type iajc
    Cause: The name is undefined.
    Action: Check the spelling.
    Action: Check that any custom tasks/types have been declared.
    Action: Check that any / declarations have taken place.

    Could you help me with that?
    Thanks!

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Update: I was able to install the app using Ant but when I launch it it crashes with a ClassNotFound exception. I can't copy & paste my build file here but I can send it to you in case you want to have alook at it.

    ReplyDelete
  4. can you post the exception here? Are you trying to get the sample project posted at the above blog working? Or you are getting errors when trying to get your project working?

    ReplyDelete
  5. I am trying to get the sample project working. Here's the exception:

    E/AndroidRuntime( 252): FATAL EXCEPTION: main
    E/AndroidRuntime( 252): java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.test/com.test.HelloWorldActivity}: java.lang.ClassNotFoundException: com.test.HelloWorldActivity in loader dalvik.system.PathClassLoader[/data/app/com.test-2.apk]
    E/AndroidRuntime( 252): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2585)
    E/AndroidRuntime( 252): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2679)
    E/AndroidRuntime( 252): at android.app.ActivityThread.access$2300(ActivityThread.java:125)
    E/AndroidRuntime( 252): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2033)
    E/AndroidRuntime( 252): at android.os.Handler.dispatchMessage(Handler.java:99)
    E/AndroidRuntime( 252): at android.os.Looper.loop(Looper.java:123)
    E/AndroidRuntime( 252): at android.app.ActivityThread.main(ActivityThread.java:4627)
    E/AndroidRuntime( 252): at java.lang.reflect.Method.invokeNative(Native Method)
    E/AndroidRuntime( 252): at java.lang.reflect.Method.invoke(Method.java:521)
    E/AndroidRuntime( 252): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
    E/AndroidRuntime( 252): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
    E/AndroidRuntime( 252): at dalvik.system.NativeStart.main(Native Method)
    E/AndroidRuntime( 252): Caused by: java.lang.ClassNotFoundException: com.test.HelloWorldActivity in loader dalvik.system.PathClassLoader[/data/app/com.test-2.apk]
    E/AndroidRuntime( 252): at dalvik.system.PathClassLoader.findClass(PathClassLoader.java:243)
    E/AndroidRuntime( 252): at java.lang.ClassLoader.loadClass(ClassLoader.java:573)
    E/AndroidRuntime( 252): at java.lang.ClassLoader.loadClass(ClassLoader.java:532)
    E/AndroidRuntime( 252): at android.app.Instrumentation.newActivity(Instrumentation.java:1021)
    E/AndroidRuntime( 252): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2577)
    E/AndroidRuntime( 252): ... 11 more
    W/ActivityManager( 60): Force finishing activity com.test/.HelloWorldActivity

    Thanks very much for your help!

    ReplyDelete
  6. Another thing I just noticed: the aop.output property isn"t set in the build.xml file I am using (I followed your instructions). If I try to add that definition then the build fails with this exception:
    [apply] UNEXPECTED TOP-LEVEL EXCEPTION:
    [apply] com.android.dx.cf.code.SimException: local variable type mismatch: attempt to set or access a value of type int using a local variable of type java.lang.String. This is symptomatic of .class transformation tools that ignore local variable information

    This is the same exception I get when building using the "original" build.xml. I believe that something is not working in the dex-helper macro, but I've got no clue on how to fix that.

    ReplyDelete
  7. You are right, I missed to add the line that sets the property aop.output. I have modified the post with that line.

    From following line in your exception, I think you have got multiple applications that might be using same package names.

    java.lang.ClassNotFoundException: com.test.HelloWorldActivity in loader dalvik.system.PathClassLoader[/data/app/com.test-2.apk

    Notice the "-2" appended to apk name. You may have more than one activities with same names in those two apks. I would recommend uninstalling any of those apps and then trying with this one.

    ReplyDelete
  8. That doesn't solve the issue though. As far as I understood - correct me if I am wrong - in the end the post-compile target moves the weaved .class files to the "bin/classes" directory so that the apk can be created using those .class files. Without setting that property the apk created by ant did not contain the weaved code, so I think the real problem is this UNEXPECTED TOP-LEVEL EXCEPTION and not the ClassNotFound one (but your explanation on this one makes total sense).

    ReplyDelete
  9. I have no clue. To doble check, I did the same setup on my ubuntu machine and it was giving me ClassDefNotFound error for one of the class from aspecjrt.jar - org.aspectj.runtime.reflect.Factory. To bring the environment same as my Mac, I updated all the SDK tools and platforms to the latest and after that it worked fine. FYI, I am using google API 8 as target.

    ReplyDelete
  10. Hello, nice work. I've found a solution without using Ant by using AJDT plugin for Eclipse which enables also AspectJ UI guidelines and suggestions. I posted an article here: http://ikrk.wz.cz/archiv/23-how-to-use-aspectj-with-android

    Hope it helps.

    ReplyDelete
  11. @ Adam: Thanks ! It works great. Very well documented as well.

    ReplyDelete
  12. I gave up on Ant and I started using the AJDT as well but I followed the HelloWorldAspectJ project demo here: http://www.eclipse.org/ajdt/demos/HelloWorldAspectJ.html. In particular what I missed was the "Convert to AspectJ" step, after doing that my project compiles again. That said: the AJDT is a bit flakey, and honestly it makes Android development even more painful (but the AJDT guys are really active and reply to bug reports quickly).

    ReplyDelete