OSDN Git Service

Fix a memory leak in the Graphical Layout Editor.
authorXavier Ducrohet <xav@android.com>
Fri, 9 Jul 2010 19:48:04 +0000 (12:48 -0700)
committerXavier Ducrohet <xav@android.com>
Fri, 9 Jul 2010 21:18:41 +0000 (14:18 -0700)
The rendering requires a looper, but never actually uses it.
Some views (like scrollView) make use of messages during rendering
putting messages into the looper message queue which was never
read or emptied.

In the case of the scrollview, the message actually contains
a reference to the scrollview, which would then leak (with all
its children views)

The fix is to delete the looper that was created. This must be
done by reflection as there's no public access to it, and this
must be done from ADT so that all versions of layoutlib get
the fix.

Change-Id: I998ad0ec17e77e36a42d77f8ab888917a9ff6441

eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle1/GraphicalLayoutEditor.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java
eclipse/sites/external/.gitignore [new file with mode: 0644]

index 45a56d7..06a7ba4 100644 (file)
@@ -721,6 +721,9 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette
                             configuredProjectResources, frameworkResources, projectCallback,
                             null /* logger */);
 
+                    // post rendering clean up
+                    bridge.cleanUp();
+
                     // update the UiElementNode with the layout info.
                     if (result.getSuccess() == ILayoutResult.SUCCESS) {
                         BufferedImage largeImage = result.getImage();
@@ -1044,6 +1047,9 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette
                                     configuredProjectRes, frameworkResources, mProjectCallback,
                                     mLogger);
 
+                            // post-rendering clean up
+                            bridge.cleanUp();
+
                             // update the UiElementNode with the layout info.
                             if (result.getSuccess() == ILayoutResult.SUCCESS) {
                                 model.setEditData(result.getImage());
index fbfea59..591282b 100755 (executable)
@@ -1085,6 +1085,9 @@ public class GraphicalEditorPart extends EditorPart
                                     configuredProjectRes, frameworkResources, mProjectCallback,
                                     mLogger);
 
+                            // post rendering clean up
+                            bridge.cleanUp();
+
                             mCanvasViewer.getCanvas().setResult(result);
 
                             // update the UiElementNode with the layout info.
index abd7cf8..dd1f054 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.ide.eclipse.adt.internal.sdk;
 
+import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
 import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
 import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors;
@@ -28,6 +29,7 @@ import com.android.layoutlib.api.ILayoutBridge;
 import com.android.sdklib.IAndroidTarget;
 import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
 
+import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Hashtable;
 import java.util.Map;
@@ -55,6 +57,27 @@ public class AndroidTargetData {
         public ClassLoader classLoader;
 
         public int apiLevel;
+
+        /**
+         * Post rendering clean-up that must be done here so that it's done even for older
+         * versions of the layoutlib.
+         */
+        public void cleanUp() {
+            try {
+                Class<?> looperClass = classLoader.loadClass("android.os.Looper"); //$NON-NLS-1$
+                Field threadLocalField = looperClass.getField("sThreadLocal"); //$NON-NLS-1$
+                if (threadLocalField != null) {
+                    threadLocalField.setAccessible(true);
+                    // get object. Field is static so no need to pass an object
+                    ThreadLocal<?> threadLocal = (ThreadLocal<?>) threadLocalField.get(null);
+                    if (threadLocal != null) {
+                        threadLocal.remove();
+                    }
+                }
+            } catch (Exception e) {
+                AdtPlugin.log(e, "Failed to clean up bridge for API level %d", apiLevel);
+            }
+        }
     }
 
     private final IAndroidTarget mTarget;
diff --git a/eclipse/sites/external/.gitignore b/eclipse/sites/external/.gitignore
new file mode 100644 (file)
index 0000000..ccfee1d
--- /dev/null
@@ -0,0 +1,2 @@
+*.jar
+*/*.jar
\ No newline at end of file