四大数据存储方式:
一. SharedPreferences
1. 保存:
SharedPreferences sharedPreferences = getApplicationContext().getSharedPreferences("xml_file_name", Context.MODE_PRIVATE); Editor editor = sharedPreferences.edit();//获取编辑器 editor.putString("key", "value");//还可以保存Set<String>,int,long,float,boolean editor.commit();//提交修改
默认保存在:/data/data/<package name>/shared_prefs/xml_file_name.xml
2. 读取:
SharedPreferences sharedPreferences = getApplicationContext().getSharedPreferences("xml_file_name", Context.MODE_PRIVATE); String value = sharedPreferences.getString( "key", "" );
注意:
如果应用A要访问应用B的SharedPreferences,必须先获取应用B的Context,通过B的Context来获取SharedPreferences,才会在应用B所在包下的shared_prefs目录找到XML文件。
二. File
1. 内部资源文件,只读:
InputStream in = getResources().openRawResource(R.raw.test); //得到资源中的Raw数据流
InputStream in = getResources().getAssets().open(fileName); //得到资源中的asset数据流
2. 可以读写的文件:
String filePath = getFilesDir().getPath(); //路径:/data/data/<package name>/files
String CachePath = getCacheDir().getPath(); //路径:/data/data/<package name>/cache,换存目录有可能被系统删除
String sdPath = Environment.getExternalStorageDirectory().getPath() // 路径:/mnt/sdCard
private String read( boolean savedInExternal, String path ){ String result = new String(); FileInputStream in = null; try{ if( savedInExternal ) { //SD卡中的文件使用FileInputStream和FileOutputStream进行文件的操作 in = new FileInputStream(path); } else { //存放在数据区(/data/data/..)的文件只能使用openFileOutput和openFileInput进行操作 in = openFileInput(path); } int len = in.available(); if( len > 0 ){ byte[] buffer = new byte[len]; in.read(buffer); result = EncodingUtils.getString(buffer, "UTF-8"); } }catch(Exception e){ e.printStackTrace(); }finally{ if (in != null) { try { in.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } return result; } private void write(boolean savedInExternal, String path, String text){ FileOutputStream out = null; try{ if(savedInExternal){ out = new FileOutputStream(path); }else{ out = openFileOutput(path, MODE_PRIVATE); } byte[] buffer = text.getBytes(); out.write(buffer); }catch(Exception e){ e.printStackTrace(); }finally{ if(out!=null){ try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } }
3.RandomAccessFile同时将FileInputStream和FileOutputStream整合到一起,而且支持访问文件任意字节处读或写数据,常用于断点续传保存文件。
定位用的getFilePointer()
在文件里移动用的seek()
判断文件大小的length()
跳过多少字节数skipBytes()
注意:read()和write(),都是从文件当前位置开始读和写。
//打开文件时,FilePointer指向文件起始位置。 RandomAccessFile r = new RandomAccessFile(file,"rwd"); // 将String转byte[],在文件起始位置写入byte[]。 String f = "123456789"; byte[] buffer = f.getBytes("UTF-8"); r.write(buffer); // 验证:让FilePointer指向文件起始位置。 r.seek(0); // 从文件起始位置开始读byte[],然后转String。 long len = r.length(); byte[] dst = new byte[(int) len]; r.read(dst); String f_read = new String(dst,"UTF-8");//结果:123456789 // 让FilePointer指向文件末尾位置,在末尾处接着写入byte[]。 r.seek(len); r.write(buffer); // 验证:让FilePointer指向文件起始位置。从文件起始位置开始读byte[]。 r.seek(0); len = r.length(); byte[] dst2 = new byte[(int) len]; r.read(dst2); f_read = new String(dst2,"UTF-8");//结果:123456789123456789 // 关闭 r.close();
三. 数据库(默认路径:/data/data/<package name>/databases/)
在Android平台上,集成了一个嵌入式关系型数据库—SQLite,支持 NULL、INTEGER、REAL(浮点数字)、TEXT(字符串文本)和BLOB(二进制对象)。
1. SQLiteOpenHelper 是用来管理数据库的,因为是抽象类,所以必须自定义子类来继承它。
SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version)的构造函数中,name表示数据库的名字,默认路径是:/data/data/<package name>/databases/name,可以自定义数据库路径赋值给name,将数据库保存在指定路径下,比如SD卡下。
提供了获取SQLiteDatabase的接口:getReadableDatabase(),getWritableDatabase()。
2. SQLiteDatabase 操作SQLite数据库。
execSQL()接口,主要是执行无需返回数据的操作:
CREATE TABLE IF NOT EXISTS table_name (id INTERGER PRIMARY KEY, content TEXT)
DROP TABLE IF EXISTS table_name
ALTER TABLE table_name ADD COLUMN add_content TEXT
下面的SQL方法也可以用execSQL()执行,也可以使用SQLiteDatabase对应接口。
INSERT INTO table_name (k1,k2) VALUES (v1,v2)
UPDATE table_name SET k1=v1 WHERE 条件
DELETE FROM table_name WHERE 条件
rawQuery()接口,主要是执行返回结果的操作,返回值是结果集游标Cursor:
SELECT * FROM table_name WHERE 条件 GROUP BY 分组 HAVING 分组条件 ORDER BY 排序
public class DatabaseHelper extends SQLiteOpenHelper { private static int DATABASE_VERSION = 1; public DatabaseHelper(Context context, String dbPath) { super(context, dbPath, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } public class DatabaseUtil { private static final String DATABASE_DIR = "database_dir"; private static final String DATABASE_NAME = "database.db"; private Context mContext; private DatabaseHelper mDatabaseHelper; private String mDatabaseDirPath; private String mDatabaseFullPath; private static DatabaseUtil mInstance; //单例模式 public static DatabaseUtil getInstance(Context c) { if( mInstance == null ) { mInstance = new DatabaseUtil(c); } return mInstance; } private DatabaseUtil (Context c) { mContext = c.getApplicationContext(); StringBuffer path = new StringBuffer(); if ( Environment.getExternalStorageState().equalsIgnoreCase(Environment.MEDIA_MOUNTED) ) { File file = Environment.getExternalStorageDirectory(); path.append(file.getPath()); } else { path.append(mContext.getFilesDir()); } path.append(File.separator); path.append(DATABASE_DIR); mDatabaseDirPath = path.toString(); File dir = new File(mDatabaseDirPath); if(!dir.exists()){ dir.mkdir(); } path.append(File.separator); path.append(DATABASE_NAME); mDatabaseFullPath = path.toString(); mDatabaseHelper = new DatabaseHelper(mContext, mDatabaseFullPath); } public void createDatabaseTable(String table){ SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); db.beginTransaction();//开始事务 try{ StringBuffer str = new StringBuffer(); str.append("CREATE TABLE IF NOT EXISTS "); str.append(table); str.append(" (id INTERGER PRIMARY KEY, content TEXT)"); db.execSQL(str.toString()); for(int i = 0; i < 200; ++i){ StringBuffer buf = new StringBuffer(); buf.append("数据:"); buf.append(i+1); StringBuffer s = new StringBuffer(); s.append("INSERT INTO "); s.append(table); s.append(" (id,content) values (?,?)"); db.execSQL(s.toString(), new Object[]{i+1, buf.toString()}); } db.setTransactionSuccessful();//设置事务的标志为成功 } finally { db.endTransaction();//由事务的标志决定是提交事务,还是回滚事务 db.close(); } } public void deleteDatabaseTable(String table) { SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); StringBuffer str = new StringBuffer(); str.append("DROP TABLE IF EXISTS "); str.append(table); db.execSQL(str.toString()); db.close(); } public ArrayList<String> getDataFromDatabaseTable(String table){ ArrayList<String> list = new ArrayList<String>(); SQLiteDatabase db = mDatabaseHelper.getReadableDatabase(); StringBuffer str = new StringBuffer(); str.append("SELECT * FROM "); str.append(table); Cursor c = db.rawQuery(str.toString(),null); while(c.moveToNext()){ int content_index = c.getColumnIndex("content"); String content = c.getString(content_index); list.add(content); } c.close(); db.close(); return list; } }
四. ContentProvider 向其他应用共享其数据,其他应用通过ContentResolver来访问这些数据。
Android系统提供的常见Content Provider,可以在源码src\android\provider目录中找到。
数据传递:
一. Activity, Service, BroadcastReceiver通过Intent对象来实现:
Intent的功能包括启动四大组件以及相关信息+传递数据。
其中传递数据Intent提供了putExtra和对应的getExtra方法,所以无需再创建一个bundle对象。
对应自定义数据结构,可以封装成Serializable和Parcelable来实现序列化。
但是,数据类型有限,比如遇到不可序列化的数据Bitmap,InputStream,或者LinkList链表等数据类型就不太好。
1. Serializable使用IO读写存储在外存上,在序列化时会产生大量的临时变量,从而引起频繁的GC,适用于保存对象的字节序列到本地文件或数据库中,以及网络传输。
自定义数据结构只需要实现Serializable接口,并提供一个序列化版本id(serialVersionUID)即可。
使用ObjectOutputStream.writeObject()可以将自定义数据写入文件流或网络流。
使用ObjectInputStream.readObject()可以从文件流或网络流中读取自定义数据。
2. Parcelable是直接在内存中读写,是通过IBinder通信的消息的载体,只适用进程间传递对象。
需要实现writeToParcel、describeContents函数以及静态的CREATOR变量,务必保持写入和读取的类型及顺序一致,否则会发生异常。
二. 利用Application定义的全局变量:
A通过getApplication()对全局变量进行值的修改,B可以访问Application的值。修改全局变量的代码段需要用synchronized包裹来实现同步,防止多线程同时修改。
三. 通过单例模式实现:
可以保证Application中的这个类有且只有一个实例。在A中设置参数,在B中直接访问了。
public class Singleton { //单例模式实例 private static Singleton instance = null; //synchronized 用于线程安全,防止多线程同时创建实例 public synchronized static Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } private HashMap<String, Object> mMap; public Singleton() { mMap = new HashMap<String,Object>(); } public void put(String key,Object value){ mMap.put(key,value); } public Object get(String key) { return mMap.get(key); } }
四. 通过Handler实现线程间的消息传递:
Handler类的主要作用有两个:
1.在新启动的线程中发送消息。
2.在主线程中获取、处理消息。
五. 基于外部存储的传输,使用SharedPreferences,File,SQLite,针对第三方应用需要ContentProvider+ContentResolver。
六. 基于IPC通信机制:
1. 定义AIDL接口文件实现进程间通信:
为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现,使用接口定义语言(Interface Definition Language,IDL)来公开服务的接口。
建立AIDL服务的步骤:
(1)在工程的Java包目录中建立一个扩展名为aidl的文件。每个文件只能定义一个接口,而且只能是接口的声明和方法的声明。
(2)如果aidl文件的内容是正确的,刷新工程,ADT会在gen包下自动生成一个Java接口文件(*.java)
(3)建立一个服务类(Service的子类),定义了一个内嵌类来实现.aidl文件定义的Java接口。
(4)在AndroidManifest.xml文件中配置AIDL服务,尤其要注意的是,<action>标签中android:name的属性值就是客户端要引用该服务的ID,也就是Intent类的参数值。如果服务端service定义了android:process=":remote",代表在应用程序里,当需要该service时,会自动创建新的进程。而如果是android:process="remote",没有“:”分号的,则创建全局进程,不同的应用程序共享该进程。
客户端获取接口:
(1)将服务端自动生成的IMyService.java文件连同包目录一起复制到客户端工程的src目录中(R文件除外).
(2)客户端通过ServiceConnection获取从Service返回的IBinder,bindService以后可调用服务器的接口
2. 通过Messager实现进程间通信:
使用场景:当仅有2个应用要互相通讯时
(1)service 内部需要有一个 Handler 的实现,它被用来处理从每一个 client 发送过的来请求
(2)通过这个 Handler ,来生成一个 Messenger
(3) 在 service 的onBind() 方法中,需要向 client 返回由该 Messenger 生成的一个 IBinder 实例
(4) client 使用从 service 返回的 IBinder 实例来初始化一个 Messenger, 然后使用该 Messenger 与 service 进行通信
(5) service 通过它自身内部的 Handler 实现(Handler 人 handleMessage() 方法中)来处理从 client 发送过来的请求