Saturday, December 24, 2011

Eclipse Maven and Android romance

This is a story of three, Eclipse as the IDE, Maven as the build, and Android is our dear framework...

The hell with them all!

The first time I tried to build an Android project, I've used the ADT build, which was flawed, but it did the work, later on most bugs were fixed, but the most annoying matter remained, Why the hell to use the Eclipse old build process, if I love the Maven repository concept, and the fact that it saves lots of build path configuration time, and very straight forward?

So I started digging for an Android-Maven plugin, as it was there was one, it called maven-android-plugin,
I've tried it out, and the results were terrible, importing other Maven 'apklibs' together with 'jar' artifacts from the Maven repository ended up in a tragedy, the project didn't compile. So I have learned the build process of Android, and implemented a workaround in the pom.xml to build the project. The real challenge was to enable Eclipse to also build the project using the ADT, so F11 would instantly debug the application, and sign it with Eclipse debug key.

This took days, but finally I got it working... I was so happy, I think it was ADT 13 that everything worked then... and then Google released their ADT 14, which for some reason messed everything up, again, not compiling and not deploying, I had to rewrite my pom only to realized ADT 14 had a bug, and ADT 15 should fix, which it did not...

Frustrated as I was, as soon as ADT 16 came out, I've installed it, and gave it another go, after two long hours the project compiled, with all it dependencies, Jars, and Apklibs, I was thrilled, I could not wait to try it out... but here is the funny part:

When launching the application from Eclipse in debug mode, the ADT (starting from version 8) addes the debuggable="true" to the manifest, by default, and the apk is signed with Eclipse default debug key, you would not believe how this feature saves your time... doing this with maven has a few minutes overhead, for every time you want to switch configuration... hell, I was willing to accept this, and then I realized, that for every deploy I want to have, an install must be made... do you have any idea how long it takes to build a serious apk with a single 'apklib' dependency, and other pure java dependencies? more then 2 min...

So every time I would want to deploy an app and test it, I would have to configure, and wait... what the hell, I don't have time for this BS, so I've left the pom.xml file in the project root folder and manipulated the '.classpath' file, and preserved two copies:

One to handle ADT 16 build process, and the other to build with android-maven-plugin.

I've kept the Maven build to build from my keystore, and sign the apk for publishing, so when I build using continues integration, I can publish the apk instantly...

The END.


Sunday, December 4, 2011

Android AsyncTask great idea BAD implementation!

I got so tired of Memory leaks in my android application which was caused mainly by the AsyncTask object of the Android framework, so I've implemented my own as part of my "Cyborg" project, this works for me...

I can use the same instance of the task over and over and over, and to publish to the UI on a UI thread...

Shame on you Android and shame on you Google... :(

No warranty... use at your own risk!!!

I've updated this on the 16-03-2012, previous version had a design flaw.



package com.nu.art.software.android.core;

import java.lang.ref.WeakReference;

import android.os.Handler;
import android.os.Message;

import com.nu.art.software.android.log.AndroidLogImpl;
import com.nu.art.software.android.log.Logger;

public abstract class AsyncTaskModel<Model, Progress, Result>
  extends Handler
  implements Logger {


 @Override
 @SuppressWarnings("unchecked")
 public void handleMessage(Message msg) {
  switch (msg.what) {
   case ProgressUpdate :
    onProgressUpdate((Progressmsg.obj);
    break;
   case ExecutionCompleted :
    onExecuteCompleted((Resultmsg.obj);
    break;
   case ExecutionCancelled :
    onExecutionCancelled((Resultmsg.obj);
    break;
   case Dispose :
    model = null;
    threadReference = null;
    break;
  }
  super.handleMessage(msg);
 }

 private static final int ProgressUpdate = 1;

 private static final int ExecutionCompleted = 2;

 private static final int ExecutionCancelled = 3;

 protected static final int Dispose = 4;

 protected Model model;

 private final String name;

 private WeakReference<Thread> threadReference;

 private volatile boolean cancelled;

 protected AsyncTaskModel(String name) {
  super();
  this.name = name;
 }

 public final boolean isRunning() {
  return threadReference != null;
 }

 public final void execute(Model model) {
  if (isRunning())
   throw new TaskInProcessException("Task is already running");
  this.model = model;
  cancelled = false;
  onPreExecute();
  Runnable r = new Runnable() {

   @Override
   public void run() {
    Result result = doInBackgroundImpl();
    Message message;
    if (cancelled)
     message = obtainMessage(ExecutionCancelled);
    else
     message = obtainMessage(ExecutionCompleted);
    message.obj = result;
    message.sendToTarget();
    message = obtainMessage(Dispose);
    message.sendToTarget();
   }
  };
  Thread thread = new Thread(r, name);
  thread.start();
  threadReference = new WeakReference<Thread>(thread);
 }

 protected abstract Result doInBackgroundImpl();

 protected abstract void onProgressUpdate(Progress progress);

 public final void cancel() {
  cancelled = true;
  cancelImpl();
  if (threadReference != null)
   threadReference.get().interrupt();
 }

 @SuppressWarnings("unused")
 protected void onExecutionCancelled(Result result) {}

 protected void cancelImpl() {}

 @SuppressWarnings("unused")
 protected void onExecuteCompleted(Result result) {}

 protected void onPreExecute() {}

 public void publishProgress(Progress progress) {
  if (cancelled)
   return;
  Message message = obtainMessage(ProgressUpdate);
  message.obj = progress;
  message.sendToTarget();
 }

 public boolean wasCancelled() {
  return cancelled;
 }

 @Override
 public void logDebug(String debug) {
  AndroidLogImpl.LogImpl.logDebug(debug);
 }

 @Override
 public void logError(String error) {
  AndroidLogImpl.LogImpl.logError(error);
 }

 @Override
 public void logError(String error, Throwable e) {
  AndroidLogImpl.LogImpl.logError(error, e);
 }

 @Override
 public void logError(Throwable e) {
  AndroidLogImpl.LogImpl.logError(e);
 }

 @Override
 public void logInfo(String info) {
  AndroidLogImpl.LogImpl.logInfo(info);
 }

 @Override
 public void logVerbose(String verbose) {
  AndroidLogImpl.LogImpl.logVerbose(verbose);
 }


 @Override
 public void logWarning(String warning) {
  AndroidLogImpl.LogImpl.logWarning(warning);
 }

 @Override
 public void logWarning(String warning, Throwable e) {
  AndroidLogImpl.LogImpl.logWarning(warning, e);
 }

}
Java2html