1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * http://www.apache.org/licenses/LICENSE-2.0 7 * Unless required by applicable law or agreed to in writing, software 8 * distributed under the License is distributed on an "AS IS" BASIS, 9 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 * See the License for the specific language governing permissions and 11 * limitations under the License. 12 */ 13 14package android.databinding.testapp; 15 16import android.databinding.testapp.databinding.LeakTestBinding; 17import android.test.ActivityInstrumentationTestCase2; 18import android.util.Log; 19import android.widget.FrameLayout; 20 21import java.lang.ref.WeakReference; 22import java.util.ArrayList; 23 24public class LeakTest extends ActivityInstrumentationTestCase2<TestActivity> { 25 WeakReference<LeakTestBinding> mWeakReference = new WeakReference<LeakTestBinding>(null); 26 27 public LeakTest() { 28 super(TestActivity.class); 29 } 30 31 @Override 32 protected void setUp() throws Exception { 33 super.setUp(); 34 35 try { 36 getActivity().runOnUiThread(new Runnable() { 37 @Override 38 public void run() { 39 try { 40 LeakTestBinding binding = LeakTestBinding.inflate( 41 getActivity().getLayoutInflater()); 42 getActivity().setContentView(binding.getRoot()); 43 mWeakReference = new WeakReference<LeakTestBinding>(binding); 44 binding.setName("hello world"); 45 binding.executePendingBindings(); 46 } catch (Exception e) { 47 e.printStackTrace(); 48 throw e; 49 } 50 } 51 }); 52 getInstrumentation().waitForIdleSync(); 53 } catch (Throwable t) { 54 throw new Exception(t); 55 } 56 } 57 58 public void testBindingLeak() throws Throwable { 59 assertNotNull(mWeakReference.get()); 60 runTestOnUiThread(new Runnable() { 61 @Override 62 public void run() { 63 getActivity().setContentView(new FrameLayout(getActivity())); 64 } 65 }); 66 WeakReference<Object> canary = new WeakReference<Object>(new Object()); 67 ArrayList<WeakReference<byte[]>> leak = new ArrayList<>(); 68 while (canary.get() != null) { 69 leak.add(new WeakReference<byte[]>(new byte[100])); 70 System.gc(); 71 } 72 assertNull(mWeakReference.get()); 73 } 74 75 // Test to ensure that when the View is detached that it doesn't rebind 76 // the dirty Views. The rebind should happen only after the root view is 77 // reattached. 78 public void testNoChangeWhenDetached() throws Throwable { 79 final LeakTestBinding binding = mWeakReference.get(); 80 final AnimationWatcher watcher = new AnimationWatcher(); 81 82 runTestOnUiThread(new Runnable() { 83 @Override 84 public void run() { 85 getActivity().setContentView(new FrameLayout(getActivity())); 86 binding.setName("goodbye world"); 87 getActivity().getWindow().getDecorView().postOnAnimation(watcher); 88 } 89 }); 90 91 watcher.waitForAnimationThread(); 92 93 runTestOnUiThread(new Runnable() { 94 @Override 95 public void run() { 96 assertEquals("hello world", binding.textView.getText().toString()); 97 getActivity().setContentView(binding.getRoot()); 98 getActivity().getWindow().getDecorView().postOnAnimation(watcher); 99 } 100 }); 101 102 watcher.waitForAnimationThread(); 103 104 runTestOnUiThread(new Runnable() { 105 @Override 106 public void run() { 107 assertEquals("goodbye world", binding.textView.getText().toString()); 108 } 109 }); 110 } 111 112 private static class AnimationWatcher implements Runnable { 113 private boolean mWaiting = true; 114 115 public void waitForAnimationThread() throws InterruptedException { 116 synchronized (this) { 117 while (mWaiting) { 118 this.wait(); 119 } 120 mWaiting = true; 121 } 122 } 123 124 125 @Override 126 public void run() { 127 synchronized (this) { 128 mWaiting = false; 129 this.notifyAll(); 130 } 131 } 132 } 133} 134