AttributeCache.java revision 98bf12f99989ba2550fac83ee48ecbb6f1582f07
1/*
2**
3** Copyright 2007, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18package com.android.server;
19
20import android.content.Context;
21import android.content.pm.ActivityInfo;
22import android.content.pm.PackageManager;
23import android.content.res.Configuration;
24import android.content.res.Resources;
25import android.content.res.TypedArray;
26import android.os.UserHandle;
27import android.util.ArrayMap;
28import android.util.SparseArray;
29
30import com.android.internal.annotations.GuardedBy;
31
32import java.lang.ref.WeakReference;
33
34/**
35 * TODO: This should be better integrated into the system so it doesn't need
36 * special calls from the activity manager to clear it.
37 */
38public final class AttributeCache {
39    private static AttributeCache sInstance = null;
40
41    private final Context mContext;
42
43    @GuardedBy("this")
44    private final ArrayMap<String, WeakReference<Package>> mPackages = new ArrayMap<>();
45    @GuardedBy("this")
46    private final Configuration mConfiguration = new Configuration();
47
48    public final static class Package {
49        public final Context context;
50        private final SparseArray<ArrayMap<int[], Entry>> mMap = new SparseArray<>();
51
52        public Package(Context c) {
53            context = c;
54        }
55    }
56
57    public final static class Entry {
58        public final Context context;
59        public final TypedArray array;
60
61        public Entry(Context c, TypedArray ta) {
62            context = c;
63            array = ta;
64        }
65
66        void recycle() {
67            if (array != null) {
68                array.recycle();
69            }
70        }
71    }
72
73    public static void init(Context context) {
74        if (sInstance == null) {
75            sInstance = new AttributeCache(context);
76        }
77    }
78
79    public static AttributeCache instance() {
80        return sInstance;
81    }
82
83    public AttributeCache(Context context) {
84        mContext = context;
85    }
86
87    public void removePackage(String packageName) {
88        synchronized (this) {
89            final WeakReference<Package> ref = mPackages.remove(packageName);
90            final Package pkg = (ref != null) ? ref.get() : null;
91            if (pkg != null) {
92                if (pkg.mMap != null) {
93                    for (int i = 0; i < pkg.mMap.size(); i++) {
94                        final ArrayMap<int[], Entry> map = pkg.mMap.valueAt(i);
95                        for (int j = 0; j < map.size(); j++) {
96                            map.valueAt(j).recycle();
97                        }
98                    }
99                }
100
101                final Resources res = pkg.context.getResources();
102                res.flushLayoutCache();
103            }
104        }
105    }
106
107    public void updateConfiguration(Configuration config) {
108        synchronized (this) {
109            int changes = mConfiguration.updateFrom(config);
110            if ((changes & ~(ActivityInfo.CONFIG_FONT_SCALE |
111                    ActivityInfo.CONFIG_KEYBOARD_HIDDEN |
112                    ActivityInfo.CONFIG_ORIENTATION)) != 0) {
113                // The configurations being masked out are ones that commonly
114                // change so we don't want flushing the cache... all others
115                // will flush the cache.
116                mPackages.clear();
117            }
118        }
119    }
120
121    public Entry get(String packageName, int resId, int[] styleable, int userId) {
122        synchronized (this) {
123            WeakReference<Package> ref = mPackages.get(packageName);
124            Package pkg = (ref != null) ? ref.get() : null;
125            ArrayMap<int[], Entry> map = null;
126            Entry ent = null;
127            if (pkg != null) {
128                map = pkg.mMap.get(resId);
129                if (map != null) {
130                    ent = map.get(styleable);
131                    if (ent != null) {
132                        return ent;
133                    }
134                }
135            } else {
136                Context context;
137                try {
138                    context = mContext.createPackageContextAsUser(packageName, 0,
139                            new UserHandle(userId));
140                    if (context == null) {
141                        return null;
142                    }
143                } catch (PackageManager.NameNotFoundException e) {
144                    return null;
145                }
146                pkg = new Package(context);
147                mPackages.put(packageName, new WeakReference<>(pkg));
148            }
149
150            if (map == null) {
151                map = new ArrayMap<>();
152                pkg.mMap.put(resId, map);
153            }
154
155            try {
156                ent = new Entry(pkg.context,
157                        pkg.context.obtainStyledAttributes(resId, styleable));
158                map.put(styleable, ent);
159            } catch (Resources.NotFoundException e) {
160                return null;
161            }
162
163            return ent;
164        }
165    }
166}
167
168