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.
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:
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:
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:
FileConnection
inherits methods from StreamConnection
for retrieving streams:
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:
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()
.
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.
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
.
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.
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:
Sign your MIDlet suite so that the user will not be drowned in prompts for access to the file system.
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.
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
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.
3.133.122.68