From 740da4753a878bfa2103ca3d4f487bdbfa75f445 Mon Sep 17 00:00:00 2001 From: Adam Powell Date: Fri, 7 Nov 2014 14:51:16 -0800 Subject: [PATCH] Don't register more receivers and observers for every DateTimeView SystemUI uses several of these per notification, so be a little more conservative and track individual views to update per process instead. Bug 16902706 Change-Id: Ib77a8e7727d027cae39d5e6f431cac1d1ff8a121 --- core/java/android/widget/DateTimeView.java | 126 +++++++++++++++++++---------- 1 file changed, 85 insertions(+), 41 deletions(-) diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java index 45d1403bc598..443884ada8c1 100644 --- a/core/java/android/widget/DateTimeView.java +++ b/core/java/android/widget/DateTimeView.java @@ -32,6 +32,7 @@ import android.widget.RemoteViews.RemoteView; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; // @@ -62,8 +63,8 @@ public class DateTimeView extends TextView { int mLastDisplay = -1; DateFormat mLastFormat; - private boolean mAttachedToWindow; private long mUpdateTimeMillis; + private static final ThreadLocal sReceiverInfo = new ThreadLocal(); public DateTimeView(Context context) { super(context); @@ -76,15 +77,21 @@ public class DateTimeView extends TextView { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - registerReceivers(); - mAttachedToWindow = true; + ReceiverInfo ri = sReceiverInfo.get(); + if (ri == null) { + ri = new ReceiverInfo(); + sReceiverInfo.set(ri); + } + ri.addView(this); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - unregisterReceivers(); - mAttachedToWindow = false; + final ReceiverInfo ri = sReceiverInfo.get(); + if (ri != null) { + ri.removeView(this); + } } @android.view.RemotableViewMethod @@ -204,49 +211,86 @@ public class DateTimeView extends TextView { } } - private void registerReceivers() { - Context context = getContext(); + void clearFormatAndUpdate() { + mLastFormat = null; + update(); + } - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_TIME_TICK); - filter.addAction(Intent.ACTION_TIME_CHANGED); - filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); - filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); - context.registerReceiver(mBroadcastReceiver, filter); + private static class ReceiverInfo { + private final ArrayList mAttachedViews = new ArrayList(); + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_TIME_TICK.equals(action)) { + if (System.currentTimeMillis() < getSoonestUpdateTime()) { + // The update() function takes a few milliseconds to run because of + // all of the time conversions it needs to do, so we can't do that + // every minute. + return; + } + } + // ACTION_TIME_CHANGED can also signal a change of 12/24 hr. format. + updateAll(); + } + }; - Uri uri = Settings.System.getUriFor(Settings.System.DATE_FORMAT); - context.getContentResolver().registerContentObserver(uri, true, mContentObserver); - } + private final ContentObserver mObserver = new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + updateAll(); + } + }; - private void unregisterReceivers() { - Context context = getContext(); - context.unregisterReceiver(mBroadcastReceiver); - context.getContentResolver().unregisterContentObserver(mContentObserver); - } + public void addView(DateTimeView v) { + final boolean register = mAttachedViews.isEmpty(); + mAttachedViews.add(v); + if (register) { + register(v.getContext().getApplicationContext()); + } + } - private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (Intent.ACTION_TIME_TICK.equals(action)) { - if (System.currentTimeMillis() < mUpdateTimeMillis) { - // The update() function takes a few milliseconds to run because of - // all of the time conversions it needs to do, so we can't do that - // every minute. - return; + public void removeView(DateTimeView v) { + mAttachedViews.remove(v); + if (mAttachedViews.isEmpty()) { + unregister(v.getContext().getApplicationContext()); + } + } + + void updateAll() { + final int count = mAttachedViews.size(); + for (int i = 0; i < count; i++) { + mAttachedViews.get(i).clearFormatAndUpdate(); + } + } + + long getSoonestUpdateTime() { + long result = Long.MAX_VALUE; + final int count = mAttachedViews.size(); + for (int i = 0; i < count; i++) { + final long time = mAttachedViews.get(i).mUpdateTimeMillis; + if (time < result) { + result = time; } } - // ACTION_TIME_CHANGED can also signal a change of 12/24 hr. format. - mLastFormat = null; - update(); + return result; } - }; - private ContentObserver mContentObserver = new ContentObserver(new Handler()) { - @Override - public void onChange(boolean selfChange) { - mLastFormat = null; - update(); + void register(Context context) { + final IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_TIME_TICK); + filter.addAction(Intent.ACTION_TIME_CHANGED); + filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); + filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + context.registerReceiver(mReceiver, filter); + + final Uri uri = Settings.System.getUriFor(Settings.System.DATE_FORMAT); + context.getContentResolver().registerContentObserver(uri, true, mObserver); } - }; + + void unregister(Context context) { + context.unregisterReceiver(mReceiver); + context.getContentResolver().unregisterContentObserver(mObserver); + } + } } -- 2.11.0