AssetManager.java revision 2ba4efff8bece69f95401768e48db1b20afd077d
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.content.res; 18 19import android.os.ParcelFileDescriptor; 20import android.util.Log; 21import android.util.TypedValue; 22 23import java.io.FileNotFoundException; 24import java.io.IOException; 25import java.io.InputStream; 26import java.util.HashMap; 27 28/** 29 * Provides access to an application's raw asset files; see {@link Resources} 30 * for the way most applications will want to retrieve their resource data. 31 * This class presents a lower-level API that allows you to open and read raw 32 * files that have been bundled with the application as a simple stream of 33 * bytes. 34 */ 35public final class AssetManager { 36 /* modes used when opening an asset */ 37 38 /** 39 * Mode for {@link #open(String, int)}: no specific information about how 40 * data will be accessed. 41 */ 42 public static final int ACCESS_UNKNOWN = 0; 43 /** 44 * Mode for {@link #open(String, int)}: Read chunks, and seek forward and 45 * backward. 46 */ 47 public static final int ACCESS_RANDOM = 1; 48 /** 49 * Mode for {@link #open(String, int)}: Read sequentially, with an 50 * occasional forward seek. 51 */ 52 public static final int ACCESS_STREAMING = 2; 53 /** 54 * Mode for {@link #open(String, int)}: Attempt to load contents into 55 * memory, for fast small reads. 56 */ 57 public static final int ACCESS_BUFFER = 3; 58 59 private static final String TAG = "AssetManager"; 60 private static final boolean localLOGV = false || false; 61 62 private static final boolean DEBUG_REFS = false; 63 64 private static final Object sSync = new Object(); 65 /*package*/ static AssetManager sSystem = null; 66 67 private final TypedValue mValue = new TypedValue(); 68 private final long[] mOffsets = new long[2]; 69 70 // For communication with native code. 71 private int mObject; 72 private int mNObject; // used by the NDK 73 74 private StringBlock mStringBlocks[] = null; 75 76 private int mNumRefs = 1; 77 private boolean mOpen = true; 78 private HashMap<Integer, RuntimeException> mRefStacks; 79 80 /** 81 * Create a new AssetManager containing only the basic system assets. 82 * Applications will not generally use this method, instead retrieving the 83 * appropriate asset manager with {@link Resources#getAssets}. Not for 84 * use by applications. 85 * {@hide} 86 */ 87 public AssetManager() { 88 synchronized (this) { 89 if (DEBUG_REFS) { 90 mNumRefs = 0; 91 incRefsLocked(this.hashCode()); 92 } 93 init(); 94 if (localLOGV) Log.v(TAG, "New asset manager: " + this); 95 ensureSystemAssets(); 96 } 97 } 98 99 private static void ensureSystemAssets() { 100 synchronized (sSync) { 101 if (sSystem == null) { 102 AssetManager system = new AssetManager(true); 103 system.makeStringBlocks(false); 104 sSystem = system; 105 } 106 } 107 } 108 109 private AssetManager(boolean isSystem) { 110 if (DEBUG_REFS) { 111 synchronized (this) { 112 mNumRefs = 0; 113 incRefsLocked(this.hashCode()); 114 } 115 } 116 init(); 117 if (localLOGV) Log.v(TAG, "New asset manager: " + this); 118 } 119 120 /** 121 * Return a global shared asset manager that provides access to only 122 * system assets (no application assets). 123 * {@hide} 124 */ 125 public static AssetManager getSystem() { 126 ensureSystemAssets(); 127 return sSystem; 128 } 129 130 /** 131 * Close this asset manager. 132 */ 133 public void close() { 134 synchronized(this) { 135 //System.out.println("Release: num=" + mNumRefs 136 // + ", released=" + mReleased); 137 if (mOpen) { 138 mOpen = false; 139 decRefsLocked(this.hashCode()); 140 } 141 } 142 } 143 144 /** 145 * Retrieve the string value associated with a particular resource 146 * identifier for the current configuration / skin. 147 */ 148 /*package*/ final CharSequence getResourceText(int ident) { 149 synchronized (this) { 150 TypedValue tmpValue = mValue; 151 int block = loadResourceValue(ident, (short) 0, tmpValue, true); 152 if (block >= 0) { 153 if (tmpValue.type == TypedValue.TYPE_STRING) { 154 return mStringBlocks[block].get(tmpValue.data); 155 } 156 return tmpValue.coerceToString(); 157 } 158 } 159 return null; 160 } 161 162 /** 163 * Retrieve the string value associated with a particular resource 164 * identifier for the current configuration / skin. 165 */ 166 /*package*/ final CharSequence getResourceBagText(int ident, int bagEntryId) { 167 synchronized (this) { 168 TypedValue tmpValue = mValue; 169 int block = loadResourceBagValue(ident, bagEntryId, tmpValue, true); 170 if (block >= 0) { 171 if (tmpValue.type == TypedValue.TYPE_STRING) { 172 return mStringBlocks[block].get(tmpValue.data); 173 } 174 return tmpValue.coerceToString(); 175 } 176 } 177 return null; 178 } 179 180 /** 181 * Retrieve the string array associated with a particular resource 182 * identifier. 183 * @param id Resource id of the string array 184 */ 185 /*package*/ final String[] getResourceStringArray(final int id) { 186 String[] retArray = getArrayStringResource(id); 187 return retArray; 188 } 189 190 191 /*package*/ final boolean getResourceValue(int ident, 192 int density, 193 TypedValue outValue, 194 boolean resolveRefs) 195 { 196 int block = loadResourceValue(ident, (short) density, outValue, resolveRefs); 197 if (block >= 0) { 198 if (outValue.type != TypedValue.TYPE_STRING) { 199 return true; 200 } 201 outValue.string = mStringBlocks[block].get(outValue.data); 202 return true; 203 } 204 return false; 205 } 206 207 /** 208 * Retrieve the text array associated with a particular resource 209 * identifier. 210 * @param id Resource id of the string array 211 */ 212 /*package*/ final CharSequence[] getResourceTextArray(final int id) { 213 int[] rawInfoArray = getArrayStringInfo(id); 214 int rawInfoArrayLen = rawInfoArray.length; 215 final int infoArrayLen = rawInfoArrayLen / 2; 216 int block; 217 int index; 218 CharSequence[] retArray = new CharSequence[infoArrayLen]; 219 for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) { 220 block = rawInfoArray[i]; 221 index = rawInfoArray[i + 1]; 222 retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null; 223 } 224 return retArray; 225 } 226 227 /*package*/ final boolean getThemeValue(int theme, int ident, 228 TypedValue outValue, boolean resolveRefs) { 229 int block = loadThemeAttributeValue(theme, ident, outValue, resolveRefs); 230 if (block >= 0) { 231 if (outValue.type != TypedValue.TYPE_STRING) { 232 return true; 233 } 234 StringBlock[] blocks = mStringBlocks; 235 if (blocks == null) { 236 ensureStringBlocks(); 237 blocks = mStringBlocks; 238 } 239 outValue.string = blocks[block].get(outValue.data); 240 return true; 241 } 242 return false; 243 } 244 245 /*package*/ final void ensureStringBlocks() { 246 if (mStringBlocks == null) { 247 synchronized (this) { 248 if (mStringBlocks == null) { 249 makeStringBlocks(true); 250 } 251 } 252 } 253 } 254 255 /*package*/ final void makeStringBlocks(boolean copyFromSystem) { 256 final int sysNum = copyFromSystem ? sSystem.mStringBlocks.length : 0; 257 final int num = getStringBlockCount(); 258 mStringBlocks = new StringBlock[num]; 259 if (localLOGV) Log.v(TAG, "Making string blocks for " + this 260 + ": " + num); 261 for (int i=0; i<num; i++) { 262 if (i < sysNum) { 263 mStringBlocks[i] = sSystem.mStringBlocks[i]; 264 } else { 265 mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true); 266 } 267 } 268 } 269 270 /*package*/ final CharSequence getPooledString(int block, int id) { 271 //System.out.println("Get pooled: block=" + block 272 // + ", id=#" + Integer.toHexString(id) 273 // + ", blocks=" + mStringBlocks); 274 return mStringBlocks[block-1].get(id); 275 } 276 277 /** 278 * Open an asset using ACCESS_STREAMING mode. This provides access to 279 * files that have been bundled with an application as assets -- that is, 280 * files placed in to the "assets" directory. 281 * 282 * @param fileName The name of the asset to open. This name can be 283 * hierarchical. 284 * 285 * @see #open(String, int) 286 * @see #list 287 */ 288 public final InputStream open(String fileName) throws IOException { 289 return open(fileName, ACCESS_STREAMING); 290 } 291 292 /** 293 * Open an asset using an explicit access mode, returning an InputStream to 294 * read its contents. This provides access to files that have been bundled 295 * with an application as assets -- that is, files placed in to the 296 * "assets" directory. 297 * 298 * @param fileName The name of the asset to open. This name can be 299 * hierarchical. 300 * @param accessMode Desired access mode for retrieving the data. 301 * 302 * @see #ACCESS_UNKNOWN 303 * @see #ACCESS_STREAMING 304 * @see #ACCESS_RANDOM 305 * @see #ACCESS_BUFFER 306 * @see #open(String) 307 * @see #list 308 */ 309 public final InputStream open(String fileName, int accessMode) 310 throws IOException { 311 synchronized (this) { 312 if (!mOpen) { 313 throw new RuntimeException("Assetmanager has been closed"); 314 } 315 int asset = openAsset(fileName, accessMode); 316 if (asset != 0) { 317 AssetInputStream res = new AssetInputStream(asset); 318 incRefsLocked(res.hashCode()); 319 return res; 320 } 321 } 322 throw new FileNotFoundException("Asset file: " + fileName); 323 } 324 325 public final AssetFileDescriptor openFd(String fileName) 326 throws IOException { 327 synchronized (this) { 328 if (!mOpen) { 329 throw new RuntimeException("Assetmanager has been closed"); 330 } 331 ParcelFileDescriptor pfd = openAssetFd(fileName, mOffsets); 332 if (pfd != null) { 333 return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); 334 } 335 } 336 throw new FileNotFoundException("Asset file: " + fileName); 337 } 338 339 /** 340 * Return a String array of all the assets at the given path. 341 * 342 * @param path A relative path within the assets, i.e., "docs/home.html". 343 * 344 * @return String[] Array of strings, one for each asset. These file 345 * names are relative to 'path'. You can open the file by 346 * concatenating 'path' and a name in the returned string (via 347 * File) and passing that to open(). 348 * 349 * @see #open 350 */ 351 public native final String[] list(String path) 352 throws IOException; 353 354 /** 355 * {@hide} 356 * Open a non-asset file as an asset using ACCESS_STREAMING mode. This 357 * provides direct access to all of the files included in an application 358 * package (not only its assets). Applications should not normally use 359 * this. 360 * 361 * @see #open(String) 362 */ 363 public final InputStream openNonAsset(String fileName) throws IOException { 364 return openNonAsset(0, fileName, ACCESS_STREAMING); 365 } 366 367 /** 368 * {@hide} 369 * Open a non-asset file as an asset using a specific access mode. This 370 * provides direct access to all of the files included in an application 371 * package (not only its assets). Applications should not normally use 372 * this. 373 * 374 * @see #open(String, int) 375 */ 376 public final InputStream openNonAsset(String fileName, int accessMode) 377 throws IOException { 378 return openNonAsset(0, fileName, accessMode); 379 } 380 381 /** 382 * {@hide} 383 * Open a non-asset in a specified package. Not for use by applications. 384 * 385 * @param cookie Identifier of the package to be opened. 386 * @param fileName Name of the asset to retrieve. 387 */ 388 public final InputStream openNonAsset(int cookie, String fileName) 389 throws IOException { 390 return openNonAsset(cookie, fileName, ACCESS_STREAMING); 391 } 392 393 /** 394 * {@hide} 395 * Open a non-asset in a specified package. Not for use by applications. 396 * 397 * @param cookie Identifier of the package to be opened. 398 * @param fileName Name of the asset to retrieve. 399 * @param accessMode Desired access mode for retrieving the data. 400 */ 401 public final InputStream openNonAsset(int cookie, String fileName, int accessMode) 402 throws IOException { 403 synchronized (this) { 404 if (!mOpen) { 405 throw new RuntimeException("Assetmanager has been closed"); 406 } 407 int asset = openNonAssetNative(cookie, fileName, accessMode); 408 if (asset != 0) { 409 AssetInputStream res = new AssetInputStream(asset); 410 incRefsLocked(res.hashCode()); 411 return res; 412 } 413 } 414 throw new FileNotFoundException("Asset absolute file: " + fileName); 415 } 416 417 public final AssetFileDescriptor openNonAssetFd(String fileName) 418 throws IOException { 419 return openNonAssetFd(0, fileName); 420 } 421 422 public final AssetFileDescriptor openNonAssetFd(int cookie, 423 String fileName) throws IOException { 424 synchronized (this) { 425 if (!mOpen) { 426 throw new RuntimeException("Assetmanager has been closed"); 427 } 428 ParcelFileDescriptor pfd = openNonAssetFdNative(cookie, 429 fileName, mOffsets); 430 if (pfd != null) { 431 return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); 432 } 433 } 434 throw new FileNotFoundException("Asset absolute file: " + fileName); 435 } 436 437 /** 438 * Retrieve a parser for a compiled XML file. 439 * 440 * @param fileName The name of the file to retrieve. 441 */ 442 public final XmlResourceParser openXmlResourceParser(String fileName) 443 throws IOException { 444 return openXmlResourceParser(0, fileName); 445 } 446 447 /** 448 * Retrieve a parser for a compiled XML file. 449 * 450 * @param cookie Identifier of the package to be opened. 451 * @param fileName The name of the file to retrieve. 452 */ 453 public final XmlResourceParser openXmlResourceParser(int cookie, 454 String fileName) throws IOException { 455 XmlBlock block = openXmlBlockAsset(cookie, fileName); 456 XmlResourceParser rp = block.newParser(); 457 block.close(); 458 return rp; 459 } 460 461 /** 462 * {@hide} 463 * Retrieve a non-asset as a compiled XML file. Not for use by 464 * applications. 465 * 466 * @param fileName The name of the file to retrieve. 467 */ 468 /*package*/ final XmlBlock openXmlBlockAsset(String fileName) 469 throws IOException { 470 return openXmlBlockAsset(0, fileName); 471 } 472 473 /** 474 * {@hide} 475 * Retrieve a non-asset as a compiled XML file. Not for use by 476 * applications. 477 * 478 * @param cookie Identifier of the package to be opened. 479 * @param fileName Name of the asset to retrieve. 480 */ 481 /*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName) 482 throws IOException { 483 synchronized (this) { 484 if (!mOpen) { 485 throw new RuntimeException("Assetmanager has been closed"); 486 } 487 int xmlBlock = openXmlAssetNative(cookie, fileName); 488 if (xmlBlock != 0) { 489 XmlBlock res = new XmlBlock(this, xmlBlock); 490 incRefsLocked(res.hashCode()); 491 return res; 492 } 493 } 494 throw new FileNotFoundException("Asset XML file: " + fileName); 495 } 496 497 /*package*/ void xmlBlockGone(int id) { 498 synchronized (this) { 499 decRefsLocked(id); 500 } 501 } 502 503 /*package*/ final int createTheme() { 504 synchronized (this) { 505 if (!mOpen) { 506 throw new RuntimeException("Assetmanager has been closed"); 507 } 508 int res = newTheme(); 509 incRefsLocked(res); 510 return res; 511 } 512 } 513 514 /*package*/ final void releaseTheme(int theme) { 515 synchronized (this) { 516 deleteTheme(theme); 517 decRefsLocked(theme); 518 } 519 } 520 521 protected void finalize() throws Throwable { 522 try { 523 if (DEBUG_REFS && mNumRefs != 0) { 524 Log.w(TAG, "AssetManager " + this 525 + " finalized with non-zero refs: " + mNumRefs); 526 if (mRefStacks != null) { 527 for (RuntimeException e : mRefStacks.values()) { 528 Log.w(TAG, "Reference from here", e); 529 } 530 } 531 } 532 destroy(); 533 } finally { 534 super.finalize(); 535 } 536 } 537 538 public final class AssetInputStream extends InputStream { 539 /** 540 * @hide 541 */ 542 public final int getAssetInt() { 543 return mAsset; 544 } 545 /** 546 * @hide 547 */ 548 public final long getNativeAsset() { 549 return mAsset; 550 } 551 private AssetInputStream(int asset) 552 { 553 mAsset = asset; 554 mLength = getAssetLength(asset); 555 } 556 public final int read() throws IOException { 557 return readAssetChar(mAsset); 558 } 559 public final boolean markSupported() { 560 return true; 561 } 562 public final int available() throws IOException { 563 long len = getAssetRemainingLength(mAsset); 564 return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len; 565 } 566 public final void close() throws IOException { 567 synchronized (AssetManager.this) { 568 if (mAsset != 0) { 569 destroyAsset(mAsset); 570 mAsset = 0; 571 decRefsLocked(hashCode()); 572 } 573 } 574 } 575 public final void mark(int readlimit) { 576 mMarkPos = seekAsset(mAsset, 0, 0); 577 } 578 public final void reset() throws IOException { 579 seekAsset(mAsset, mMarkPos, -1); 580 } 581 public final int read(byte[] b) throws IOException { 582 return readAsset(mAsset, b, 0, b.length); 583 } 584 public final int read(byte[] b, int off, int len) throws IOException { 585 return readAsset(mAsset, b, off, len); 586 } 587 public final long skip(long n) throws IOException { 588 long pos = seekAsset(mAsset, 0, 0); 589 if ((pos+n) > mLength) { 590 n = mLength-pos; 591 } 592 if (n > 0) { 593 seekAsset(mAsset, n, 0); 594 } 595 return n; 596 } 597 598 protected void finalize() throws Throwable 599 { 600 close(); 601 } 602 603 private int mAsset; 604 private long mLength; 605 private long mMarkPos; 606 } 607 608 /** 609 * Add an additional set of assets to the asset manager. This can be 610 * either a directory or ZIP file. Not for use by applications. Returns 611 * the cookie of the added asset, or 0 on failure. 612 * {@hide} 613 */ 614 public final int addAssetPath(String path) { 615 int res = addAssetPathNative(path); 616 return res; 617 } 618 619 private native final int addAssetPathNative(String path); 620 621 /** 622 * Add multiple sets of assets to the asset manager at once. See 623 * {@link #addAssetPath(String)} for more information. Returns array of 624 * cookies for each added asset with 0 indicating failure, or null if 625 * the input array of paths is null. 626 * {@hide} 627 */ 628 public final int[] addAssetPaths(String[] paths) { 629 if (paths == null) { 630 return null; 631 } 632 633 int[] cookies = new int[paths.length]; 634 for (int i = 0; i < paths.length; i++) { 635 cookies[i] = addAssetPath(paths[i]); 636 } 637 638 return cookies; 639 } 640 641 /** 642 * Determine whether the state in this asset manager is up-to-date with 643 * the files on the filesystem. If false is returned, you need to 644 * instantiate a new AssetManager class to see the new data. 645 * {@hide} 646 */ 647 public native final boolean isUpToDate(); 648 649 /** 650 * Change the locale being used by this asset manager. Not for use by 651 * applications. 652 * {@hide} 653 */ 654 public native final void setLocale(String locale); 655 656 /** 657 * Get the locales that this asset manager contains data for. 658 */ 659 public native final String[] getLocales(); 660 661 /** 662 * Change the configuation used when retrieving resources. Not for use by 663 * applications. 664 * {@hide} 665 */ 666 public native final void setConfiguration(int mcc, int mnc, String locale, 667 int orientation, int touchscreen, int density, int keyboard, 668 int keyboardHidden, int navigation, int screenWidth, int screenHeight, 669 int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, 670 int screenLayout, int uiMode, int majorVersion); 671 672 /** 673 * Retrieve the resource identifier for the given resource name. 674 */ 675 /*package*/ native final int getResourceIdentifier(String type, 676 String name, 677 String defPackage); 678 679 /*package*/ native final String getResourceName(int resid); 680 /*package*/ native final String getResourcePackageName(int resid); 681 /*package*/ native final String getResourceTypeName(int resid); 682 /*package*/ native final String getResourceEntryName(int resid); 683 684 private native final int openAsset(String fileName, int accessMode); 685 private final native ParcelFileDescriptor openAssetFd(String fileName, 686 long[] outOffsets) throws IOException; 687 private native final int openNonAssetNative(int cookie, String fileName, 688 int accessMode); 689 private native ParcelFileDescriptor openNonAssetFdNative(int cookie, 690 String fileName, long[] outOffsets) throws IOException; 691 private native final void destroyAsset(int asset); 692 private native final int readAssetChar(int asset); 693 private native final int readAsset(int asset, byte[] b, int off, int len); 694 private native final long seekAsset(int asset, long offset, int whence); 695 private native final long getAssetLength(int asset); 696 private native final long getAssetRemainingLength(int asset); 697 698 /** Returns true if the resource was found, filling in mRetStringBlock and 699 * mRetData. */ 700 private native final int loadResourceValue(int ident, short density, TypedValue outValue, 701 boolean resolve); 702 /** Returns true if the resource was found, filling in mRetStringBlock and 703 * mRetData. */ 704 private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue, 705 boolean resolve); 706 /*package*/ static final int STYLE_NUM_ENTRIES = 6; 707 /*package*/ static final int STYLE_TYPE = 0; 708 /*package*/ static final int STYLE_DATA = 1; 709 /*package*/ static final int STYLE_ASSET_COOKIE = 2; 710 /*package*/ static final int STYLE_RESOURCE_ID = 3; 711 /*package*/ static final int STYLE_CHANGING_CONFIGURATIONS = 4; 712 /*package*/ static final int STYLE_DENSITY = 5; 713 /*package*/ native static final boolean applyStyle(int theme, 714 int defStyleAttr, int defStyleRes, int xmlParser, 715 int[] inAttrs, int[] outValues, int[] outIndices); 716 /*package*/ native final boolean retrieveAttributes( 717 int xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); 718 /*package*/ native final int getArraySize(int resource); 719 /*package*/ native final int retrieveArray(int resource, int[] outValues); 720 private native final int getStringBlockCount(); 721 private native final int getNativeStringBlock(int block); 722 723 /** 724 * {@hide} 725 */ 726 public native final String getCookieName(int cookie); 727 728 /** 729 * {@hide} 730 */ 731 public native static final int getGlobalAssetCount(); 732 733 /** 734 * {@hide} 735 */ 736 public native static final String getAssetAllocations(); 737 738 /** 739 * {@hide} 740 */ 741 public native static final int getGlobalAssetManagerCount(); 742 743 private native final int newTheme(); 744 private native final void deleteTheme(int theme); 745 /*package*/ native static final void applyThemeStyle(int theme, int styleRes, boolean force); 746 /*package*/ native static final void copyTheme(int dest, int source); 747 /*package*/ native static final int loadThemeAttributeValue(int theme, int ident, 748 TypedValue outValue, 749 boolean resolve); 750 /*package*/ native static final void dumpTheme(int theme, int priority, String tag, String prefix); 751 752 private native final int openXmlAssetNative(int cookie, String fileName); 753 754 private native final String[] getArrayStringResource(int arrayRes); 755 private native final int[] getArrayStringInfo(int arrayRes); 756 /*package*/ native final int[] getArrayIntResource(int arrayRes); 757 758 private native final void init(); 759 private native final void destroy(); 760 761 private final void incRefsLocked(int id) { 762 if (DEBUG_REFS) { 763 if (mRefStacks == null) { 764 mRefStacks = new HashMap<Integer, RuntimeException>(); 765 RuntimeException ex = new RuntimeException(); 766 ex.fillInStackTrace(); 767 mRefStacks.put(this.hashCode(), ex); 768 } 769 } 770 mNumRefs++; 771 } 772 773 private final void decRefsLocked(int id) { 774 if (DEBUG_REFS && mRefStacks != null) { 775 mRefStacks.remove(id); 776 } 777 mNumRefs--; 778 //System.out.println("Dec streams: mNumRefs=" + mNumRefs 779 // + " mReleased=" + mReleased); 780 if (mNumRefs == 0) { 781 destroy(); 782 } 783 } 784} 785