目录

前言一、使用SharedPreferences存储数据    1、适用范围    2、核心原理    3、app内部实现数据存储(Demo)    4、共享其他应用的SharedPreferences    5、关于android:sharedUserId    6、SharedPreferences总结二、文件存储数据    1、功能介绍    2、存储方式    3、使用内部存储(Demo)    4、使用外部存储(Demo)三、SQLite数据库存储数据    1、简介及特点    2、实现原理    3、通过SQLiteDatabase创建(Demo)    4、通过继承SQLiteOpenHelper类创建    5、两种方式的联系四、使用ContentProvider存储数据    与SQLite数据库联系    详见:[Android实习生 —— 四大组件之ContentProvider]   五、网络存储数据【附录】  Demo

前言

Android提供了5种方式来让用户保存持久化应用程序数据。
**
① 使用SharedPreferences存储数据 
② 文件存储数据
③ SQLite数据库存储数据
④ 使用ContentProvider存储数据
⑤ 网络存储数据 **

我们可以根据需求选择对应的方式。文章根据相关Demo讲述各种方式的用法及优缺点说明,在文章末尾附录会有相关Demo的下载 。

通过以上方式还可以实现数据的在不同app间的数据共享。

一、使用SharedPreferences存储数据

1、适用范围

保存少量的数据,且这些数据的格式非常简单:字符串型、基本类型的值。比如应用程序的各种配置信息(如是否打开音效、是否使用震动效果、小游戏的玩家积分等),解锁口令密码等。

2、核心原理
  • 保存基于XML文件存储的key-value键值对数据。

  • 通过DDMS的File Explorer面板,展开文件浏览树,很明显SharedPreferences数据总是存储在/data/data//shared_prefs目录下。

  • SharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过SharedPreferences.edit()获取的内部接口Editor对象实现。

  • SharedPreferences本身是一个接口,程序无法直接创建SharedPreferences实例,只能通过Context提供的getSharedPreferences(String name, int mode)方法来获取SharedPreferences实例,该方法中name表示要操作的xml文件名,第二个参数具体如下:

Context.MODE_PRIVATE: 指定该SharedPreferences数据只能被本应用程序读、写。Context.MODE_WORLD_READABLE:  指定该SharedPreferences数据能被其他应用程序读,但不能写。Context.MODE_WORLD_WRITEABLE:  指定该SharedPreferences数据能被其他应用程序读,写。//以上三种写法均已过时,可以直接用数字代替,//Context.MODE_PRIVATE = 0//Context.MODE_WORLD_READABLE = 1//Context.MODE_WORLD_WRITEABLE = 2
  • Editor有如下主要重要方法:
SharedPreferences.Editor clear()//清空SharedPreferences里所有数据SharedPreferences.Editor putXxx(String key , xxx value)://向SharedPreferences存入指定key对应的数据,其中xxx 可以是boolean,float,int等各种基本类型据SharedPreferences.Editor remove()//删除SharedPreferences中指定key对应的数据项boolean commit()//当Editor编辑完成后,使用该方法提交修改
3、app内部实现数据存储(Demo)
  • 通过点击“保存用户名”,对登陆成功的用户进行用户名键值对的保存,让用户下次启动app时自动填充用户名。
public class MainActivity extends AppCompatActivity implements View.OnClickListener {    private EditText userName,userPass;    private CheckBox checkBox;    private Button ok,cancel;    private SharedPreferences pref;    private SharedPreferences.Editor editor;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //初始化SharedPreferences 及相关组建。        init();        //3、取出userInfo中的数据。        String name00=pref.getString("userName",null);        if (name00==null) {            checkBox.setChecked(false);        }else {            checkBox.setChecked(true);            //4、将取到的用户名赋给用户名编辑框。            userName.setText(name00);        }    }    private void init() {        userName = (EditText) findViewById(R.id.userName);        userPass = (EditText) findViewById(R.id.userPass);        checkBox = (CheckBox) findViewById(R.id.check);        ok = (Button) findViewById(R.id.join_btn);        cancel = (Button) findViewById(R.id.cancel_btn);        ok.setOnClickListener(this);        cancel.setOnClickListener(this);        //1、获取SharedPreferences对象,并把文件名设为"userInfo"。        pref =getSharedPreferences("userInfo", MODE_PRIVATE);        //2、获取SharedPreferences内部接口Editor用来编辑userInfo。        editor = pref.edit();    }    @Override    public void onClick(View v) {        //2.1:获取用户输入的用户名密码信息。        String name = userName.getText().toString();        String pass = userPass.getText().toString();        switch (v.getId()) {            case R.id.join_btn:                if ("admin".equals(name)&&"123456".equals(pass)){                    if(checkBox.isChecked()){                        //2.2.1:判断成功登入并对"保存用户名"打勾之后,                        //将用户名的键值对添加到文件名为"userInfo"文件中并提交。                        editor.putString("userName",name);                        editor.commit();                    }else{                        //2.2.2若没打勾,则清空并提交。                        editor.remove("userName");                        editor.commit();                    }                    Toast.makeText(this,"登陆成功",Toast.LENGTH_SHORT).show();                }else{                    Toast.makeText(this,"登陆失败",Toast.LENGTH_SHORT).show();                }                    break;            case R.id.cancel_btn:                userName.setText(null);                userPass.setText(null);                break;        }    }
  • 效果
    登入成功


    登入成功

    第二次打开自动填充用户名。


    第二次打开app
    【在DDMS中依次打开data/data/<包>/shared_prefs,可以看到此文件内容】
    data/data/<包>/shared_prefs
    文件内容
4、共享其他应用的SharedPreferences
  • 在创建SharedPreferences时,指定MODE_WORLD_READABLE模式,表明该SharedPreferences数据可以被其他程序读取。
SharedPreferences  pref =getSharedPreferences("userInfo", MODE_WORLD_READABLE);
  • 创建其他应用程序对应的Context上下文引用:
Context otherAppContent = null;        try {            otherAppContent = createPackageContext("com.bb.sharedpr",CONTEXT_IGNORE_SECURITY);//com.bb.sharedpr为我们要调用数据的包名        } catch (PackageManager.NameNotFoundException e) {            e.printStackTrace();        }
  • 使用其他程序的Context获取对应的SharedPreferences
SharedPreferences read = otherAppContent.getSharedPreferences("userInfo",MODE_WORLD_READABLE);
  • 如果是写入数据,使用Editor接口即可,所有其他操作均和前面一致。
5、关于android:sharedUserId
  • 通常,不同的APK会具有不同的userId,因此运行时属于不同的进程中,而不同进程中的资源是不共享的,才保障了程序运行的稳定。然后在有些时候,我们自己开发了多个APK并且需要他们之间互相共享资源,那么就需要通过设置shareUserId来实现这一目的。

  • 通过SharedUserId,拥有同一个User id的多个APK可以配置成运行在同一个进程中.所以默认就是可以通过获取上下文来互相访问任意数据. 也可以配置成运行成不同的进程, 同时可以访问其他APK的数据目录下的数据库和文件.就像访问本程序的数据一样。

  • 而上面的两个工程中并没有对Android:sharedUserId属性进行设置。这个属性是在查资料时看到的:意思是说,在manifest.xml里面将两个应用程序的android:sharedUserId属性设为相同的就可以对SharedPreferences文件进行写。(此处并没有验证)

6、SharedPreferences总结
  • 优点
    SharedPreferences对象与SQLite数据库相比显得格外轻量级,免去了创建数据库,创建表,写SQL语句等诸多操作,相对而言更加方便,简洁。
  • 缺点
    1、其职能存储boolean,int,float,long和String五种简单的数据类型。
    2、无法进行条件查询等。

【所以不论SharedPreferences的数据存储操作是如何简单,它也只能是存储方式的一种补充,而无法完全替代如SQLite数据库这样的其他数据存储方式。】

二、文件存储数据

1、功能介绍

Android文件系统和其他平台上的类似,使用File APIs可以读写文件。这部分内容需要你已经了解了Linux文件系统的基础,并且也了解了java.io包中的标准文件输入输出APIs。

2、存储方式

所有的Android设备都有两块文件存储区域:内部和外部存储。

  • 内部存储:指设备自带的非易失性存储器。
    • 永远可用,因为不可以拆卸。
    • 文件默认情况下只对你的app可用,是私有的,无论是用户或者是其他app都不能共享访问你的数据。
    • 当用户卸载你的app时,系统会自动移除app在内部存储上的所有文件。
  • 外部存储:指可拆卸的存储介质,如卫星电视SD卡。
    • 不一定一直可以访问,因为用户可以拆卸外部存储设备。
    • 文件是全局可读的,没有访问限制,不受你的控制。可以和其他app共享数据,用户使用电脑也可以访问在外部存储中的文件。
    • 当用户卸载你的app时,只有当你把文件存储在以 getExternalFilesDir().获得的路径下时,系统才会帮你自动移除。
3、使用内部存储(Demo)

Context提供了两个方法来打开数据文件里的文件IO流

FileInputStream openFileInput(String name); FileOutputStream openFileInput(String name , int mode);//name参数: 用于指定文件名称,不能包含路径分隔符“/” ,//如果文件不存在,Android 会自动创建它。

这两个方法第一个参数 用于指定文件名,第二个参数指定打开文件的模式。

MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,              写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可以使用Context.MODE_APPENDMODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。MODE_WORLD_READABLE:表示当前文件可以被其他应用读取。MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。模式可以连用 比如可读可写 就写成:MODE_WORLD_READABLE+MODE_WORLD_WRITEABLE

除此之外,Context还提供了如下几个重要的方法:

getDir(String name , int mode):在应用程序的数据文件夹下获取或者创建name对应的子目录getFilesDir():获取该应用程序的数据文件夹得绝对路径fileList():返回该应用数据文件夹的全部文件 

我们将实现的Demo为将输入的文字写入文件,并读取出来。

  • 第一步:创建和写入一个内部存储的私有文件:
public void WriteFiles(String content){         try {          //①调用Context的openFileOutput()函数,填入文件名和操作模式,它会返回一个FileOutputStream对象。            FileOutputStream fos = openFileOutput("a.txt",            MODE_PRIVATE);          //②通过FileOutputStream对象的write()函数写入数据。             fos.write(content.getBytes());          //③FileOutputStream对象的close ()函数关闭流。             fos.close();        } catch (FileNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }             }
  • 第二步:读取一个内部存储的私有文件:
public String readFiles(){        String content = null;         try {            //① 调用openFileInput( ),参数中填入文件名,会返回一个FileInputStream对象。            FileInputStream fis= openFileInput("a.txt");            StringBuilder sb = new StringBuilder();            byte [] buffer =  new byte[1024];            int len = 0;            //② 使用流对象的 read()方法读取字节            while ((len=fis.read(buffer))!=-1) {                sb.append(new String(buffer, 0, len));            }            content =sb.toString();            //③ 调用流的close()方法关闭流            fis.close();        } catch (FileNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        return content;    }

第三步:MainActivity中实现流程:

protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        edt = (EditText) findViewById(R.id.editText1);        but = (Button) findViewById(R.id.write);        contentvalue = (TextView) findViewById(R.id.contentvalue);        but.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                WriteFiles(edt.getText().toString());                contentvalue.setText(readFiles());            }        });    }

效果

  • app效果
  • 文件路径
  • 文件

【注意】保存内存缓存文件
有时候我们只想缓存一些数据而不是持久化保存,可以使用getCacheDir()去创建或打开一个文件,文件的存储目录( /data/data/包名/cache )是一个应用专门来保存临时缓存文件的内存目录。

File file = this.getCacheDir();Log.i("info", file.toString();

当设备的内部存储空间比较低的时候,Android可能会删除这些缓存文件来恢复空间,但是你不应该依赖系统来回收,要自己维护这些缓存文件把它们的大小限制在一个合理的范围内,比如1MB.当你卸载应用的时候这些缓存文件也会被移除。

4、使用外部存储(Demo)

因为内部存储容量限制,有时候需要存储数据比较大的时候需要用到外部存储,使用外部存储分为以下几个步骤:

  • 第一步:添加外部存储访问限权
               
  • 第二步:检测外部存储的可用性
//获取外存储的状态String state = Environment.getExternalStorageState();if (Environment.MEDIA_MOUNTED.equals(state)) {    // 可读可写    mExternalStorageAvailable = mExternalStorageWriteable = true;} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {    // 可读} else {    // 可能有很多其他的状态,但是我们只需要知道,不能读也不能写  }
  • 第三步:读写数据
public class MainActivity extends Activity {    private TextView textView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        textView= (TextView) findViewById(R.id.tv); if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){            File sdCardDir = Environment.getExternalStorageDirectory();//获取SDCard目录  "/sdcard"            File saveFile = new File(sdCardDir,"a.txt");            //写数据            try {                FileOutputStream fos= new FileOutputStream(saveFile);                fos.write("bobobo".getBytes());                fos.close();            } catch (Exception e) {                e.printStackTrace();            }            //读数据            try {                FileInputStream fis= new FileInputStream(saveFile);                int len =0;                byte[] buf = new byte[1024];                StringBuffer sb = new StringBuffer();                while((len=fis.read(buf))!=-1){                    sb.append(new String(buf, 0, len));                }                textView.setText(sb.toString());                fis.close();            } catch (Exception e) {                e.printStackTrace();            }        }    }}
  • 效果


    效果
    外部路径

三、SQLite数据库存储数据

1、简介及特点
  • SQLite是轻量级嵌入式数据库引擎,且只利用很少的内存就有很好的性能。

  • 在我们为移动设备开发应用程序时,使用SQLite作为复杂数据、大量数据的存储引擎。

  • SQLite它是一个独立的,无需服务进程,无需安装和管理配置,支持事务处理,可以使用SQL语言的数据库。

  • 支持多种开发语言,C,PHP,Perl,Java,ASP.NET,Python。

  • 在Content Provider 技术中就是使用SQLite数据库来操作数据的。

2、实现原理
  • 直接通过SQLiteDatabase对象来创建一个数据库。

  • 或者继承SQLiteOpenHelper类封装创建和更新数据库使用的逻辑。

它们都会在ddns 的file explorer 中的data/data/<包>/databases中创建这个数据库文件。

3、通过SQLiteDatabase创建(Demo)
  • 第一步:创建数据库并插入数据
private SQLiteDatabase db;public void init() {        db = openOrCreateDatabase("user.db", MODE_PRIVATE, null);        db.execSQL("create table if not exists usertb (_id integer primary key autoincrement, name text not null , age integer not null , sex text not null )");        db.execSQL("insert into usertb(name,sex,age) values('张三','女',18)");        db.execSQL("insert into usertb(name,sex,age) values('李四','男',19)");        db.execSQL("insert into usertb(name,sex,age) values('王五','女',22)");        //查询数据库并展示        query(findViewById(R.id.query));    }

query代码【关于查询代码的详情在本小节末尾会有详细说明】

public void query(View view) {        tv_id.setText("");        tv_name.setText("");        tv_sex.setText("");        tv_age.setText("");        Cursor cursor = db.rawQuery("select * from usertb", null);        if (cursor != null) {            while (cursor.moveToNext()) {                tv_id.append("\n" + cursor.getString(cursor.getColumnIndex("_id")));                tv_name.append("\n" + cursor.getString(cursor.getColumnIndex("name")));                tv_sex.append("\n" + cursor.getString(cursor.getColumnIndex("sex")));                tv_age.append("\n" + cursor.getString(cursor.getColumnIndex("age")));            }            cursor.close();        }//        db.close();不要关闭,不然单独调用查询操作会空指针    }

效果:


效果
文件路径
pc中使用Navicat可以操作数据表
  • 第二步:实现插入数据的操作(两种方式任选其一)
    • 使用insert方法
public void add(View view) {       ContentValues cv = new ContentValues();//实例化一个ContentValues用来装载待插入的数据        cv.put("name","新来的");        cv.put("sex","女");        cv.put("age","18");        db.insert("usertb",null,cv);//执行插入操作//      使用直接执行语句添加 //      db.execSQL("insert into usertb(name,sex,age) values('新来的','女',18)");        query(findViewById(R.id.query));    }
  • 使用execSQL方式来实现
db.execSQL("insert into usertb(name,sex,age) values('新来的','女',18)");
  • 效果


    添加效果
  • 第三步:实现删除数据的操作
    同样有2种方式可以实现

String whereClause = "name=?";//删除的条件String[] whereArgs = {"新来的"};//删除的条件参数db.delete("user",whereClause,whereArgs);//执行删除

使用execSQL方式的实现

String sql = "delete from usertb where name='新来的'";//删除操作的SQL语句db.execSQL(sql);//执行删除操作
  • 第四步:实现修改数据的操作(将张三改成张三三)
    同上,仍是2种方式
ContentValues cv = new ContentValues();//实例化ContentValuescv.put("name","张三三");//添加要更改的字段及内容String whereClause = "name=?";//修改条件String[] whereArgs = {"张三"};//修改条件的参数db.update("usertb",cv,whereClause,whereArgs);//执行修改

使用execSQL方式的实现

String sql = "update usertb set name = '张三三' where username='张三'";//修改的SQL语句db.execSQL(sql);//执行修改

【关于查询操作】
查询操作相对于上面的几种操作要复杂些,因为我们经常要面对着各种各样的查询条件,所以系统也考虑到这种复杂性,为我们提供了较为丰富的查询形式:

db.rawQuery(String sql, String[] selectionArgs);  db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy);  db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);  db.query(String distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);

各参数说明:

table:表名称colums:表示要查询的列所有名称集selection:表示WHERE之后的条件语句,可以使用占位符selectionArgs:条件语句的参数数组groupBy:指定分组的列名having:指定分组条件,配合groupBy使用orderBy:y指定排序的列名limit:指定分页参数distinct:指定“true”或“false”表示要不要过滤重复值Cursor:返回值,相当于结果集ResultSet

最后,他们同时返回一个Cursor对象,代表数据集的游标,有点类似于JavaSE中的ResultSet。下面是Cursor对象的常用方法:

c.move(int offset); //以当前位置为参考,移动到指定行  c.moveToFirst();    //移动到第一行  c.moveToLast();     //移动到最后一行  c.moveToPosition(int position); //移动到指定行  c.moveToPrevious(); //移动到前一行  c.moveToNext();     //移动到下一行  c.isFirst();        //是否指向第一条  c.isLast();     //是否指向最后一条  c.isBeforeFirst();  //是否指向第一条之前  c.isAfterLast();    //是否指向最后一条之后  c.isNull(int columnIndex);  //指定列是否为空(列基数为0)  c.isClosed();       //游标是否已关闭  c.getCount();       //总数据项数  c.getPosition();    //返回当前游标所指向的行数  c.getColumnIndex(String columnName);//返回某列名对应的列索引值  c.getString(int columnIndex);   //返回当前行指定列的值

举例

db.update("stutb", values, "_id>?", new String[]{"3"});//把id>3的性别更新成"女"db.delete("stutb", "name like ?", new String[]{"%丰%"});//删掉名字中带有"丰"的记录//使用游标类 进行查询Cursor c = db.query("stutb", null, "_id>?", new String[]{"0"}, null, null, "_id");
4、通过继承SQLiteOpenHelper类创建

Android 提供了 SQLiteOpenHelper,其是SQLiteDatabase的一个帮助类,用来管理数据库的创建和版本的更新。你只要继承 SQLiteOpenHelper 类根据开发应用程序的需要,封装创建和更新数据库使用的逻辑就行了。

  • 第一步:写一个子类继承SQLiteOpenHelper并复写三个方法
       public class DatabaseHelper extends SQLiteOpenHelper {       /**       * @param context  上下文环境(例如,一个 Activity)       * @param name   数据库名字       * @param factory  一个可选的游标工厂(通常是 Null)       * @param version  数据库模型版本的整数       *        * 会调用父类 SQLiteOpenHelper的构造函数       */       //    public DatabaseHelper(Context context, String name,    CursorFactory factory, int version) {       //        super(context, name, factory, version);       //    }  //这里我们直接定义数据库名字根版本号 private static final String DATABASE_NAME = "stu.db";     private static final int VERSION = 1;     private static final String TABLE_NAME = "stutb";     // 步骤2:重载构造方法     public DatabaseHelper(Context context) {         super(context, DATABASE_NAME, null, VERSION);     }    /**     *  在数据库第一次创建的时候会调用这个方法     *       *根据需要对传入的SQLiteDatabase 对象填充表和初始化数据。     */    //在这里进行建表       @Override    public void onCreate(SQLiteDatabase db) {        db.execSQL("create table if not exists stutb(_id integer primary key autoincrement,name text not null,sex text not null,age integer not null)");        db.execSQL("insert into stutb(name,sex,age)values('张三','男',18)");        db.execSQL("insert into stutb(name,sex,age)values('张四','女',20)");    }    /**     * 当数据库需要修改的时候(两个数据库版本不同),Android系统会主动的调用这个方法。     * 一般我们在这个方法里边删除数据库表,并建立新的数据库表.     */    @Override    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {        //三个参数,一个 SQLiteDatabase 对象,一个旧的版本号和一个新的版本号    }    @Override    public void onOpen(SQLiteDatabase db) {        // 每次成功打开数据库后首先被执行        super.onOpen(db);    }}
  • 第二步:继承SQLiteOpenHelper之后就拥有了以下两个方法
    getReadableDatabase()  创建或者打开一个查询数据库
    getWritableDatabase() 创建或者打开一个可写数据库
DatabaseHelper database = new DatabaseHelper(MainActivity.this);//传入一个上下文参数SQLiteDatabase db = null;db = database.getWritableDatabase();//上面这段代码会返回一个 SQLiteDatabase 类的实例,使用这个对象,你就可以查询或者修改数据库。getWritableDatabase()和getReadableDatabase()方法都可以获取一个用于操作数据库的SQLiteDatabase实例。//  其中getReadableDatabase()方法则是先以读写方式打开数据库,如果数据库的磁盘空间满了,就会打开失败,当打开失败后//  会继续尝试以只读方式打开数据库。如果该问题成功解决,则只读数据库对象就会关闭,然后返回一个可读写的数据库对象。//  getWritableDatabase() 方法以读写方式打开数据库,一旦数据库的磁盘空间满了,数据库就只能读而不能写,//  使用的是getWritableDatabase() 方法就会出错。 
  • 第三步:实现查询代码
Cursor c = db.rawQuery("select * from stutb", null);        if (c!=null) {            String [] cols = c.getColumnNames();            while (c.moveToNext()) {                for (String ColumnName : cols) {                    Log.i("info ", ColumnName+":"+c.getString(c.getColumnIndex(ColumnName)));                }            }            c.close();        }        db.close();
  • 效果(命令行打印)


    命令行打印结果
5、两种方式的联系
  • Context.openOrCreateDatabase 与 SQLiteDatabase.openOrCreateDatabase本质上完成的功能都一样,Context.openOrCreateDatabase最终是需要调用 SQLiteDatabase.openOrCreateDatabase来完成数据库的创建的。 也就是说, SQLiteDatabase类是android上对sqlite的最底层的封装,几乎所有的对数据库的操作最终都通过这个类来实现。

  • 对数据表的操作方式不同,SQLiteOpenHelper是SQLiteDatabase的帮助类。要如你的代码执行,你需要通过类似SQLiteOpenHelper的getWritableDatabase方法获取到SQLiteDatabase实例才可以。创建表直接可以在SQLiteOpenHelper的onCreate方法中做,那里通过参数你可以得到一个SQLiteDatabase的实例。

四、使用ContentProvider存储数据

与SQLite数据库联系

1.SQLiteOpenHelper是将对数据库和表的创建、插入、更新、删除操作进行了简单的封装;

2.而ContentProvider可以说是一个对外的接口,除了可以实现对SQLiteOpenHelper的封装,还可以实现对文件操作、图片操作、对象操作等实现封装;

3.在多线程中使用SQLiteOpenHelper要考虑线程同步问题,而如果使用ContentProvider的话基本不用考虑;

4.使用ContentProvider存储数据可以实现不同app之间的数据共享。

详见:
Android实习生 —— 四大组件之ContentProvider

五、网络存储数据

一、网络保存数据介绍

可以使用网络来保存数据,在需要的时候从网络上获取数据,进而显示在App中。用网络保存数据的方法有很多种,对于不同的网络数据采用不同的上传与获取方法。

本文利用LeanCloud来进行网络数据的存储。

LeanCloud是一种简单高效的数据和文件存储服务。感兴趣的可以查看网址:https://leancloud.cn/。 的例子。

二、使用方法

1、上传数据

AVObject personObject = new AVObject(TABLENAME);        personObject.put(NAME, person.name);        personObject.put(AGE, person.age);        personObject.put(INFO, person.info);        personObject.saveInBackground(new SaveCallback() {            @Override            public void done(AVException e) {                if (e == null) {                    Log.v(TAG, "put data success!");                } else {                    Log.v(TAG, "put data failed!error:" + e.getMessage());                }            }        });

2、读取数据

AVQuery avQuery = new AVQuery<>(TABLENAME);        avQuery.findInBackground(new FindCallback() {            @Override            public void done(List list, AVException e) {                if (e == null) {                    Log.v(TAG, "get data success!");                    String message = "";                    //倒着遍历后四条上传的数据                    for (int i = list.size()-1; i >=list.size()-4; i--) {                        String name = list.get(i).getString(NAME);                        int age = list.get(i).getInt(AGE);                        String info = list.get(i).getString(INFO);                        message += "name:" + name + ",age:" + age + ",info:" + info + ".\n";                    }                    textView.setText(message);                }            }        });
三、小案例

1、导入jar包


导入jar包

2、添加NetworkDBManager类

package com.zhangmiao.datastoragedemo;import android.util.Log;import android.widget.TextView;import com.avos.avoscloud.AVException;import com.avos.avoscloud.AVObject;import com.avos.avoscloud.AVQuery;import com.avos.avoscloud.FindCallback;import com.avos.avoscloud.SaveCallback;import java.util.List;/** * Created by zhangmiao on 2016/12/22. */public class NetworkDBManager {    private static final String TAG = "NetworkDBManager";    private final static String TABLENAME = "person";    private final static String NAME = "name";    private final static String AGE = "age";    private final static String INFO = "info";    public void putData(Person person) {        AVObject personObject = new AVObject(TABLENAME);        personObject.put(NAME, person.name);        personObject.put(AGE, person.age);        personObject.put(INFO, person.info);        personObject.saveInBackground(new SaveCallback() {            @Override            public void done(AVException e) {                if (e == null) {                    Log.v(TAG, "put data success!");                } else {                    Log.v(TAG, "put data failed!error:" + e.getMessage());                }            }        });    }    public void getData(final TextView textView) {        AVQuery avQuery = new AVQuery<>(TABLENAME);        avQuery.findInBackground(new FindCallback() {            @Override            public void done(List list, AVException e) {                if (e == null) {                    Log.v(TAG, "get data success!");                    String message = "";                     ////倒着遍历后四条上传的数据                    for (int i = list.size()-1; i >=list.size()-4; i--) {                        String name = list.get(i).getString(NAME);                        int age = list.get(i).getInt(AGE);                        String info = list.get(i).getString(INFO);                        message += "name:" + name + ",age:" + age + ",info:" + info + ".\n";                    }                    textView.setText(message);                }            }        });    }}

3、修改AndroidManifest.xml文件

    

4、MainActivity

public class MainActivity extends AppCompatActivity implements View.OnClickListener {    private NetworkDBManager mNetworkDBManager;    private TextView mTableInfo;    private EditText et1,et2,et3;    @Override    protected void onCreate(Bundle savedInstanceState) {        Log.v("MainActivity", "onCreate");        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        AVOSCloud.initialize(this, "yMNUazdBt872mNtC9aSakjYy-gzGzoHsz", "d4vw3VYdMCjLpsXRhHTBRutC");        mNetworkDBManager = new NetworkDBManager();        et1= (EditText) findViewById(R.id.name);        et2= (EditText) findViewById(R.id.age);        et3= (EditText) findViewById(R.id.sex);        Button networkGet = (Button) findViewById(R.id.network_get);        Button networkPut = (Button) findViewById(R.id.network_put);        mTableInfo = (TextView) findViewById(R.id.table_info);                networkGet.setOnClickListener(this);        networkPut.setOnClickListener(this);            }        @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.network_put:                Person person3 = new Person(et1.getText().toString(), Integer.parseInt(et2.getText().toString()), et3.getText().toString());                mNetworkDBManager.putData(person3);                Toast.makeText(this,"上传成功",Toast.LENGTH_SHORT).show();                break;            case R.id.network_get:                mNetworkDBManager.getData(mTableInfo);                break;            default:                Log.v("MainActivity", "default");                break;        }    }}

【附录】

Demo

  • 一、SharedPreferences
    app内部实现数据存储
    其他app实现共享数据
    【先安装使用第一个并登入才可以使用第二个】

  • 二、文件存储数据
    内部存储
    外部存储

  • 三、SQLite数据库存储数据
    SQLiteDatabase直接操作数据库
    继承SQLiteOpenHelper类操作数据库

  • 四、ContentProvider

  • 五、网络存储数据
    网络存储


整理作者:汪博
少壮不努力,老大徒悲伤。
本文为Android学习规划打造,如有不好的地方请多多指教。

更多相关文章

  1. 一句话锁定MySQL数据占用元凶
  2. Android开发——Android(安卓)Studio下使用Cmake在NDK环境下移植
  3. android实现session保持
  4. Android(安卓)Activity间跳转与传递数据
  5. DICOM入门(四)——Android读取DCM文件图片
  6. Android(安卓)SharedPreferences保存Map集合
  7. cocos2d-x android 添加新场景报错: undefined reference to `vt
  8. Android录音--AudioRecord、MediaRecorder的使用
  9. Android恢复出厂设置流程分析

随机推荐

  1. Android(安卓)开发艺术探索笔记之二 -- I
  2. Android实现导航菜单左右滑动效果
  3. Android(安卓)蓝牙模块
  4. Android(安卓)socket 开发中遇到的问题.
  5. 用CSS3生成的一个漂亮的android客户端页
  6. Android下Affinities和Task
  7. Android(安卓)Push Notification实现信息
  8. Android(安卓)AES加密算法及事实上现
  9. 《第一行代码Android》学习总结第十三章
  10. [转]Android(安卓)开发中的日常积累