每一个Android程序都有一个特有的,只能由本程序自己访问的私有目录,这个目录一般位于/data/data/<Package Name>/目录下。所有应用程序私有目录中保存的文件在用户卸载该应用的时候,都会被自动删除掉。
在Android应用程序开发中,经常使用的一些系统提供的API,会在这个私有目录中创建一些子目录,并将相应的数据存放于其中。而这些对于应用程序开发者来说,完全是透明的,本文将对所有会访问应用程序私有目录的API及其行为做一个简单的总结。
程序私有目录下,一般会有下面几个子目录,它们的用处和涉及到的API(多半继承自Context类)如下:
一、files目录
这个目录是用来存放应用程序自己在安装过后创建的文件的地方。有下面几个API都会涉及对这个目录的操作:
File Context.getFilesDir();
该方法返回指向/data/data/<Package Name>/files/目录的一个File对象。
FileInputStream Context.openFileInput(String name);
该方法将在/data/data/<Package Name>/files/目录下打开一个指定名字的文件,这个文件只能用来读取,不能用来写入。函数将返回一个FileInputStream类的实例。
FileOutputStream Context.openFileOutput(String name, int mode);
该方法将在/data/data/<Package Name>/files/目录下打开一个指定名字的文件,这个文件可以用来写入。如果这个文件不存在,会新创建一个。函数将返回一个FileOutputStream类的实例。
这个函数的参数除了文件名之外,还有一个所谓的模式(mode)。它主要用来指定创建文件的读写权限以及当文件已经存在时,如何写入文件。主要有下面几个值:
1) MODE_PRIVATE(0x00000000),表示创建的文件只能被本程序或者共享同一用户ID的其它应用程序访问到,别的程序既没有权限读也没有权限写,这个是默认模式;
2) MODE_WORLD_READABLE(0x00000001),表示创建的文件可以被任何程序读,但是不能修改;
3) MODE_WORLD_WRITABLE(0x00000002),表示任何程序都可以修改创建文件的内容,出于对安全方面的考虑,从Android 4.2.2(API Level 17)开始,这种模式已经被舍弃了;
4) MODE_APPEND(0x00008000),表示如果要写入的文件已经存在了,则将写入的内容追加到文件末尾,而不是覆盖原来的内容。
如果想联合使用以上的多个模式,可以用“+”将它们联接起来。
如果想查看/data/data/<Package Name>/files/目录下到底有哪些文件,可以用下面的函数:
String[] Context.fileList();
该函数将返回包含/data/data/<Package Name>/files/目录下所有现存文件文件名的字符串数组。
如果想删除一个在/data/data/<Package Name>/files/目录下的文件,可以用下面的函数:
boolean Context.deleteFile(String name);
如果指定名字的文件被删除成功将返回true,反之返回false。
二、cache目录
这个目录是默认用来给应用程序做数据缓存的,在程序中可以通过下面的API获得这个目录的路径:
File Context.getCacheDir();
该方法返回指向/data/data/<Package Name>/cache/目录的一个File对象。
当内部存储空间变得紧张的时候,存于该目录下的所有文件都有可能被Android系统自动删除掉。
三、databases目录
该目录主要用来存放应用程序自己创建或使用的SQL Lite数据库文件。有下面几个API都会涉及对这个目录的操作:
SQLiteDatabase Context.openOrCreateDatabase(String name, int mode, CursorFactory factory);
该函数将在/data/data/<Package Name>/databases/目录下,查找指定名字的数据库文件,如果没有的话,会创建一个新的。创建数据库的时候,也需要指定模式,有如下几种:
1)MODE_PRIVATE(0x00000000),作用和前面介绍的相同;
2)MODE_WORLD_READABLE(0x00000001),作用和前面介绍的相同;
3)MODE_WORLD_WRITABLE(0x00000002),作用和前面介绍的相同;
4)MODE_ENABLE_WRITE_AHEAD_LOGGING(0x00000008),这个模式是从Android3.0(API Level 11)开始加入进来的。将打开数据库的预写日志功能,提高数据的可靠性。
同时,从Android 4.0(API Level 15)开始,还提供了一个android.database.sqlite.SQLiteOpenHelper抽象类来帮助操作SQL Lite数据库。一般用法是自己实现一个类,并继承自SQLiteOpenHelper类,重载onCreate(数据库第一次被创建)、onUpgrade(数据库升级)和onOpen(数据库被打开)方法,加入自己的逻辑。接着构造出一个你自己实现类的实例,并调用下面的函数,打开或创建出数据库:
-
SQLiteDatabase SQLiteOpenHelper.getReadableDatabase();
-
SQLiteDatabase SQLiteOpenHelper.getWritableDatabase();
前面的函数获得的数据库只能用来读,而后面的函数获得的数据库既可以读也可以写。
其实,这个帮助类最终也是通过调用了前面的Context. openOrCreateDatabase函数来打开或创建数据库,并且是使用默认的MODE_PRIVATE模式。因此,通过SQLiteOpenHelper类,最终也是在/data/data/<Package Name>/databases/目录下创建了数据库文件,并且别的程序不能访问到。
File Context.getDatabasePath(String name);
该方法将返回一个指向指定数据库文件的File对象。
如果想查看/data/data/<Package Name>/ databases/目录下到底有哪些数据库,可以用下面的函数:
String[] Context.databaseList();
该函数将返回包含/data/data/<Package Name>/databases/目录下所有数据库名的字符串数组。
如果想删除一个在/data/data/<Package Name>/databases/目录下的数据库,可以用下面的函数:
boolean Context.deleteDatabase(String name);
如果指定名字的数据库文件被删除成功将返回true,反之返回false。
四、shared_prefs目录
该目录主要用来存放应用程序自己创建或使用的一些常用配置文件(Shared Preference)。
程序中主要是用下面的函数来获取一个配置文件:
SharedPreferences Context.getSharedPreferences(String name, int mode);
函数的第一个参数是配置文件的名字,函数调用成功之后会在/data/data/<Package Name>/shared_prefs/目录下创建一个名字为<name>.xml的文件。
函数的第二个参数是创建的模式,有下面几个值:
1)MODE_PRIVATE(0x00000000),作用和前面介绍的相同;
2)MODE_WORLD_READABLE(0x00000001),作用和前面介绍的相同;
3)MODE_WORLD_WRITABLE(0x00000002),作用和前面介绍的相同;
4)MODE_MULTI_PROCESS(0x00000004),如果你的应用程序有多个进程,并且都需要操作同一个配置文件的话,出于数据同步方面的考虑,在每次修改之前都需要检查一下这个配置文件是否已经被别的进程修改了。
五、lib目录
这个子目录存放着的是应用程序需要使用的Native原生程序,即JNI调用需要加载的.so文件。
这些文件一般是位于apk安装包里的lib目录下。在程序安装的时候,系统会将所有apk文件中lib目录下的所有原生程序拷贝到这个子目录下。
该目录是程序不能操作的,不能在程序运行时,向该目录拷贝.so文件,然后再通过调用System.loadLibrary函数将其加载进来。
六、自定义目录
当然,程序也可以在自己的私有目录中创建任意名字的子目录:
File Context.getDir(String name, int mode);
该方法返回指向/data/data/<Package Name>/下指定名称的文件夹的一个File对象。如果该文件夹不存在则用指定名称创建一个新的文件夹。
该目录下还有一些其它的目录,如app_sslcache、app_webview等,这些都是有Android系统中的某些特殊模块自动创建的,一般可以通过其名字猜到是由哪个模块创建的。
例子:
在files目录下创建文件夹111和文件aaa:
int myProcessID = Process.myPid();//import android.os.Process
File filepath = MainActivity.this.getFilesDir();//this.getCacheDir();
String filepathstr = filepath.toString();
Log.d(TAG, "filepathstr= "+filepath.toString());//打印的是:filepathstr= /data/user/0/com.example.myapplication/files
//创建文件夹 111
File f = new File(filepathstr, "111");
Log.d(TAG, "fpathstr= "+f.toString());//fpathstr= /data/user/0/com.example.myapplication/files/111
Log.d(TAG, "f.getAbsolutePath= "+f.getAbsolutePath());//f.getAbsolutePath= /data/user/0/com.example.myapplication/files/111
//以上两个方法获取的path是一样的
try {
//下面这行代码打印结果 f.getAbsolutePath= /data/data/com.example.myapplication/files/111
Log.d(TAG, "f.getAbsolutePath= "+f.getCanonicalPath());
} catch (IOException e) {
e.printStackTrace();
}
if(!f.exists()){
Log.e(TAG, "onCreate: !f.exists()");
f.mkdirs();//创建文件夹
}
else if(f.exists()) {
Log.e(TAG, "onCreate: f.exists()");
}
//创建文件 aaa
File f2 = new File(filepathstr, "aaa");
try {
f2.createNewFile();//创建文件
//file.mkdirs();
Log.d(TAG, "onClick: file.createNewFile()");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}