Android数据存储——SharedPreferences

时间:2022-06-23 05:30:01

Android数据存储——SharedPreferences

一、数据存储选项:Data Storage ——Storage Options【重点】
1、Shared Preferences
Store private primitive data in key-value pairs.
保存简单的键值对数据。
2、Internal Storage
Store private data on the device memory.
在手机内存中保存不对外共享的信息。
3、External Storage
Store public data on the shared external storage.
在外部存储设备上保存公共的数据信息。主要指保存在SDCard上。
4、SQLite Databases
Store structured data in a private database.
将结构化的数据保存进数据库。
5、Network Connection
Store data on the web with your own network server.
将数据保存到自己的远程服务器上。

二、SharedPreferences:(一)、概念:        SharedPreferences是Android系统提供的一个通用的数据持久化框架,用于存储和读取key-value类型的原始基本数据类型对,目前支持string、int、long、float、boolean等基本类型的存储,对于自定义的对象数据类型,无法使用SharedPreferences来存储。        SharedPreferences主要用于存储系统的配置信息。例如上次登录的用户名,上次最后设置的配置信息(如:是否打开音效、是否使用振动,小游戏的玩家积分等)。当再次启动程序后依然保持原有设置。SharedPreferences用键值对方式存储,方便写入和读取。
(二)、使用SharedPreferences的步骤:
1、获取SharedPreferences对象;         SharedPreferences本身是一个接口,无法直接创建实例,通过Context的getSharedPreferences(String name, int  mode)方法来获取实例。         该方法的第二个参数有以下三个值:【文件读写的操作模式
    • Context.MODE_PRIVATE:  指定该SharedPreferences的数据只能被本应用程序读、写;
    • Context.MODE_APPEND:新内容追加到原内容后;
    • Context.MODE_WORLD_READABLE:  指定 SharedPreferences数据能被其他应用程序读,但是不支持写;
    • Context.MODE_WORLD_WRITEABLE:  指定 SharedPreferences数据能被其他应用程序读、写。会覆盖原数据。 
    • 可以使用  +  连接这些权限
2、调用edit()方法获取SharedPreferences.Editor; 3、通过SharedPreferences.Editor接口提供的put()方法对SharedPreferences进行更新; 4、调用SharedPreferences.Editor的commit()方法,将更新提交到SharedPreferences中。

(三)、核心代码:
button_main_savedata.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                                prefs = getSharedPreferences("myaccount", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
                                editor.putInt("age", 38);
                                editor.putString("username", "wangxiangjun");
                                editor.putString("pwd", "123456");
                                editor.putString("username", "xiangjun");
                                editor.putString("age", "I'm 40 years old!");
                                editor.commit();
                        }
                });
                button_main_readdata.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                                prefs = getSharedPreferences("myaccount", Context.MODE_PRIVATE);
                                String name = prefs.getString("username", "wxj");
                                String pwd = prefs.getString("pwd", "000");
                                int age = prefs.getInt("age", 20);
                                System.out.println("====>" + name + ":" + pwd + ":" + age);
                        }
                });


(四)、保存之后的SharedPreferences数据文件:        SharedPreferences数据总是以xml格式保存在:/data/data/包名/shared_prefs目录下;
例如: <?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map>     <string name="pwd">123456</string>     <string name="username">xiangjun</string>     <int  name="age">20</int> </map>


(五)、SharedPreferences的设置Setting功能:
1、引入:
Android数据存储——SharedPreferences

        手机中常有这样的设置页面,如果做这样的页面呢?是不是需要写一个复杂的布局文件,再写一堆事件监听来完成呢?

2、PreferenceActivity的简单用法:
    1)、步骤:
    • 将setting.xml文件放到res的xml目录下;
    • 将arrays.xml文件放到values目录下;
    • 写一个页面SettingActivity。
    2)、目录结构:
Android数据存储——SharedPreferences

    3)、核心代码:
//在SettingActivity中。不再需要setContentView(R.layout.activity_main)方法来加载布局了。
    protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                // setContentView(R.layout.activity_main);
addPreferencesFromResource(R.xml.setting);  
 //备注:This method was deprecated in API level 11. This function is not relevant for a modern 
//fragment-based PreferenceActivity.这个方法在11版本以上就已经不推荐使用了。
 }


Android数据存储——SDCard

一、上节课回顾:
(一)、SharedPreferences的使用步骤:
(二)、借助SharedPreferences实现黑名单管理:

1、示例代码
publicclass MainActivity extends Activity {
private ListView listView_main_blockList;
private EditText editText_main_number;
private TextView textView_main_emptyinfo;
private SharedPreferences prefs = null;
private Editor editor = null;
private ArrayAdapter<String> adapter = null;

@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);

         editText_main_number = (EditText) findViewById(R.id.editText_main_number);
         listView_main_blockList = (ListView) findViewById(R.id.listView_main_blocklist);
         textView_main_emptyinfo = (TextView) findViewById(R.id.text_main_emptyinfo);

prefs = getSharedPreferences("blocklist", Context.MODE_PRIVATE);
editor = prefs.edit();

         List<String> list = getBlocklist();
adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1, list);

// 注意setEmptyView()的用法。当适配器为空的时候,设置ListView中的展示内容。
listView_main_blockList.setEmptyView(textView_main_emptyinfo);
listView_main_blockList.setAdapter(adapter);
 }

publicvoid clickButton(View view) {
switch (view.getId()) {
case R.id.button_main_add:
                 String mpnumber = editText_main_number.getText().toString();
editor.putString(mpnumber, mpnumber);
editor.commit();
                 fillListView();
break;
case R.id.button_main_clear:
editor.clear();
editor.commit();
                 fillListView();
break;
default:
break;
         }
 }

 /*
        * 获取SharedPreferences中的全部数据,放到List集合中。形成适配器的数据源
        */
private List<String> getBlocklist() {
         List<String> list = new ArrayList<String>();
try {
                 Map<String, ?> map = prefs.getAll();
                 // 增强for循环,实现对Map集合的遍历
for (Map.Entry<String, ?> entry : map.entrySet()) {
                         list.add(entry.getKey());
                 }
return list;
         } catch (Exception e) {
returnnull;
         }
 }

 /*
        * 填充ListView控件,实现刷新显示数据的效果
        */
privatevoid fillListView() {
adapter.clear();
adapter.addAll(getBlocklist());
 }

 @Override
publicboolean onCreateOptionsMenu(Menu menu) {
         getMenuInflater().inflate(R.menu.main, menu);
returntrue;
 }
}
【备注:】ListView的setEmptyView()的用法:要放置在adapter适配器生成之后。

二、External Storage之SDCard操作:
(一)、引入: Android中提供了特有的两个方法来进行IO操作(openFileInput()和openFileOutput() ),但是毕竟手机内置存储空间很有限,为了更好地存储应用程序的大文件数据,需要读写SD卡上的文件。SD卡大大扩充了手机的存储能力。
(二)、读写SD卡的步骤: 1、先判断手机是否有sd卡;         调用Environment的getExternalStorageState()方法判断手机是否插上sdcard。 2、获取sdcard的路径;         调用Environment的getExternalStorageDirectory()方法来获取外部存储器的目录。 3、此外还可以获取SDCard可用磁盘空间的大小(借助StatFs类来实现); 4、清单文件中设置读写sdcard的权限;   <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>   在sdcard中创建与删除文件的权限:   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>   向sdcard写入权限 5、执行读写操作(基本IO流操作)。


(三)、封装SDCard的工具类:SDCardHelper类
publicclass SDCardHelper {
privatestatic String TAG = "SDCardHelper";

 /*
        * 判断sdcard是否挂载
        */
publicstaticboolean isSDCardMounted() {
return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
 }

       /*
        * 获取sdcard绝对物理路径
        */
publicstatic String getSDCardPath() {
if (isSDCardMounted()) {
return Environment.getExternalStorageDirectory().getAbsolutePath();
         } else {
returnnull;
         }
 }

 /*
        * 获取sdcard的全部的空间大小。返回MB字节
        */
publicstaticlong getSDCardSize() {
if (isSDCardMounted()) {
                 StatFs fs = new StatFs(getSDCardPath());
long size = fs.getBlockSize();
long count = fs.getBlockCount();
return size * count / 1024 / 1024;
         }
return 0;
 }
 /*
        * 获取sdcard的剩余的可用空间的大小。返回MB字节
        */
publicstaticlong getSDCardFreeSize() {
if (isSDCardMounted()) {
                 StatFs fs = new StatFs(getSDCardPath());
long size = fs.getBlockSize();
long count = fs.getAvailableBlocks();
return size * count / 1024 / 1024;
         }
return 0;
 }

 /*
        * 将文件(byte[])保存进sdcard指定的路径下
        */
publicstaticboolean saveFileToSDCard(byte[] data, String dir,String filename) {
         BufferedOutputStream bos = null;
if (isSDCardMounted()) {
                 Log.i (TAG, "==isSDCardMounted==TRUE");
                 File file = new File(getSDCardPath() + File.separator + dir);
                 Log.i (TAG, "==file:" + file.toString() + file.exists());
if (!file.exists()) {
boolean flags = file.mkdirs();
                         Log.i (TAG, "==创建文件夹:" + flags);
                 }

try {
                         bos = new BufferedOutputStream(new FileOutputStream(new File(file, filename)));
                         bos.write(data, 0, data.length);
                         bos.flush();
returntrue;
                 } catch (Exception e) {
                         e.printStackTrace();
                 } finally {
try {
                                 bos.close();
                         } catch (IOException e) {
                                 e.printStackTrace();
                         }
                 }
         }
returnfalse;
 }
 /*
        * 已知文件的路径,从sdcard中获取到该文件,返回byte[]
        */
publicstaticbyte[] loadFileFromSDCard(String filepath) {
         BufferedInputStream bis = null;
         ByteArrayOutputStream baos = null;
if (isSDCardMounted()) {
                 File file = new File(filepath);
if (file.exists()) {
try {
                                 baos = new ByteArrayOutputStream();
                                 bis = new BufferedInputStream(new FileInputStream(file));
byte[] buffer = newbyte[1024 * 8];
int c = 0;
while ((c = bis.read(buffer)) != -1) {
                                         baos.write(buffer, 0, c);
                                         baos.flush();
                                 }
return baos.toByteArray();
                         } catch (Exception e) {
                                 e.printStackTrace();
                         } finally {
try {
if (bis != null) {
                                                 bis.close();
                                                 baos.close();
                                         }
                                 } catch (IOException e) {
                                         e.printStackTrace();
                                 }
                         }
                 }
         }
returnnull;
 }
}

(四)、案例:
1、功能:点击按钮,实现从网络*问图片,将图片保存进SDCard中。点击另外一按钮,可以获取到
      刚才保存进SDCard中的图片,将其加载的页面中的ImageView控件中。
2、示例代码:、示例代码:

publicclass MainActivity extends Activity {
private ImageView imageView_main_img;
private String urlString = "http://t2.baidu.com/it/u=2,1891512358&fm=19&gp=0.jpg";

 @Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
imageView_main_img = (ImageView) findViewById(R.id.imageView_main_img);
 }

publicvoid clickButton(View view) {
switch (view.getId()) {
case R.id.button_main_save :
new MyTask(this).execute(urlString);
break;
case R.id.button_main_show :
 String filepath = SDCardHelper.getSDCardPath() + File.separator
 + "mydir" + File.separator + "firstimg.jpg";
byte[] data = SDCardHelper.loadFileFromSDCard(filepath);
if (data != null) {
 Bitmap bm = BitmapFactory.decodeByteArray(data, 0, data.length);
imageView_main_img.setImageBitmap(bm);
 } else {
 Toast.makeText(this, "没有该图片!", Toast.LENGTH_LONG).show();
 }
break;
default:
break;
 }
 }

 //下载文件的异步任务
class MyTask extends AsyncTask<String, Void, byte[]> {
private Context context;
private ProgressDialog pDialog;

public MyTask(Context context) {
this.context = context;
pDialog = new ProgressDialog(context);
pDialog.setIcon(R.drawable.ic_launcher);
pDialog.setMessage("图片加载中...");
 }

 @Override
protectedvoid onPreExecute() {
super.onPreExecute();
pDialog.show();
 }

 @Override
protectedbyte[] doInBackground(String... params) {
 BufferedInputStream bis = null;
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
 URL url = new URL(params[0]);
 HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
 httpConn.setDoInput(true);
 httpConn.connect();

if (httpConn.getResponseCode() == 200) {
 bis = new BufferedInputStream(httpConn.getInputStream());
byte[] buffer = newbyte[1024 * 8];
int c = 0;
while ((c = bis.read(buffer)) != -1) {
 baos.write(buffer, 0, c);
 baos.flush();
 }
return baos.toByteArray();
 }
 } catch (Exception e) {
 e.printStackTrace();
 }
returnnull;
 }

 @Override
protectedvoid onPostExecute(byte[] result) {
super.onPostExecute(result);
if (result == null) {
 Toast.makeText(context, "图片加载失败!", Toast.LENGTH_LONG).show();
 } else {
 // 将字节数组转成Bitmap,然后将bitmap加载的imageview控件中
 // Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0,
 // result.length);
 // imageView_main_img.setImageBitmap(bitmap);
if (SDCardHelper.saveFileToSDCard (result, "mydir","firstimg.jpg")) {
 Toast.makeText (context, "图片保存OK!", Toast.LENGTH_LONG).show();
 } else {
 Toast.makeText (context, "图片保存失败!", Toast.LENGTH_LONG).show();
 }
 }
pDialog.dismiss();
 }
 }

 @Override
publicboolean onCreateOptionsMenu(Menu menu) {
 getMenuInflater().inflate(R.menu.main, menu);
returntrue;
 }
}

三、当日作业:
1、访问javaee上的某个地址http://192.168.104.160:8080/AndroidServer/UserlistWithImg,该地址返回json字符串。解析json字符串,会看到以下格式: {"userlists":[{"headpic":"1.jpg","username":"wangxiangjun","email":"wxj@qq.com"},{"headpic":"2.jpg","username":"chagnguibin","email":"chagnguibin@qq.com"},{"headpic":"3.jpg","username":"shanhouwang","email":"xiaoshan@gmail.com"},{"headpic":"4.jpg","username":"devin","email":"devin@sohu.com"},{"headpic":"5.jpg","username":"xiasiyong","email":"xisiyong@163.com"},{"headpic":"6.jpg","username":"zhouzulun","email":"zulun@263.com"},{"headpic":"7.jpg","username":"wangjingbin","email":"jinbin@gmail.com"},{"headpic":"8.jpg","username":"husheng","email":"husheng@qq.com"}]} 请解析json,将内容加载在ListView中。要求图片先保存进Sdcard,如果sdcard中有该图片,就直接从sdcard中获取。
2、写一个SDCard文件浏览器。 效果如图: Android数据存储——SharedPreferences


【提示】、SDCard文件浏览器的制作:
1、原理:利用File对象的listFile()方法获得File[]数组。将数组产生的信息填充在listview中。 核心代码中的重要方法:
    1. listFiles()
    2. isFile()
    3. isDirectory()
    4. getAbsolutePath()
    5. getParentFile()

HttpClient网络访问

一、HttpClient网络访问:
(一)、简介:
1、Apache组织提供了HttpClient项目,可以实现网络访问。在Android中,成功集成了HttpClient,所以在Android中可以直接使用HttpClient访问网络。 2、与HttpURLConnection相比,HttpClient将前者中的输入、输出流操作,统一封装成HttpGet、HttpPost、HttpRequest类。
    • HttpClient:网络连接对象;
    • HttpGet:代表发送GET请求;
    • HttpPost:代表发送POST请求;
    • HttpResponse:代表处理服务器响应的对象。
    • HttpEntity对象:该对象中包含了服务器所有的返回内容。
3、使用步骤:(六部曲)【重点
    1. 创建HttpClient对象:通过实例化DefaultHttpClient获得;
    2. 创建HttpGet或HttpPost对象:通过实例化 HttpGet或HttpPost 获得,而构造方法的参数是urlstring(即需要访问的网络url地址)。也可以通过调用setParams()方法来添加请求参数;
    3. 调用HttpClient对象的execute()方法,参数是刚才创建的 HttpGet或HttpPost对象 ,返回值是HttpResponse对象;
    4. 通过response对象中的getStatusLine()方法和getStatusCode()方法获取服务器响应状态是否是200。
    5. 调用 HttpResponse对象的getEntity()方法,返回HttpEntity对象。而该对象中包含了服务器所有的返回内容。
    6. 借助EntityUtils的toString()方法或toByteArray()对 HttpEntity对象进行处理,也可以通过IO流对 HttpEntity对象进行操作。

(二)、封装HttpClientHelper工具类:

public class HttpClientHelper {

public static HttpClient checkNetwork(String url) {

HttpClient httpClient = new DefaultHttpClient();

HttpGet request = new HttpGet(url);

HttpResponse httpResponse = null;

try {

httpResponse = httpClient.execute(request);

if (httpResponse.getStatusLine().getStatusCode() == 200) {

return httpClient;

}

catch (ClientProtocolException e) {

e.printStackTrace();

catch (IOException e) {

e.printStackTrace();

}

return null;

}


/**

 * 作用:实现网络访问文件,将获取到数据储存在文件流中

 * @param url

 *:访问网络的url地址

 * @return inputstream

 */

public static InputStream loadFileFromURL(String url) {

HttpClient httpClient = new DefaultHttpClient();

HttpGet requestGet = new HttpGet(url);

HttpResponse httpResponse;

try {

httpResponse = httpClient.execute(requestGet);

if (httpResponse.getStatusLine().getStatusCode() == 200) {

HttpEntity entity = httpResponse.getEntity();

return entity.getContent();

}

catch (Exception e) {

e.printStackTrace();

}

return null;

}


/**

 * 作用:实现网络访问文件,将获取到的数据存在字节数组中

 * @param url

 *:访问网络的url地址

 * @return byte[]

 */

public static byte[] loadByteFromURL(String url) {

HttpClient httpClient = new DefaultHttpClient();

HttpGet requestGet = new HttpGet(url);

try {

HttpResponse httpResponse = httpClient.execute(requestGet);

if (httpResponse.getStatusLine().getStatusCode() == 200) {

HttpEntity httpEntity = httpResponse.getEntity();

return EntityUtils.toByteArray(httpEntity);

}

catch (Exception e) {

e.printStackTrace();

System.out.println("====>" + e.toString());

}

return null;

}


/**

 * 作用:实现网络访问文件,返回字符串

 * @param url

 *:访问网络的url地址

 * @return String

 */

public static String loadTextFromURL(String url) {

HttpClient httpClient = new DefaultHttpClient();

HttpGet requestGet = new HttpGet(url);

try {

HttpResponse httpResponse = httpClient.execute(requestGet);

if (httpResponse.getStatusLine().getStatusCode() == 200) {

HttpEntity httpEntity = httpResponse.getEntity();

return EntityUtils.toString(httpEntity, "utf-8");

}

catch (Exception e) {

e.printStackTrace();

}

return null;

}


/**

 * 作用:实现网络访问文件,先给服务器通过“GET”方式提交数据,再返回相应的数据

 * @param url

 *:访问网络的url地址

 * @param params

 *String url:访问url时,需要传递给服务器的参数。

 *  第二个参数格式为:username=wangxiangjun&password=123456

 * @return byte[]

 */

public static byte[] doGetSubmit(String url, String params) {

HttpClient httpClient = new DefaultHttpClient();

HttpGet requestGet = new HttpGet(url + "?" + params);

try {

HttpResponse httpResponse = httpClient.execute(requestGet);

if (httpResponse.getStatusLine().getStatusCode() == 200) {

HttpEntity httpEntity = httpResponse.getEntity();

return EntityUtils.toByteArray(httpEntity);

}

catch (Exception e) {

e.printStackTrace();

}

return null;

}


/**

 * 作用:实现网络访问文件,先给服务器通过“POST”方式提交数据,再返回相应的数据

 * @param url

 * :访问网络的url地址

 * @param params

 * String url:访问url时,需要传递给服务器的参数。 第二个参数为:List<NameValuePair>

 * @return byte[]

 */

public static byte[] doPostSubmit(String url, List<NameValuePair> params) {

HttpClient httpClient = new DefaultHttpClient();

HttpPost requestPost = new HttpPost(url);

try {

requestPost.setEntity(new UrlEncodedFormEntity(params, "utf-8"));

HttpResponse httpResponse = httpClient.execute(requestPost);

if (httpResponse.getStatusLine().getStatusCode() == 200) {

HttpEntity httpEntity = httpResponse.getEntity();

return EntityUtils.toByteArray(httpEntity);

}

catch (Exception e) {

e.printStackTrace();

}

return null;

}


/**

 * 作用:实现网络访问文件,先给服务器通过“POST”方式提交数据,再返回相应的数据

 * @param url

 * :访问网络的url地址

 * @param params

 * String url:访问url时,需要传递给服务器的参数。 Map<String , Object>

 * @return byte[]

 */

public static byte[] doPostSubmit(String url, Map<String, Object> params) {

HttpClient httpClient = new DefaultHttpClient();

HttpPost requestPost = new HttpPost(url);


List<BasicNameValuePair> parameters = new ArrayList<BasicNameValuePair>();

try {

if (params != null) {

for (Map.Entry<String, Object> entry : params.entrySet()) {

String key = entry.getKey();

String value = entry.getValue().toString();

BasicNameValuePair nameValuePair = new BasicNameValuePair(key, value);

parameters.add(nameValuePair);

}

}

requestPost.setEntity(new UrlEncodedFormEntity(parameters, "utf-8"));

HttpResponse httpResponse = httpClient.execute(requestPost);


if (httpResponse.getStatusLine().getStatusCode() == 200) {

HttpEntity httpEntity = httpResponse.getEntity();

return EntityUtils.toByteArray(httpEntity);

}

catch (Exception e) {

e.printStackTrace();

}

return null;

}

}



【复习:】POST和GET在表单提交时的区别:【重点】  Post传递必须依赖于表单,传值的内容没有大小限制,传值内容不会在地址栏显示,比 Get 传值安全;  Get传值的内容会通过地址栏显示,而地址栏长度有最大长度限制,因为 Get 传参内容不可以过长,当超过某个长度后会自动裁切掉多余内容。比起 Post 传参, Get 传参不够安全。但是 Get 传参可以通过表单,也可以不通过表单,直接通过地址栏传参。因此 Get 传参更灵活。


四、assets目录中的图片如何访问?
(一)、回顾资源访问:
    1. 字符串资源文件:string
    2. 颜色资源、尺寸资源文件 :color、dimen
    3. 布局资源: layout
    4. 数组资源: string-array 、 integer-array
    5. 图片资源: drawable
    6. 样式和主题资源:style、theme
    7. 菜单资源: menu
    8. 原始XML资源: xml
    9. 原生文件资源: raw
【备注:】raw目录下主要放置的资源有音频、视频等文件。raw目录下的文件会在R.java中被注册。
        如何访问raw目录下的文件?        InputStream is = getResources().openRawResource(R.raw.文件名);
【备注:】       项目,那么就可以借助getResources().openRawResource(R.raw.文件名)产生的输入流,将文件保存在sdcard的某个目录下。这样SQLiteDatabase就可以调用openDatabase()方法来访问数据库了。特别提醒的是:要注意openDatabase()方法的第三个参数flag。如果选择不正确,则无法正常访问数据库。(应该是哪个常量值呢?请同学们先查看API,自己寻找答案。)