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