15. Reading and Writing Files

MIDP devices often have some kind of hierarchical file system. Data lives in files that belong to directories. Directories can contain other directories. File system roots contain all the directories.

JSR 75, the PDA Optional Packages, defines a FileConnection API that provides access to a device’s file system. It is a mandatory part of MSA and MSA subset.

For the actual work of reading files and writing files, you use the familiar stream classes from java.io. The FileConnection API gives you the streams you need from the device’s file system.

Remember, though, that using FileConnection means your application must be signed. If you are sure you need the FileConnection API, make sure your project budget includes the time and money you’ll need to make signing happen.

15.1. The Quick Story

Let’s say you want to read some data from the file system. First, you have to create a string that represents the filename. FileConnection strings always begin with file://. The rest of the string is an absolute file path.

To create a FileConnection object, you use something called the Generic Connection Framework (GCF). You’ll learn all about it in Chapter 18. All you need to know now is that when you pass a file URL to the open() method in javax.microedition.io.Connector, you get back a FileConnection object.

For example, let’s say you wanted to read from a file /root1/photos/mango. png. You would do something like this to make the initial connection:

Image

The FileConnection object can supply an InputStream for reading or an OutputStream for writing. Here is how you can get an InputStream for reading from the file:

Image

Now you can read from in as much as you like. When you’re done, close everything up as usual. Use try and finally blocks to make sure the input stream and the FileConnection are closed no matter what happens. In this example, use nested blocks like this:

Image

15.2. Working with Files and Directories

FileConnection inherits methods from StreamConnection for retrieving streams:

Image

The real mojo of FileConnection, however, is its additional methods that pertain directly to files and directories.

You can create files and directories with create() and mkdir() respectively. You must first create a FileConnection with the URL of the file or directory you wish to create. You can check to see if the file or directory already exists by calling exists(). Then call create() or mkdir() as appropriate. Here is a simple method that creates a file and writes some text into it:

Image

You can remove a file or a directory by calling delete(). Use rename() to assign a new name to a file or directory.

One interesting method is truncate(), which chops off the contents of the file after the specified number of bytes.

To find out if a FileConnection represents a file or directory, call isDirectory(). To list the contents of a directory, use one of the list() methods. Each returns an Enumeration of strings. Each string in the Enumeration represents a file or directory. The directory names have a trailing slash (/).

The size of the files in a directory is computed by directorySize(false). If you call directorySize(true) instead, you’ll get the size of all files, including those in subdirectories.

You can use an existing directory FileConnection to navigate to a contained file or subdirectory. Call setFileConnection() with the name of the file or subdirectory, or use .. for the parent directory.

Files may or may not be readable or writable. Use canRead() and canWrite() to check, or modify these attributes with setReadable() and setWritable().

Some file systems also support the concept of hidden files, which are files that exist but are not normally listed in directory listings. Use isHidden() and setHidden() to test or modify this attribute.

The size of a file is returned by fileSize(). If the file system supports it, you can find out the last time a file was changed with lastModified().

Three additional methods supply information about the file system itself: totalSize(), usedSize(), and availableSize().

15.3. Somewhere, a Place for Us

MSA guarantees that a MIDlet suite has its own private directory that can be used for storage. As with record stores, this directory should not be visible to other MIDlet suites or applications.

The URL for the directory is given by the system property fileconn.dir. private. A human-readable name for this directory is contained in the system property fileconn.dir.private.name.

In the Sun Java Wireless Toolkit, use Run via OTA to test FileConnection MIDlets. When run this way, the emulator will supply a URL for fileconn.dir.private where you can store information.

15.4. Finding Pictures, Music, and Other Goodies

MSA defines a handful of system properties that point the way to interesting directories on a device. Each system property, if it is not null, contains a URL for a directory. The property names are self-explanatory:

  • fileconn.dir.photos

  • fileconn.dir.videos

  • fileconn.dir.graphics

  • fileconn.dir.tones

  • fileconn.dir.music

  • fileconn.dir.recordings

For example, on the Sun Java Wireless Toolkit emulator, the fileconn.dir. photos property contains file:///root1/photos, a default location for photograph files. Any of these properties can be null, which means the device does not have a default location for the corresponding item.

URLs and pathnames are confusing to most users. If you need to represent one of these locations on the screen, you should use a descriptive name instead. MSA defines a separate set of system properties that contain descriptive names in a language appropriate for the device. Just append .name to the system property names above to retrieve the corresponding descriptive name.

For example, on Sun’s emulator, the property fileconn.dir.music.name is Music. On a German MSA device, it might be Musik.

15.5. Starting from the Top

Instead of examining “favorite” locations like the ones described previously, you might want to navigate through the file system by starting at the roots. Each root is usually a physical device, like a memory card or a hard disk drive.

FileSystemRegistry contains a static method, listRoots(), which returns an Enumeration of strings, one per root. Each string is a full URL that can be passed to Connector.open().

If you want to display the roots to a user, you should use descriptive names instead. The system property fileconn.dir.roots.names contains these descriptive names, separated by semicolons. The descriptive names are returned in the same order as in the Enumeration returned by FileSystemRegistry.listRoots().

File systems can come and go, particularly in devices that have slots for memory cards. To receive notifications, create an implementation of FileSystemListener and pass it to FileSystemRegistry.addFileSystemListener(). Your listener will be notified when roots arrive and depart via the rootChanged() method. Check the API documentation for all the details.

Unless you have a really good reason, you should not expose your users to the complexities of a hierarchical file system. If you can help it, you probably should shield them from the file system roots, even the nice display names. Whenever possible, just store things in the MIDlet suite’s private directory and don’t bother the user about the details. Applications should just work. Users on mobile phones will be even less understanding and patient than users on desktop computers. If your application is the slightest bit difficult or inconvenient, users will discard it and seek out a simpler solution.

15.6. Ask for Permission

Reading and writing local files is, of course, a sensitive operation. If you plan to distribute an application that makes use of the file system, make sure you request one or both of the file-related permissions in the application descriptor:

Image

Sign your MIDlet suite so that the user will not be drowned in prompts for access to the file system.

15.7. An Example

The FileExerciserMIDlet shows how to create a directory, create a file, write and read a file, remove a file, and remove a directory. It will give you a start in reading and writing files in your own applications.

Image

Image

Image

You can run this MIDlet directly in the Sun Java Wireless Toolkit emulator, but you’ll be inundated with prompts to allow access to the file system. To get around the prompts, register the read and write permissions, sign the MIDlet suite, and use Run via OTA to get the full experience.

You’ll also notice that the example uses a different root directory depending on whether you run it directly or use Run via OTA. When run directly, the private directory is not defined, so the application uses the first available root. When using Run via OTA, the private directory is available (see Figure 15.1).

Figure 15.1. Using the private application directory for file storage

Image

15.8. Summary

The FileConnection API provides your application with the ability to manipulate files and directories. You can read files, write files, create directories, and remove directories. You can examine the file system roots available on a device and receive notifications when roots come and go. MSA provides some system properties that point to commonly used file directories on a device, like directories for pictures, music, or videos. To avoid lots of security prompts, include the file permissions in your application descriptor and cryptographically sign your MIDlet suite.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.122.68