Tagjava

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

Localized vs locale-independent double

Sometimes we need to have double converted to string in specific format. For example, when this data will be used in system level rather than displayed to user.

Let’s say, we need to send double to server in format like X.XX

The easiest (wrong) way to do this, for example:

String formattedDouble = String.format("%.2f", 0.01f);

Expecting to see “0.01” as output. But actually it will be converted using phone default locale.

On most locales this will work and nobody will notice the difference. However if your default locale is, for example, Russian, decimal delimeter for it is “,” opposing to “.”

In this case we will get “0,01” and the server will return error (evil laugh here) as it expects “.” as delimiter.

So we want to avoid any unexpected locale-specific formatting. We can use US locale to get expected results.

String formattedDouble = String.format(Locale.US, "%.2f", 0.01f)

This will return “0.01” and server will be happy.

Another way is to use DecimalFormat for advanced formatting.

Again, be aware of default locale, doing this:

DecimalFormat decimalFormat = new DecimalFormat("0.00");
String formattedDouble = decimalFormat.format(0.01f);

could result “0,01” if default locale is Russian.

//Anton

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 ↑