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; 22 23public class LeakTest extends ActivityInstrumentationTestCase2<TestActivity> { 24 WeakReference<LeakTestBinding> mWeakReference = new WeakReference<LeakTestBinding>(null); 25 26 public LeakTest() { 27 super(TestActivity.class); 28 } 29 30 @Override 31 protected void setUp() throws Exception { 32 super.setUp(); 33 34 try { 35 getActivity().runOnUiThread(new Runnable() { 36 @Override 37 public void run() { 38 try { 39 LeakTestBinding binding = LeakTestBinding.inflate( 40 getActivity().getLayoutInflater()); 41 getActivity().setContentView(binding.getRoot()); 42 mWeakReference = new WeakReference<LeakTestBinding>(binding); 43 binding.setName("hello world"); 44 binding.executePendingBindings(); 45 } catch (Exception e) { 46 e.printStackTrace(); 47 throw e; 48 } 49 } 50 }); 51 getInstrumentation().waitForIdleSync(); 52 } catch (Throwable t) { 53 throw new Exception(t); 54 } 55 } 56 57 public void testBindingLeak() throws Throwable { 58 assertNotNull(mWeakReference.get()); 59 runTestOnUiThread(new Runnable() { 60 @Override 61 public void run() { 62 getActivity().setContentView(new FrameLayout(getActivity())); 63 } 64 }); 65 WeakReference<Object> canary = new WeakReference<Object>(new Object()); 66 while (canary.get() != null) { 67 byte[] b = new byte[1024 * 1024]; 68 System.gc(); 69 } 70 assertNull(mWeakReference.get()); 71 } 72 73 // Test to ensure that when the View is detached that it doesn't rebind 74 // the dirty Views. The rebind should happen only after the root view is 75 // reattached. 76 public void testNoChangeWhenDetached() throws Throwable { 77 final LeakTestBinding binding = mWeakReference.get(); 78 final AnimationWatcher watcher = new AnimationWatcher(); 79 80 runTestOnUiThread(new Runnable() { 81 @Override 82 public void run() { 83 getActivity().setContentView(new FrameLayout(getActivity())); 84 binding.setName("goodbye world"); 85 getActivity().getWindow().getDecorView().postOnAnimation(watcher); 86 } 87 }); 88 89 watcher.waitForAnimationThread(); 90 91 runTestOnUiThread(new Runnable() { 92 @Override 93 public void run() { 94 assertEquals("hello world", binding.textView.getText().toString()); 95 getActivity().setContentView(binding.getRoot()); 96 getActivity().getWindow().getDecorView().postOnAnimation(watcher); 97 } 98 }); 99 100 watcher.waitForAnimationThread(); 101 102 runTestOnUiThread(new Runnable() { 103 @Override 104 public void run() { 105 assertEquals("goodbye world", binding.textView.getText().toString()); 106 } 107 }); 108 } 109 110 private static class AnimationWatcher implements Runnable { 111 private boolean mWaiting = true; 112 113 public void waitForAnimationThread() throws InterruptedException { 114 synchronized (this) { 115 while (mWaiting) { 116 this.wait(); 117 } 118 mWaiting = true; 119 } 120 } 121 122 123 @Override 124 public void run() { 125 synchronized (this) { 126 mWaiting = false; 127 this.notifyAll(); 128 } 129 } 130 } 131} 132