您的位置:首页 > 移动开发 > Android开发

Android持久化存储的几种方式

2017-12-12 16:05 302 查看
在Android开发中,数据的持久化是肯定会用到,而持久化的方法大致有五种,分别是文件存储、网络存储、SharedPreferences、Sqlit数据库存储和ContentProvider。这五种存储方式各有各的优点,下面我将一一对它们进行介绍。

1.文件存储

在应用开发中有一个场景,就是从网络中加载图片。我们知道,网络请求都需要时间,如果网络速度不够快,或者没有网络将会造成图片加载速度慢、甚至无法加载成功的问题。在Android中的处理方式其实是对已加载的图片进行一个缓存,包括内存缓存和硬盘缓存两种方式。而硬盘缓存就是运用了在本地文件存储的方式。

我们来看一段代码。

public void getImage(String url){
//从url网络路径解析出文件的名字。
filename=url.substring(url.lastIndexOf("/")+1,url.length());
File file_ima=new File(file_url+filename);
//判断文件是不是存在
if(file_ima.exists()){
//存在直接从本地加载
Toast.makeText(getApplication(),"从本地加载",Toast.LENGTH_LONG).show();
iv_ima.setImageBitmap(BitmapFactory.decodeFile(file_url+filename));
return;
}
//不存在的话,使用下载线程,对图片进行下载,进行存储。
DownloadFile thread=new DownloadFile(mHandler,url,file_url+filename);
thread.start();
}


上面代码,是通过对传入图片的网络路径对图片的名字进行解析,判断该图片在本地文件中是否存在,如果存在,则直接从本地加载。而如果不存在则会调用网络线程对图片进行加载。

public class DownloadFile extends Thread
{
Handler mHandler;
String mUrl;
String mPath;
public DownloadFile(Handler handler, String url, String path){
mHandler=handler;
mUrl=url;
mPath=path;
}

@Override
public void run()
{
super.run();
downLoadFromUrl(mUrl,mPath);
}

public  void downLoadFromUrl(String urlStr, String path) {
if(urlStr==null){
return;
}
URL url = null;
try {
url = new URL(urlStr);
Log.e("music", urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置超时间为3秒
conn.setConnectTimeout(3 * 1000);
//防止屏蔽程序抓取而返回403错误
//conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
//得到输入流
InputStream inputStream = conn.getInputStream();
//获取自己数组
byte[] getData = new byte[1024];
//文件保存位置
File saveDir = new File(Environment.getExternalStorageDirectory().toString()+"/test_1/");
Log.e("jdjj",Environment.getExternalStorageDirectory().toString()+"/test_1/");
if (!saveDir.exists()) {
saveDir.mkdirs();
}
File file = new File(path);
//打开文件输出流
FileOutputStream fos = new FileOutputStream(file);
int i=0;
int count=0;
//从输入流里读出,通过输出流写入文件。
while ((count=inputStream.read(getData))!=-1) {
fos.write(getData,0,count);
}
fos.flush();
if (fos != null) {
//输出流关闭
fos.close();
}
if (inputStream != null) {
//输入流关闭
inputStream.close();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Message message=new Message();
mHandler.sendMessage(message);

}
}


上面代码也就是网络加载图片的代码。首先是创建一个HttpURLConnection的连接。

为连接做相应的配置。然后通过该连接得到输入流,根据文件路径和文件名,创建一个文件输出流。将字节流写入本地文件。这就完成了对网络图片的本地存储。在一次加载图片的时候,就不会再去进行网络请求,节约了时间与流量。其实在真正图片缓存实现中,并没有这么简单,硬盘缓存文件也与内存缓存一样,有相应的大小和管理机制。有机会我和写一篇图片的三级缓存机制。

我们来看看效果。

点击按钮,第一次加载时从网络加载。



关闭网络再点击一次。就会从本地加载。



由此可见,本地文件存储方式可以用来存储图片、歌曲等各种格式的文件,防止重复从网络加载。

2.网络存储

网络存储是网络应用开发中必不可少部分。比如说每个用户的个人资料,都会在远程的服务器中存储一份,这样才能保证在不同的终端都可以登陆和获取到每个用户的个人资料。

网络存储自然离不开数据和文件的上传,对于用户的基本资料,我们可以把信息封装成Json数据进行上传。对于大文件来说我们需要通过HTTP来进行上传。

1.使用volley框架上传json数据。

HashMap insertnmap = new HashMap();
//将数据封装,转为json数据
insertnmap.put("phonenum", tephone);
insertnmap.put("name", name);
insertnmap.put("password", password);
JSONObject jsonObject = JSONObject.fromObject(insertnmap);
//使用volley框架,设置为post请求方式,上传Json数据
RequestQueue queue = Volley.newRequestQueue(mContext);
JsonObjectRequest sr = new JsonObjectRequest(Method.POST,url1, jsonObject, new com.android.volley.Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject jsonObject) {

}
}, new com.android.volley.Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {

}
});
queue.add(sr);


在服务器端,对json数据进行解析,然后存储在数据库中。

2.上传文件,使用http。

public class UploadFile extends Thread {
final int EVENT_POST_START =1;
final int EVENT_POST_FAILED =2;
final int EVENT_POST_SUCCESS =3;
private Handler mUpdateHandler;
private String uploadUrl;
private String srcPath;

public UploadFile(Handler handler,String uploadUrl,String srcPath){
mUpdateHandler = handler;
this.uploadUrl = uploadUrl;
this.srcPath = srcPath;
}
@Override
public void run() {
super.run();
mUpdateHandler.sendEmptyMessage(EVENT_POST_START);
String end = "\r\n";
String twoHyphens = "--";
String boundary = "******";
try {
URL url = new URL(uploadUrl);
HttpURLConnection httpURLConnection = (HttpURLConnection) url
.openConnection();
// 允许输入输出流
httpURLConnection.setDoInput(true);
httpURLConnection.setDoOutput(true);
httpURLConnection.setUseCaches(false);
// 使用POST方法
httpURLConnection.setRequestMethod("POST");
httpURLConnection.setRequestProperty("Connection", "Keep-Alive");
httpURLConnection.setRequestProperty("Charset", "UTF-8");
httpURLConnection.setRequestProperty("Content-Type",
"multipart/form-data;boundary=" + boundary);
//打开输出流,配置数据头
DataOutputStream dos = new DataOutputStream(
httpURLConnection.getOutputStream());
dos.writeBytes(twoHyphens + boundary + end);
dos.writeBytes("Content-Disposition: form-data; name=\"uploadedfile\"; filename=\""
+ srcPath.substring(srcPath.lastIndexOf("/") + 1)
+ "\""
+ end);
dos.writeBytes(end);
//打开当前文件输入流
FileInputStream fis = new FileInputStream(srcPath);
byte[] buffer = new byte[8192]; // 8k
int count = 0;
// 读取文件,写入服务器中
while ((count = fis.read(buffer)) != -1) {
dos.write(buffer, 0, count);
}
//关闭输入流
fis.close();
//配置数据
dos.writeBytes(end);
dos.writeBytes(twoHyphens + boundary + twoHyphens + end);
dos.flush();
//打开网络输入流
InputStream is = httpURLConnection.getInputStream();
InputStreamReader isr = new InputStreamReader(is, "utf-8");
BufferedReader br = new BufferedReader(isr);
String result = br.readLine();
mUpdateHandler.sendEmptyMessage(EVENT_POST_SUCCESS);
//关闭输出流与输入流
dos.close();
is.close();

} catch (Exception e) {
mUpdateHandler.sendEmptyMessage(EVENT_POST_FAILED);
e.printStackTrace();
}
}
}


3.SharedPreferences

SharedPreferences是一种轻量型的存储方式,他适合用于存储一些简单的参数配置。比如说在游戏中是否打开声音、上一次登录的账号等,这些都可以用SharedPreferences来实现。其实SharedPreferences的原理是使用xml文件来存放数据。比如说我们在res/values下新建一个setting.xml文件。文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="number"></string>
<string name="music">否</string>
</resources>


具体使用步骤:

public class LoginActivity extends AppCompatActivity
{

Button bt_login;
RadioButton rb_music;
EditText et_pass;
EditText et_num;
//声明SharedPreferences,用来读取xml;
SharedPreferences sp;
//声明SharedPreferences.Editor,用来修改xml里面的值。
SharedPreferences.Editor ed;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//与相应的xml文件建立连接,第二个参数是打开的方式。如果不存在将创建。
sp=getSharedPreferences("setting",MODE_WORLD_READABLE);
et_num=(EditText)findViewById(R.id.et_num);
et_pass=(EditText)findViewById(R.id.et_pass);
rb_music=(RadioButton)findViewById(R.id.rb_music);
//直接使用getxxxx方法来获取xml中指定key的值,第二个参数是当不存在这个数据时,会返回的值
if(sp.getString("music","否").equals("是")){
rb_music.setChecked(true);
et_num.setText(sp.getString("number",""));
}else {
rb_music.setChecked(false);
}
bt_login=(Button)findViewById(R.id.bt_login);
bt_login.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
//得到指定xml的SharedPreferences.Editor
ed=sp.edit();
if(rb_music.isChecked()){
//修改指定xml的值
ed.putString("music","是");
ed.putString("number",et_num.getText().toString());
}else {
ed.putString("music","否");
ed.putString("number","");
}
//记住,这里一定要commit才能生效。
ed.commit();
}
});

}
}


具体使用方法都在上面代码中可以看到,这里要提一点,在打开指定xml时,调用了getSharedPreferences(“setting”,MODE_WORLD_READABLE)这个方法,其中第一个就是当前要打开xml的名字。而第二个参数就是以什么样的方式打开xml,一共有四种方式:

1. MODE_APPEND: 追加方式存储

2. MODE_PRIVATE: 私有方式存储,其他应用无法访问

3. MODE_WORLD_READABLE: 表示当前文件可以被其他应用读取

4. MODE_WORLD_WRITEABLE: 表示当前文件可以被其他应用写入

如果采用3与4两种方式创建或打开的话。这个Preferences将可以被其他应用打开,打开的时候其他应用需要得到当前应用的Context。然后通过Context.getSharedPreferences()打开。

4.Sqlit数据库存储和ContentProvider

为什么要将Sqlit与ContentProvider放一起呢?首先我们说说什么是ContentProvider:

ContentProvider

ContentProvider属于Android四大组件之一。从名字上来翻译的话,叫做内容提供者。它的作用主要是用来与其他应用进行数据交互的。举个列子来说,在QQ中拥有一个日程的入口,进入后我们可以在日历上标注某个日子的备注,用来提醒自己。然后我们打开系统的日历,发现刚才在qq中添加的备注,日历上也得到了显示。其实在这里就可以使用ContentProvider来完成。

所以就如同上面所说,如果我们希望得到其他应用的一些私密数据,而且其他应用愿意给我们提供这些数据的话,我们就可以使用ContentProvider这个组件来完成这个功能。

那么ContentProvider与sqlit有什么关系呢?其实ContentProvider只是用来提供内容或者修改内容的接口。而具体如何去获取数据、去哪里获取数据我们还是需要依靠其他的数据获取的方式。在ContentProvider内部,我们可以通过网络去得到当前应用的用户资料,可以通过SharedPreferences得到当前应用的配置,也可以通过文件来得到某些资源。所以它当然也可以通过Sqlit来操作底层的数据库。

因此我想将ContentProvider与sqlit数据库一起来讲,方便大家理解与使用。

具体请看下一篇文章:ContentProvider封装sqlit数据库
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 存储