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.
A few of my tips and tricks on software development and architecture, and why to do things as I see them.
Saturday, December 24, 2011
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.
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;
|
Java2html |
Tuesday, November 15, 2011
How to save an archive
I'm not going to say too much, this does the trick... no grantees...
/**
* An object that archives files and folders.
*
* @author TacB0sS
*/
public class Archiver {
/**
* The file to save the archive to.
*/
private File archiveFile;
/**
* The archive manifest.
*/
private Manifest manifest;
/**
* The collection of files and folders to archive.
*/
private Vector<File> files = new Vector<File>();
/**
* Inner management file to represent the common parent file of the file collection to archive.
*/
private File parentFile;
/**
* Adds a file to the files list for archiving.
*
* @param file The file to add.
*/
public void addFile(File file) {
if (parentFile != null)
if (!file.getParentFile().equals(parentFile))
throw new IllegalArgumentException("Multiple files with multiple parents is not allowed!");
files.add(file);
if (files.size() == 1)
parentFile = file.getParentFile();
}
/**
* @param files An array of files to add to the archive.
*/
public void addFiles(File[] files) {
for (File file : files)
addFile(file);
}
/**
* Performs the archiving process.
*
* @return The file object with the specified file path.
* @throws IOException If an IOException has occurred while archiving
*/
public File archiveToFile()
throws IOException {
archiveFile.createNewFile();
JarOutputStream jos;
if (manifest == null)
jos = new JarOutputStream(new FileOutputStream(archiveFile));
else
jos = new JarOutputStream(new FileOutputStream(archiveFile), manifest);
writeFilesIntoArchive(files.toArray(new File[files.size()]), jos);
jos.finish();
jos.close();
return archiveFile;
}
public File getFilePath() {
return archiveFile;
}
public Manifest getManifest() {
return manifest;
}
/**
* Removes a file from the files list for archiving.
*
* @param file The file to remove.
*/
public void removeFile(File file) {
files.remove(file);
if (files.size() == 0)
parentFile = null;
}
/**
* @param files An array of files to remove from the archive.
*/
public void removeFiles(File[] files) {
for (File file : files)
removeFile(file);
}
/**
* The file path to save the archive to.
*
* @param archiveFile The file to save the archive to.
*/
public void setArchiveFile(File archiveFile) {
this.archiveFile = archiveFile;
}
/**
* If there is a need for manifest, set it up.
*
* @param manifest The archive manifest.
*/
public void setManifest(Manifest manifest) {
this.manifest = manifest;
}
private void writeFilesIntoArchive(File[] files, JarOutputStream jos)
throws IOException {
for (File file : files) {
if (file.isDirectory()) {
writeFilesIntoArchive(file.listFiles(), jos);
continue;
}
writeIntoJar(file, jos);
}
}
private void writeIntoJar(File toArchive, JarOutputStream jos)
throws IOException {
byte[] buf = new byte[1024];
String relativePath = toArchive.getAbsolutePath().replace(parentFile.getAbsolutePath(), "").replace(File.separator, "/");
relativePath = relativePath.substring(1);
jos.putNextEntry(new JarEntry(relativePath));
FileInputStream fis = new FileInputStream(toArchive);
int anz;
while ((anz = fis.read(buf)) != -1)
jos.write(buf, 0, anz);
jos.closeEntry();
}
}
/**
* An object that archives files and folders.
*
* @author TacB0sS
*/
public class Archiver {
/**
* The file to save the archive to.
*/
private File archiveFile;
/**
* The archive manifest.
*/
private Manifest manifest;
/**
* The collection of files and folders to archive.
*/
private Vector<File> files = new Vector<File>();
/**
* Inner management file to represent the common parent file of the file collection to archive.
*/
private File parentFile;
/**
* Adds a file to the files list for archiving.
*
* @param file The file to add.
*/
public void addFile(File file) {
if (parentFile != null)
if (!file.getParentFile().equals(parentFile))
throw new IllegalArgumentException("Multiple files with multiple parents is not allowed!");
files.add(file);
if (files.size() == 1)
parentFile = file.getParentFile();
}
/**
* @param files An array of files to add to the archive.
*/
public void addFiles(File[] files) {
for (File file : files)
addFile(file);
}
/**
* Performs the archiving process.
*
* @return The file object with the specified file path.
* @throws IOException If an IOException has occurred while archiving
*/
public File archiveToFile()
throws IOException {
archiveFile.createNewFile();
JarOutputStream jos;
if (manifest == null)
jos = new JarOutputStream(new FileOutputStream(archiveFile));
else
jos = new JarOutputStream(new FileOutputStream(archiveFile), manifest);
writeFilesIntoArchive(files.toArray(new File[files.size()]), jos);
jos.finish();
jos.close();
return archiveFile;
}
public File getFilePath() {
return archiveFile;
}
public Manifest getManifest() {
return manifest;
}
/**
* Removes a file from the files list for archiving.
*
* @param file The file to remove.
*/
public void removeFile(File file) {
files.remove(file);
if (files.size() == 0)
parentFile = null;
}
/**
* @param files An array of files to remove from the archive.
*/
public void removeFiles(File[] files) {
for (File file : files)
removeFile(file);
}
/**
* The file path to save the archive to.
*
* @param archiveFile The file to save the archive to.
*/
public void setArchiveFile(File archiveFile) {
this.archiveFile = archiveFile;
}
/**
* If there is a need for manifest, set it up.
*
* @param manifest The archive manifest.
*/
public void setManifest(Manifest manifest) {
this.manifest = manifest;
}
private void writeFilesIntoArchive(File[] files, JarOutputStream jos)
throws IOException {
for (File file : files) {
if (file.isDirectory()) {
writeFilesIntoArchive(file.listFiles(), jos);
continue;
}
writeIntoJar(file, jos);
}
}
private void writeIntoJar(File toArchive, JarOutputStream jos)
throws IOException {
byte[] buf = new byte[1024];
String relativePath = toArchive.getAbsolutePath().replace(parentFile.getAbsolutePath(), "").replace(File.separator, "/");
relativePath = relativePath.substring(1);
jos.putNextEntry(new JarEntry(relativePath));
FileInputStream fis = new FileInputStream(toArchive);
int anz;
while ((anz = fis.read(buf)) != -1)
jos.write(buf, 0, anz);
jos.closeEntry();
}
}
Instantiating Class via Reflection
These are snippets from an old object I use from time to time, the "InstanceType" is a generic type of the enclosing class, you may replace it with Object or '?' respectively.
First we find the desired constructor:
/**
* Searches for the constructor with the specified parameters in the supplied class object.
*
* @param _class The class which contains the constructor with the specified parameters.
* @param parameterTypes The constructor parameters types.
* @return The constructor instance of the supplied class.
* @throws ConstructorNotFoundException if a constructor with the supplied specifications was not found.
*/
@SuppressWarnings("unchecked")
private Constructor<InstanceType> findAConstructor(Class<InstanceType> _class, Class<?>[] parameterTypes)
throws ConstructorNotFoundException {
Constructor<?>[] constructors = _class.getConstructors();
for (Constructor<?> constructor2 : constructors)
if (compareConstructorParametersTypes(parameterTypes, constructor2.getParameterTypes()))
return (Constructor<InstanceType>) constructor2;
throw new ConstructorNotFoundException("There was no match for Constructor: \n " + _class.getSimpleName() + "("
+ ReflectiveTools.parseParametersType(parameterTypes) + "); \n In the specified class object: " + _class.getName());
}
Later just to invoke it to get the object:
public final InstanceType newInstance(Object... parameters)
throws ClassInstantiationException {
try {
checkParameters(parameters);
return constructor.newInstance(parameters);
} catch (IllegalArgumentException e) {
throw new ClassInstantiationException(this, parameters, e);
} catch (IllegalAccessException e) {
throw new ClassInstantiationException(this, parameters, e);
} catch (InvocationTargetException e) {
throw new ClassInstantiationException(this, parameters, e);
} catch (InstantiationException e) {
throw new ClassInstantiationException(this, parameters, e);
} catch (WrongParameterType e) {
throw new ClassInstantiationException(this, parameters, e);
}
}
Knock yourself out...
First we find the desired constructor:
/**
* Searches for the constructor with the specified parameters in the supplied class object.
*
* @param _class The class which contains the constructor with the specified parameters.
* @param parameterTypes The constructor parameters types.
* @return The constructor instance of the supplied class.
* @throws ConstructorNotFoundException if a constructor with the supplied specifications was not found.
*/
@SuppressWarnings("unchecked")
private Constructor<InstanceType> findAConstructor(Class<InstanceType> _class, Class<?>[] parameterTypes)
throws ConstructorNotFoundException {
Constructor<?>[] constructors = _class.getConstructors();
for (Constructor<?> constructor2 : constructors)
if (compareConstructorParametersTypes(parameterTypes, constructor2.getParameterTypes()))
return (Constructor<InstanceType>) constructor2;
throw new ConstructorNotFoundException("There was no match for Constructor: \n " + _class.getSimpleName() + "("
+ ReflectiveTools.parseParametersType(parameterTypes) + "); \n In the specified class object: " + _class.getName());
}
Later just to invoke it to get the object:
public final InstanceType newInstance(Object... parameters)
throws ClassInstantiationException {
try {
checkParameters(parameters);
return constructor.newInstance(parameters);
} catch (IllegalArgumentException e) {
throw new ClassInstantiationException(this, parameters, e);
} catch (IllegalAccessException e) {
throw new ClassInstantiationException(this, parameters, e);
} catch (InvocationTargetException e) {
throw new ClassInstantiationException(this, parameters, e);
} catch (InstantiationException e) {
throw new ClassInstantiationException(this, parameters, e);
} catch (WrongParameterType e) {
throw new ClassInstantiationException(this, parameters, e);
}
}
Knock yourself out...
Invoking Method via Reflection
These are snippets from an old object I use from time to time, the "InstanceType" is a generic type of the enclosing class, you may replace it with Object or '?' respectively
First of all we need to get the method instance:
/**
* Searches for the method with the specified parameters in the supplied class object.
*
* @param _class The class which contains the method with the specified parameters.
* @param methodName The method name to search for.
* @param parameterTypes The method parameters types.
* @return The method instance of the supplied class.
* @throws MethodNotFoundException if a method with the supplied specifications was not found.
*/
private Method findAMethod(Class<InstanceType> _class, String methodName, Class<?>... parameterTypes)
throws MethodNotFoundException {
Method[] methods = _class.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (!methods[i].getName().equals(methodName))
continue;
if (compareMethodParametersTypes(parameterTypes, methods[i].getParameterTypes()))
return methods[i];
}
throw new MethodNotFoundException("There was no match for method: \n " + methodName + "("
+ ReflectiveTools.parseParametersType(parameterTypes) + "); \n In the specified class object: " + _class.getName());
}
notifyer.handleException(e);
First of all we need to get the method instance:
/**
* Searches for the method with the specified parameters in the supplied class object.
*
* @param _class The class which contains the method with the specified parameters.
* @param methodName The method name to search for.
* @param parameterTypes The method parameters types.
* @return The method instance of the supplied class.
* @throws MethodNotFoundException if a method with the supplied specifications was not found.
*/
private Method findAMethod(Class<InstanceType> _class, String methodName, Class<?>... parameterTypes)
throws MethodNotFoundException {
Method[] methods = _class.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (!methods[i].getName().equals(methodName))
continue;
if (compareMethodParametersTypes(parameterTypes, methods[i].getParameterTypes()))
return methods[i];
}
throw new MethodNotFoundException("There was no match for method: \n " + methodName + "("
+ ReflectiveTools.parseParametersType(parameterTypes) + "); \n In the specified class object: " + _class.getName());
}
Next we need to invoke the method, with its parameters, which may be called synchronously,or asynchronously:
public final Object invokeMethod(InstanceType instance, Object... parameters)
throws MethodInvocationException {
try {
checkParameters(parameters);
return method.invoke(instance, parameters);
} catch (IllegalArgumentException e) {
throw new MethodInvocationException(method, instance, parameters, e);
} catch (IllegalAccessException e) {
throw new MethodInvocationException(method, instance, parameters, e);
} catch (InvocationTargetException e) {
throw new MethodInvocationException(method, instance, parameters, e);
} catch (WrongParameterType e) {
throw new MethodInvocationException(method, instance, parameters, e);
}
}
public final <ReturnType> void invokeMethodAsynch(final InstanceType instance, final MethodInvocationCompleted<ReturnType> notifyer, final Object... parameters) {
Runnable methodInvocationRun = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
try {
Object returnValue = invokeMethod(instance, parameters);
if (notifyer != null)
notifyer.methodInvocationCompleted((ReturnType) returnValue);
} catch (MethodInvocationException e) {
if (notifyer != null)
}
}
};
Thread t = new Thread(methodInvocationRun, "Invocation of method: " + ReflectiveTools.getMethodAsString(method));
t.start();
}
The Notifier interface should be implemented when called asynchronously:
public interface MethodInvocationCompleted<ReturnType> {
void handleException(MethodInvocationException e);
void methodInvocationCompleted(ReturnType returnValue);
}
Have Fun.
Tuesday, November 8, 2011
Creating an Array from a Generic class type
Well, I didn't figure this out quick enough but it is as simple as:
@SuppressWarnings("unchecked")
protected final <ArrayType> ArrayType[] getArray(Class<ArrayType> arrayType, int size) {
return (ArrayType[]) Array.newInstance(arrayType, size);
}
@SuppressWarnings("unchecked")
protected final <ArrayType> ArrayType[] getArray(Class<ArrayType> arrayType, int size) {
return (ArrayType[]) Array.newInstance(arrayType, size);
}
Ridicules isn't it...?
My architecture for a long time was lacking because I was missing this info, and in combination with this post, it makes Generics the most useful feature while designing an application architecture.
Monday, November 7, 2011
Getting Java generic parameter types in runtime
For the long while I was using Java Generic types, in Methods, Fields and Classes, and I used to transfer the Class type instance as a parameter to the constructor or method respectively. Lately I've learned how Java's Generic types, can be determine in runtime.
To get the generic types of a Class:
To make this work:
To get the generic types of a Class:
abstract class ClassWithGenericParameterType<ItemType> {
private Class<ItemType> itemType;
ClassWithGenericParameterType() {
ParameterizedType classType = (ParameterizedType) getClass().getGenericSuperclass();
Type[] types = classType.getActualTypeArguments();
itemType= (Class<ItemType>) types[0];
}
}
To make this work:
class ExtendingParameterizedGenericClass extends ClassWithGenericParameterType<String> {...}Thing is about this trick, is that the class you are analyzing must be abstract, and that a new Class would extend that abstract class specifying a valid Class as the Generic argument and not something like <ItemType2 extends Number>, this would result in getting Number.class!
Subscribe to:
Posts (Atom)