Categoryjava

Nice Shimmer library by Facebook

10734316_1115633315129045_1618068289_n

original post: https://code.facebook.com/posts/636856646421011/shimmer-for-android/

//DL

Foursquare like map animation (sliding panel) with ListView

If you are wondering how to achieve map and listview behaviour like in Foursquare – this post is for you.
Here’s a short video what I’m talking about:

Continue reading

Disco Lights in Android

There was a great party at Google IO last year, with lots of loud music, beer and, of course, with lots of new gadgets and smartphones around. For us, it was also a day when an idea of an application transformed into a set of simple requirements.

Probably, everyone has seen a device with a simple but entertaining functionality: transform audio into color. It has multiple names, but most people know it as “Disco lights”. It does not have any useful functions except one single cool thing – amaze people with the graphical representation of a music they listen.

Thus, the only requirement was: let’s have those disco lights on an android phone. It will record the audio from the mic, and draw something exciting on the screen, synchronously with the audio rhythm, notes and loudness.

Quick search in the market indicated nothing similar to this, so the idea made it to the actual application which is now published.

If you are still reading this, here are a few technical details about the implementation.

As you probably know, there are basically two common approaches to the audio-to-light converters:
1. A set of bandpass filters.
2. Signal-to-spectrum conversion via Fast Fourier Transform.

The initial implementation was using the FFT approach, as a quickest way to have a working app. Later on we did a few experiments with the bandpass filters as well, however it turned out to be that good FIR or IIR filters require too much computation power, especially if you want many frequency bands. Thus, it was decided to stick to the FFT solution and optimize it as much as possible.

A few words about the audio recording. Android devices support that with AudioRecord class. It gives PCM (wave) output @ 44100 Hz (the only standard frequency for every android phone), which is more than enough for our goal. In fact, it would be even too much, since this sampling frequency allows working with audio of up to 20 KHz and we will never be able to reach that level of quality through the microphone embedded in most smartphones.

Besides, the usual music spectrum (from rap to classics) fits well within much narrower band, something like 50 to 5000 Hz, so the sampling frequency of 44100 is not necessary. Fortunately, it can be easily decimated at runtime into half or quarter of that without much computation (just take every second of forth sample and that’s it).

The bad news is that the audio recording requires the platform to collect some certain amount of samples before passing it to the application, which, in simple words, means delays between the music you hear and visual effect you see. This delay seems to be different among android phone models, but keeping the recording buffer small helps reduce that delay to a reasonable minimum (100 ms is quite satisfactory).

The most interesting part of this is the performance and battery life. In order to make an application which would work on virtually any android phone, the weakiest phone needed to be considered. The Google Ion (HTC Saphire) with Qualcomm MSM 7201 @ 528 Mhz is probably the best representative of this category at the moment, so it was taken as the target device for development.

In order to evaluate the CPU speed, we did a few simple tests which indicated the difference between floating point and integer mathematics, as well as between java and native calculations.

Long story short, here are the data we collected. 100 millions multiplications. The speed that we observed was as follows:

Saphire’s CPU supports ARMv6 instruction set, however NDK compiles the code to ARMv5 and the floating point math is implemented in software. There are various sources in the Internet saying different things about floating point support in ARMv6 chipsets, but the most important knowledge from all that is: there are plenty of ARMv6 phones which do not have HW acceleration of the floating point math. Therefore, the winner is native long long int. Obviously, we will have to use NDK for native computations which is available since Cupcake. However, to make things more interesting, it was decided to use Donut as the oldest platform version, because in Donut we also have some OpenGL ES support natively which gave us greater choice of visual effect implementation.

Another thing worth mentioning is sqrt() function. Initially we have made our own (it was supposed to be quick as hell) implementation of sqrt(). It turned out that the standard implementation of this function is approximately 10x faster than Newton algorithm written in C. Seems like it is quite optimized for ARM CPU which is great. So the lesson basically is: do not try to make something faster unless you understand how fast you can make it 🙂

By the way, here is a great article about ARM computations of sqrt() function: http://www.finesse.demon.co.uk/steven/sqrt.html

All in all, here are the options which were chosen for the final version of the app:

1. FFT size is 512 samples, collected at 44100 Hz and downsampled to 11025 Hz, about 20 FFTs per second. This gives a signal spectrum for up to 5 KHz.

2. Effects are rendered on the SurfaceView at the same ~20 frames/sec

3. Frequency band is split into visual channels on the musical note basis (i.e. non-linear). Each channel measures overall audio power in its frequency band and renders a bar or circle with the appropriate color brightness.

This set gives a nice result from the entertainment point of view, and uses as little battery as possible.

That’s basically it. The only question which might be bothering you is why did we need OpenGL? Well, OpenGL is necessary for more visual effects that will be coming soon to the Disco Lights. 🙂

https://market.android.com/details?id=com.amberfog.colormusic

   

// Sergey

Some logging tips

If you don’t like use android debugger (because it is too slow on tablets or whatever) – you can use StackTraceElement to easily log your application activity.

You don’t need to put System.out in every method in your code. Just put LogUtils.trace() and you will know the exact method name and line of code.

Here’s simple sample LogUtils class:

package com.test.log;

// !!!!!
// NOTE: enabled log significantly reduces performance
// because it uses high cost getStackTrace() method, which produces huge amounts of objects
// !!!!!
public final class LogUtils {

	private static boolean sIsLogEnabled = true;
	private static String sApplicationTag = "LogDemo";

	private static final String TAG_FORMAT_METHOD = "%s:%s.%s:%d";
	private static final String FORMAT_METHOD_NAME = "%s:%d";

	private static StackTraceElement getCurrentStackTraceElement() {
		return Thread.currentThread().getStackTrace()[4];
	}

	public static void trace() {
		if (sIsLogEnabled) {
			android.util.Log.d(getMethodTag(getCurrentStackTraceElement()),
					getMethodName(getCurrentStackTraceElement()));
		}
	}

	private static String getMethodTag(StackTraceElement trace) {
		return String.format(TAG_FORMAT_METHOD, sApplicationTag,
				trace.getClassName(), trace.getMethodName(),
				trace.getLineNumber());
	}

	private static String getMethodName(StackTraceElement trace) {
		return String.format(FORMAT_METHOD_NAME, trace.getMethodName(),
				trace.getLineNumber());
	}

	public static void traceStack() {
		if (sIsLogEnabled) {
			traceStack(sApplicationTag, android.util.Log.ERROR);
		}
	}

	public static void traceStack(String tag, int priority) {
		if (sIsLogEnabled) {
			StackTraceElement[] stackTrace = Thread.currentThread()
					.getStackTrace();
			if (tag == null) {
				tag = getMethodTag(getCurrentStackTraceElement());
			}
			android.util.Log.println(priority, tag, stackTrace[4].toString());
			StringBuilder str = new StringBuilder();
			String prevClass = null;
			for (int i = 5; i < stackTrace.length; i++) {
				String className = stackTrace[i].getFileName();
				int idx = className.indexOf(".java");
				if (idx >= 0) {
					className = className.substring(0, idx);
				}
				if (prevClass == null || !prevClass.equals(className)) {
					str.append(className.substring(0, idx));
				}
				prevClass = className;
				str.append(".").append(stackTrace[i].getMethodName())
						.append(":").append(stackTrace[i].getLineNumber())
						.append("->");
			}
			android.util.Log.println(priority, tag, str.toString());
		}
	}

}

and simple Activity:

package com.test.log;

import android.app.Activity;
import android.os.Bundle;

public class LogDemoActivity extends Activity {

	private void methodB() {
		LogUtils.trace();
		LogUtils.traceStack();
	}

	private void methodA() {
		LogUtils.trace();
		methodB();
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		methodA();
	}
}

The output will be:

08-04 13:55:22.020: DEBUG/LogDemo:com.test.log.LogDemoActivity.methodA:13(31856): methodA:13
08-04 13:55:22.020: DEBUG/LogDemo:com.test.log.LogDemoActivity.methodB:9(31856): methodB:9

08-04 13:57:25.610: DEBUG/LogDemo:com.test.log.LogDemoActivity.methodA:14(31996): methodA:14
08-04 13:57:25.610: DEBUG/LogDemo:com.test.log.LogDemoActivity.methodB:9(31996): methodB:9
08-04 13:57:25.610: ERROR/LogDemo(31996): com.test.log.LogDemoActivity.methodB(LogDemoActivity.java:10)
08-04 13:57:25.610: ERROR/LogDemo(31996): LogDemoActivity.methodA:15->.onCreate:22->Instrumentation.callActivityOnCreate:1048->ActivityThread.performLaunchActivity:1715->.handleLaunchActivity:1767->.access$1500:122->.handleMessage:1005->Handler.dispatchMessage:99->Looper.loop:132->ActivityThread.main:4028->Method.invokeNative:-2->.invoke:491->ZygoteInit.run:844->.main:602->NativeStart.main:-2->

So you can easily trace:) Don’t forget to disable it for production.

//DL

Java Bomb

That’s just brilliant!

http://thedailywtf.com/Articles/Disgruntled-Bomb-Java-Edition.aspx

//DL

Android tutorial slides

I would like to share some Android-related tutorial slides that we presented with Alexey in 2010 (http://habrahabr.ru/blogs/android/95250/) at St.Petersburg GTUG.

Some tips are still actual. So some of you could find it helpful;)

«Java and Android basics» (78 slides)

http://vk.amberfog.com/gtug/part1_Introduction2JavaAndroid_gtug.pdf

1.     Java and Android code style
2.     Java Collections
3.     Dalvik – Java Android VM
4.     Memory management in Java (GC + hard, soft, weak, phantom references)
5.     Java Exceptions handling
6.     Multithreading in Java
7.     Android Architecture
8.     Android entities: Activity, Service, Intent, BroadcastReceiver, ContentProvider
9.     Android Application, Context and Manifest
10.   Android System Services
11.   Android platform fragmentation and Java Reflection
12.   Saving state of application
13.   Android NDK
14.   Optimization for mobile: Design for better performance, responsiveness, seamlessness


«Android UI» (81 slides)

http://vk.amberfog.com/gtug/part2_WorkingWithAndroidUI_gtug.pdf

http://vk.amberfog.com/gtug/part2Examples.zip

1.     Activity lifecycle
2.     Activity, Layout, View, UI events, XML layouts
3.     XML layout’s types: FrameLayout, RelativeLayout, LinearLayout, TableLayout
4.     Layout tricks: merge, ViewStub, include
5.     Work with ListView and Adapters
6.     Menu & Dialogs
7.     UI thread! (and ANR)
8.     Developing custom UI-elements
9.     UI-development for difference screen resolutions and Android platform versions
10.   UI layout-tree analysis and optimization with special tools (hierarchyviewer, layoutopt)
11.   UI performance improvements
12.   UI guidelines
13.   Styles, Themes, Activity based dialogs
14.   Widgets, Live Folders, Live Wallpapers

«Android in Action» (69 slides).

http://vk.amberfog.com/gtug/part3_AndroidInAction_gtug.pdf

1.     Dealing with screen orientation change
2.     Possible causes of memory leaks: analysis and tools
3.     Data flow: Parcelable, Bundle, etc.
4.     android.app.Service
5.     Effective XML data parsing protocol
6.     Working with Databases
7.     Input Methods
8.     Search Providers
9.     Debugging hints & tools
10.   Battery life
11.   Usage statistics: Flurry
12.   Application signing and publishing

enjoy

//DL

How to decompile Android application

It’s quite easy to decompile Android application and take a look into it:)

  1. Download dex2jar tool (thanks to the creator!) http://code.google.com/p/dex2jar/
  2. Find apk that you want to decompile (like Twitter_v1.0.5.apk)
  3. Use dex2jar.bat Twitter_v1.0.5.apk – get Twitter_v1.0.5.apk.dex2jar.jar
  4. Download Java Decompiler
  5. Open jar file – enjoy pure java code:)

java decompiler (dex android)

If you want to take a look into AndroidManifest.xml:

  1. Download AXMLPrinter2.jar
  2. Extract AndroidManifest.xml from apk file
  3. Use java -jar AXMLPrinter2.jar AndroidManifest.xml >> twitter_manifest.txt
  4. Enjoy readable AndroidManifest.xml

Hope you will learn a lot from somebody’s code and create a new powerful Android app! 🙂

//DL

How to use PhantomReference

What is PhantomReference?

    1. They allow you to determine exactly when an object was removed from memory. They are in fact the only way to determine that. This isn’t generally that useful, but might come in handy in certain very specific circumstances like manipulating large images: if you know for sure that an image should be garbage collected, you can wait until it actually is before attempting to load the next image, and therefore make the dreaded OutOfMemoryError less likely [read more]
    2. The main advantage of using a PhantomReference over finalize() is that finalize() is called by a garbage-collector thread, meaning it introduces concurrency even in a single-threaded program, with all the potential issues (like correctly synchronizing shared state). With a PhantomReference, you choose the thread that dequeues references from your queue (in a single-threaded program, that thread could periodically do this job) [read more]

      Read more about references here: http://www.ibm.com/developerworks/library/j-refs/

      See example in Email application in Android platform: http://google.com/codesearch/p?hl=en#SS2by_AKaLs/src/org/apache/commons/io/FileCleaningTracker.java

      Btw, you can also ressurect object with reflection (http://code-o-matic.blogspot.com/2009/01/subtleties-of-phantomreference-and.html)

      Here’re small exaple of usage:

      import java.lang.ref.PhantomReference;
      import java.lang.ref.Reference;
      import java.lang.ref.ReferenceQueue;
      import java.lang.reflect.Field;
      import java.util.ArrayList;
      
      public class MemoryTest {
          private ReferenceQueue<Data> mQueue = new ReferenceQueue<Data>();
          private boolean mIsRun = true;
      
          public void test() {
      
              final ArrayList<PhantomReference<Data>> blocks = new ArrayList<PhantomReference<Data>>();
              ArrayList<Data> dataArray = new ArrayList<Data>();
              System.out.println("occupied mem 1 = " + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()));
              for (int i = 0; i < 10; i++) {
                  Data data = new Data(i);
                  PhantomReference<Data> ref = new PhantomReference<Data>(data, mQueue);
                  blocks.add(ref);
                  dataArray.add(data);
              }
              System.out.println("occupied mem 2 = " + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()));
              System.out.println("remove strong ref to object 0 and 1");
              dataArray.remove(0);
              dataArray.remove(0);
              new Thread() {
                  public void run() {
                      System.out.println("thread started");
                      while (mIsRun) {
                          Reference<?> ref = null;
                          try {
                              ref = mQueue.remove(1000);
                          } catch (IllegalArgumentException e) {
                              e.printStackTrace();
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                          if (ref != null) {
                              System.out.println(">> removed ref = " + ref);
                              blocks.remove(ref);
                              Field f = null;
                              try {
                                  f = Reference.class.getDeclaredField("referent");
                                  f.setAccessible(true);
                                  System.out.println("I see dead objects! --> " + f.get(ref));
                              } catch (SecurityException e) {
                                  e.printStackTrace();
                              } catch (NoSuchFieldException e) {
                                  e.printStackTrace();
                              } catch (IllegalAccessException e) {
                                  e.printStackTrace();
                              }
                          }
                      }
                      System.out.println("thread ended");
                  }
              }.start();
              System.out.println("objects created");
              try {
                  Thread.sleep(3000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println("occupied mem 2 = " + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()));
              System.out.println("run gc() to make object enqueued");
              System.gc();
              try {
                  Thread.sleep(3000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println("collect memory");
              System.runFinalization();
              System.gc();
              try {
                  Thread.sleep(5000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println("occupied mem 4 = " + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()));
              mIsRun = false;
          }
      
          private static class Data {
              int id;
              byte[] memoryBlock = new byte[1048576];
      
              public Data(int id) {
                  this.id = id;
                  System.out.println("Data created: " + this);
              }
      
              @Override
              public String toString() {
                  return "{id=" + id + " " + super.toString() + "}";
              }
          }
      }
      

      What we can see in LogCat:

      06-08 14:38:51.154: INFO/System.out(1385): occupied mem 1 = 2186800
      06-08 14:38:51.254: DEBUG/dalvikvm(1385): GC freed 845 objects / 56288 bytes in 94ms
      06-08 14:38:51.295: INFO/dalvikvm-heap(1385): Grow heap (frag case) to 4.119MB for 1048592-byte allocation
      06-08 14:38:51.394: DEBUG/dalvikvm(1385): GC freed 11 objects / 544 bytes in 93ms
      06-08 14:38:51.404: INFO/System.out(1385): Data created: {id=0 com.gtug.test.MemoryTest$Data@4373e658}
      06-08 14:38:51.484: DEBUG/dalvikvm(1385): GC freed 22 objects / 1456 bytes in 80ms
      06-08 14:38:51.534: INFO/dalvikvm-heap(1385): Grow heap (frag case) to 5.119MB for 1048592-byte allocation
      06-08 14:38:51.724: DEBUG/dalvikvm(1385): GC freed 159 objects / 7136 bytes in 191ms
      06-08 14:38:51.734: INFO/System.out(1385): Data created: {id=1 com.gtug.test.MemoryTest$Data@4372b7e8}
      06-08 14:38:51.825: DEBUG/dalvikvm(1385): GC freed 20 objects / 1352 bytes in 92ms
      06-08 14:38:51.865: INFO/dalvikvm-heap(1385): Grow heap (frag case) to 6.112MB for 1048592-byte allocation
      06-08 14:38:51.965: DEBUG/dalvikvm(1385): GC freed 36 objects / 18336 bytes in 96ms
      06-08 14:38:51.974: INFO/System.out(1385): Data created: {id=2 com.gtug.test.MemoryTest$Data@43734338}
      06-08 14:38:52.054: DEBUG/dalvikvm(1385): GC freed 20 objects / 1344 bytes in 87ms
      06-08 14:38:52.105: INFO/dalvikvm-heap(1385): Grow heap (frag case) to 7.112MB for 1048592-byte allocation
      06-08 14:38:52.204: DEBUG/dalvikvm(1385): GC freed 0 objects / 0 bytes in 98ms
      06-08 14:38:52.214: INFO/System.out(1385): Data created: {id=3 com.gtug.test.MemoryTest$Data@4372a968}
      06-08 14:38:52.285: INFO/System.out(1385): Data created: {id=4 com.gtug.test.MemoryTest$Data@4372a498}
      06-08 14:38:52.375: DEBUG/dalvikvm(1385): GC freed 44 objects / 2888 bytes in 92ms
      06-08 14:38:52.455: INFO/System.out(1385): Data created: {id=5 com.gtug.test.MemoryTest$Data@43736180}
      06-08 14:38:52.565: DEBUG/dalvikvm(1385): GC freed 25 objects / 1464 bytes in 102ms
      06-08 14:38:52.635: INFO/System.out(1385): Data created: {id=6 com.gtug.test.MemoryTest$Data@4372b950}
      06-08 14:38:52.755: DEBUG/dalvikvm(1385): GC freed 28 objects / 1584 bytes in 116ms
      06-08 14:38:52.804: INFO/ActivityManager(578): Displayed activity com.gtug.test/.PhantomTest: 2327 ms
      06-08 14:38:52.875: INFO/System.out(1385): Data created: {id=7 com.gtug.test.MemoryTest$Data@4373b680}
      06-08 14:38:53.065: DEBUG/dalvikvm(1385): GC freed 26 objects / 1600 bytes in 179ms
      06-08 14:38:53.104: INFO/System.out(1385): Data created: {id=8 com.gtug.test.MemoryTest$Data@437381a8}
      06-08 14:38:53.185: DEBUG/dalvikvm(1385): GC freed 24 objects / 1504 bytes in 74ms
      06-08 14:38:53.224: INFO/System.out(1385): Data created: {id=9 com.gtug.test.MemoryTest$Data@4373aec8}
      06-08 14:38:53.235: INFO/System.out(1385): occupied mem 2 = 12600240
      06-08 14:38:53.235: INFO/System.out(1385): remove strong ref to object 0 and 1
      06-08 14:38:53.244: INFO/System.out(1385): thread started
      06-08 14:38:53.244: INFO/System.out(1385): objects created
      06-08 14:38:56.254: INFO/System.out(1385): run gc() -> overrided finalyze print
      06-08 14:38:56.324: DEBUG/dalvikvm(1385): GC freed 45 objects / 2512 bytes in 74ms
      06-08 14:38:56.334: INFO/System.out(1385): Data finalized: {id=1 com.gtug.test.MemoryTest$Data@4372b7e8}
      06-08 14:38:56.334: INFO/System.out(1385): Data finalized: {id=0 com.gtug.test.MemoryTest$Data@4373e658}
      06-08 14:38:58.184: DEBUG/dalvikvm(622): GC freed 119 objects / 5872 bytes in 79ms
      06-08 14:38:59.344: INFO/System.out(1385): occupied mem 3 = 12602504
      06-08 14:38:59.344: INFO/System.out(1385): run gc() second time to make object enqueued
      06-08 14:38:59.434: DEBUG/dalvikvm(1385): GC freed 45 objects / 2944 bytes in 85ms
      06-08 14:38:59.434: INFO/System.out(1385): >> removed ref = java.lang.ref.PhantomReference@4373a4c0
      06-08 14:38:59.434: INFO/System.out(1385): I see dead objects! --> {id=1 com.gtug.test.MemoryTest$Data@4372b7e8}
      06-08 14:38:59.434: INFO/System.out(1385): >> removed ref = java.lang.ref.PhantomReference@437343f8
      06-08 14:38:59.434: INFO/System.out(1385): I see dead objects! --> {id=0 com.gtug.test.MemoryTest$Data@4373e658}
      06-08 14:39:02.446: INFO/System.out(1385): collect memory
      06-08 14:39:02.524: DEBUG/dalvikvm(1385): GC freed 75 objects / 2101968 bytes in 76ms
      06-08 14:39:07.554: INFO/System.out(1385): occupied mem 4 = 10503728
      06-08 14:39:08.454: INFO/System.out(1385): thread ended<!--more-->
      

      happy coding!

      //DL

      © 2017 Android Tales

      Theme by Anders NorenUp ↑