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