在Android应用中,咱们常须要记录用户设置的一些偏好参数,,此时咱们就须要用SharedPreferences和Editor将这些信息保存下来,在下次登陆时读取。java
SharedPreferences保存的数据主要相似于配置信息格式的数据,所以它保存数据的形式为key-value对,下面咱们来看下实例代码。android
首先是界面布局,比较简单,就是一个普通的登录界面.sql
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/account" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/password" android:layout_below="@id/account" /> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@id/password" android:text="保存参数" android:id="@+id/save" android:onClick="save" /> </RelativeLayout>
这是自定义的Preferences 类,用来实现数据的保存 ,可在Android的内置存储空间产生一文件。数据库
import android.R.integer; import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.widget.EditText; public class Preferences { private Context context; public Preferences(Context context) { this.context=context; } public void save(String name, Integer valueOf) { //保存文件名字为"shared",保存形式为Context.MODE_PRIVATE即该数据只能被本应用读取 SharedPreferences preferences=context.getSharedPreferences("shared",Context.MODE_PRIVATE); Editor editor=preferences.edit(); editor.putString("name", name); editor.putInt("age", valueOf); editor.commit();//提交数据 } }
下面是Mainactivity的代码。在activity的oncreate阶段咱们加载本地的数据。api
import java.util.HashMap; import java.util.Map; import android.R.integer; import android.os.Bundle; import android.app.Activity; import android.content.SharedPreferences; import android.view.Menu; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; public class MainActivity extends Activity { private EditText account,passworad; Preferences prefer;//自定义的类 SharedPreferences preference; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); account=(EditText)findViewById(R.id.account); passworad=(EditText)findViewById(R.id.password); //获取本地的数据 preference=getSharedPreferences("shared", MODE_PRIVATE); Map<String, String> map=new HashMap<String, String>(); map.put("name",preference.getString("name","")); map.put("age", String.valueOf(preference.getInt("age", 0))); account.setText(map.get("name")); passworad.setText(map.get("age")); } //保存文件的方法 public void save(View v) { String name=account.getText().toString(); String age=passworad.getText().toString(); prefer=new Preferences(this); prefer.save(name,Integer.valueOf(age)); Toast.makeText(getApplicationContext(), "保存完成", Toast.LENGTH_SHORT).show(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
咱们看一下效果.
点击保存参数,出现保存完成则说明咱们已经保存成功了,在下次登陆的时候能够看到这些参数还在。由于记录文件是在内置空间中的,因此咱们在SD卡中找不到该文件,数组
若是有root权限的手机能够下载个RE文件管理,咱们能够再/data/data/的路径找到不少应用程序的内置文件夹,咱们能够在这些文件夹中看到一个shared_prefs文件夹,缓存
里面就有咱们刚刚设置而产生的xml文件。
app
咱们先来考虑这样一个问题:ide
打开手机设置,选择应用管理,选择任意一个App,而后你会看到两个按钮,一个是清除缓存,另外一个是清除数据,那么当咱们点击清除缓存的时候清除的是哪里的数据?当咱们点击清除数据的时候又是清除的哪里的数据?读完本文相信你会有答案。工具
在android开发中咱们经常听到这样几个概念,内存,内部存储,外部存储,不少人经常将这三个东西搞混,那么咱们今天就先来详细说说这三个东西是怎么回事?
内存,咱们在英文中称做memory,内部存储,咱们称为InternalStorage,外部存储咱们称为ExternalStorage,这在英文中本不会产生歧义,可是当咱们翻译为中文以后,前两个都简称为内存,因而,混了。
那么究竟什么是内部存储什么是外部存储呢?
首先咱们打开DDMS,有一个File Explorer,以下:
完全理解android中的内部存储与外部存储0
这里有三个文件夹须要咱们重视,一个是data,一个是mnt,一个是storage,咱们下面就详细说说这三个文件夹。
1.内部存储
data文件夹就是咱们常说的内部存储,当咱们打开data文件夹以后(没有root的手机不能打开该文件夹),里边有两个文件夹值得咱们关注,以下:
一个文件夹是app文件夹,还有一个文件夹就是data文件夹,app文件夹里存放着咱们全部安装的app的apk文件,其实,当咱们调试一个app的时候,能够看到控制台输出的内容,有一项是uploading .....就是上传咱们的apk到这个文件夹,上传成功以后才开始安装。另外一个重要的文件夹就是data文件夹了,这个文件夹里边都是一些包名,打开这些包名以后咱们会看到这样的一些文件:
1.data/data/包名/shared_prefs
2.data/data/包名/databases
3.data/data/包名/files
4.data/data/包名/cache
若是打开过data文件,应该都知道这些文件夹是干什么用的,咱们在使用sharedPreferenced的时候,将数据持久化存储于本地,其实就是存在这个文件中的xml文件里,咱们App里边的数据库文件就存储于databases文件夹中,还有咱们的普通数据存储在files中,缓存文件存储在cache文件夹中,存储在这里的文件咱们都称之为内部存储。
2.外部存储
外部存储才是咱们平时操做最多的,外部存储通常就是咱们上面看到的storage文件夹,固然也有多是mnt文件夹,这个不一样厂家有可能不同。
通常来讲,在storage文件夹中有一个sdcard文件夹,这个文件夹中的文件又分为两类,一类是公有目录,还有一类是私有目录,其中的公有目录有九大类,好比DCIM、DOWNLOAD等这种系统为咱们建立的文件夹,私有目录就是Android这个文件夹,这个文件夹打开以后里边有一个data文件夹,打开这个data文件夹,里边有许多包名组成的文件夹。
说到这里,我想你们应该已经能够分清楚什么是内部存储什么是外部存储了吧?好,分清楚以后咱们就要看看怎么来操做内部存储和外部存储了。
3.操做存储空间
首先,通过上面的分析,你们已经明白了,什么是内部存储,什么是外部存储,以及这两种存储方式分别存储在什么位置,通常来讲,咱们不会本身去操做内部存储空间,没有root权限的话,咱们也无法操做内部存储空间,事实上内部存储主要是由系统来维护的。不过在代码中咱们是能够访问到这个文件夹的。因为内部存储空间有限,在开发中咱们通常都是操做外部存储空间,Google官方建议咱们App的数据应该存储在外部存储的私有目录中该App的包名下,这样当用户卸载掉App以后,相关的数据会一并删除,若是你直接在/storage/sdcard目录下建立了一个应用的文件夹,那么当你删除应用的时候,这个文件夹就不会被删除。
通过以上的介绍,咱们能够总结出下面一个表格:
一目了然,什么是内部存储,什么是外部存储。
若是按照路径的特征,咱们又能够将文件存储的路径分为两大类,一类是路径中含有包名的,一类是路径中不含有包名的,含有包名的路径,由于和某个App有关,因此对这些文件夹的访问都是调用Context里边的方法,而不含有包名的路径,和某一个App无关,咱们能够经过Environment中的方法来访问。以下图:
public class MainActivity extends AppCompatActivity { private MyDatabaseHelper dbHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 3); // 执行这句并不会建立数据库文件 Button btnCreateDatabase = (Button) findViewById(R.id.button); btnCreateDatabase.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dbHelper.getWritableDatabase(); // 执行这句才会建立数据库文件 } }); } }
public class MyDatabaseHelper extends SQLiteOpenHelper { public static final String CREATE_BOOK = "create table book (" + "id integer primary key autoincrement, " + "author text, " + "price real," + "pages integer, " + "name text)"; private Context mContext; public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); mContext = context; } /** * 数据库已经建立过了, 则不会执行到,若是不存在数据库则会执行 * @param db */ @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_BOOK); // 执行这句才会建立表 Toast.makeText(mContext, "create succeeded", Toast.LENGTH_SHORT).show(); } /** * 建立数据库时不会执行,增大版本号升级时才会执行到 * @param db * @param oldVersion * @param newVersion */ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // 在这里面能够把旧的表 drop掉 重新建立新表, // 但若是数据比较重要更好的作法仍是把旧表的数据迁移到新表上,好比升级qq聊天记录被删掉确定招骂 Toast.makeText(mContext, "onUpgrade oldVersion:" + oldVersion + " newVersion:" + newVersion, Toast.LENGTH_SHORT).show(); } }
验证数据库文件是否存在的方法看最后部分
剩下的工做就是对数据库表的增删改查了
首先经过下面的代码得到一个引用以便操做数据库
1 SQLiteDatabase db = dbHelper.getWritableDatabase();
对于增删改均可以用 db.execSQL(String sql); 来执行sql语句。 例如增长一条记录
1 db.execSQL("insert into book(name , author, pages, price) values("Android数据库操做指南", "panda fang", 200, 35.5)");
遇到字符串要转义 有没有以为很蛋疼, 用下面的方法就好多了
1 db.execSQL("insert into book(name , author, pages, price) values(?, ? ,? ,? )", new String[]{"Android数据库操做指南", "panda fang", "200", "35.5"});
sql 中用 ? 占位 后面传入真正的参数, 因为在建立表的时候已经约定pages 和 price字段的数据类型为integer和real, 虽然代码中写的是字符串并不影响,存入数据库会自动处理的。数组嘛,必须与其余的元素类型一致。 这第二个方式是 execSQL(String sql)的重载方法 api是 public void execSQL(String sql, Object[] bindArgs) throws SQLException
对于查询则要使用 rawQuery(String sql, String[] selectionArgs) , 由于 execSQL返回void ,而查询须要访问查询结果。方法以下:
Cursor cursor = db.rawQuery("select * from book", null); while (cursor.moveToNext()) { String name = cursor.getString(cursor.getColumnIndex("name")); String author = cursor.getString(cursor.getColumnIndex("author")); Log.i(TAG, "name:" + name + " author:" + author); } cursor.close();
如何检查数据库文件是否存在,以及检查表中的数据呢。
前提是使用模拟器或者root过的真机。从 android studio 菜单中 Tools -> Android -> Android Device Monitor -> File Explorer 找到 data/data/程序包名/databases 目录
查看是否存在数据库文件。若是存在能够导出到电脑上, 用 如下工具查看数据库中的表
import java.io.FileNotFoundException; import android.content.ContentResolver; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.ImageView; import com.maikefengchao.daixu.R; public class WriteArticle_CompeterelayActivity extends BaseActivity { private ImageView im_upload_img; @Override public void initView(Bundle savedInstanceState){ setContentView(R.layout.view_write_competerelay); im_upload_img = (ImageView)findViewById(R.id.write_competerelay_cover_iv); } @Override protected void setListener() { im_upload_img.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); /* 开启Pictures画面Type设定为image */ intent.setType("image/*"); /* 使用Intent.ACTION_GET_CONTENT这个Action */ intent.setAction(Intent.ACTION_GET_CONTENT); /* 取得相片后返回本画面 */ startActivityForResult(intent, 1); } }); } @Override protected void processLogic(Bundle saveInstanceState) { } //获取本地图片 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK) { Uri uri = data.getData(); String img_url = uri.getPath();//这是本机的图片路径 ContentResolver cr = this.getContentResolver(); try { Bitmap bitmap = BitmapFactory.decodeStream(cr.openInputStream(uri)); ImageView imageView = (ImageView) findViewById(R.id.write_competerelay_cover_iv); /* 将Bitmap设定到ImageView */ imageView.setImageBitmap(bitmap); } catch (FileNotFoundException e) { Log.e("Exception", e.getMessage(),e); } } super.onActivityResult(requestCode, resultCode, data); } }
statistics.sh