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