[Android] 05 - Local & Remote DB

  • 本地文件存储管理
    • Content Provider: 由于Android系统中,数据基本都是私有的的,都是存放于“data/data/程序包名”目录下,所以要实现数据共享,正确方式是使用Content Provider。
    • SQLite: SQLite是一个轻量级的数据库,支持基本SQL语法,是常被采用的一种数据存储方式。
    • SharedPreference: 除SQLite数据库外,另一种常用的数据存储方式,其本质就是一个xml文件,常用于存储较简单的参数设置。
    • File: 即常说的文件(I/O)存储方法,常用语存储大数量的数据,但是缺点是更新数据将是一件困难的事情。
  • 以及远程管理:Google Protocol Buffer embedded in Netty

  

基本法:

文件存储读写【类似java,不推荐】

 

XML法 - SharedPreferences:

一些用户参数,内容少,如果使用数据库的话,显得有点大材小用了,可以使用SharedPreferences。

    • windows使用ini文件,
    • J2SE中使用properties属性文件与xml文件来保存软件的配置信息;
    • Android中我们通常使用 一个轻量级的存储类——SharedPreferences 来保存用户偏好的参数【xml文件, 然后类似于Map集合,使用键-值的形式来存储数据】

举例子还是三个类:layout,function,main,当然有必要来个加密。

  • 编写简单的SP工具类:SharedHelper.java
/**
 * Created by Jay on 2015/9/2 0002.
 */
public class SharedHelper {

    private Context mContext;

    public SharedHelper() {
}
public SharedHelper(Context mContext) { this.mContext = mContext; } public void save(String username, String passwd) {
SharedPreferences sp
= mContext.getSharedPreferences("mysp", Context.MODE_PRIVATE); SharedPreferences.Editor editor = sp.edit(); editor.putString("username", username); editor.putString("passwd", passwd); editor.commit(); Toast.makeText(mContext, "信息已写入SharedPreference中", Toast.LENGTH_SHORT).show(); } public Map<String, String> read() {
Map
<String, String> data = new HashMap<String, String>(); SharedPreferences sp = mContext.getSharedPreferences("mysp", Context.MODE_PRIVATE); data.put("username", sp.getString("username", "")); data.put("passwd", sp.getString("passwd", "")); return data; } } 
  • 最后是MainActivity.java实现相关逻辑:
public class MainActivity extends AppCompatActivity {

    private EditText editname;
    private EditText editpasswd;
    private Button btnlogin;
    private String strname;
    private String strpasswd;
    private SharedHelper sh;
    private Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = getApplicationContext();
        sh = new SharedHelper(mContext);
        bindViews();  // ---->
    }

    private void bindViews() {
        editname   = (EditText)findViewById(R.id.editname);
        editpasswd = (EditText)findViewById(R.id.editpasswd);
        btnlogin   = (Button)  findViewById(R.id.btnlogin);
        btnlogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                strname   = editname.getText().toString();
                strpasswd = editpasswd.getText().toString();
                sh.save(strname,strpasswd);
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        Map<String,String> data = sh.read();
        editname.setText(data.get("username"));
        editpasswd.setText(data.get("passwd"));
    }
}

 

  • 使用MD5对SharedPreference的重要数据进行加密

 

流程图解析:

    • Step 1.用户注册账号密码,账号密码校验后(账号是否重复,密码位数 > 6位等), 即账号密码有效,注册成功后,我们提交给服务器的账号,以及本地加密过的密码!
    • Step 2.服务器端将用户提交的账号,加密过的密码保存到服务端的数据库中,也就是服务端并不会保存我们的明文密码(原始)密码!
    • Step 3.说回客户端,如果注册成功或者登陆成功,你想保存账号密码到SP中,保存的的密码 也需要走一趟加密流程!即明文密码——>加密,再保存!如果不保存,每次请求的时候,明文密码 也要走一趟家里流程,然后拿着加密后的密码来请求服务器
    • Step 4.服务器验证账号以及加密密码,成功,分配客户端一个session标识,后续客户端可以拿着 这个session来访问服务端提供的相关服务

 

/**
 * Created by Jay on 2015/9/2 0002.
 */
public class MD5 {
public static String getMD5(String content) {    // 迭代100次,就不好破解了 try { MessageDigest digest = MessageDigest.getInstance("MD5"); digest.update(content.getBytes()); return getHashString(digest); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } private static String getHashString(MessageDigest digest) { StringBuilder builder = new StringBuilder(); for (byte b : digest.digest()) { builder.append(Integer.toHexString((b >> 4) & 0xf)); builder.append(Integer.toHexString(b & 0xf)); } return builder.toString(); } }

 

package com.jay.sharedpreferencedemo3;

import android.content.Context;
import android.content.SharedPreferences;

import java.util.Map;

/**
 * Created by Jay on 2015/9/2 0002.
 */
public class SPUtils {
    /**
     * 保存在手机里的SP文件名
     */
    public static final String FILE_NAME = "my_sp";

    /**
     * 保存数据
     */
    public static void put(Context context, String key, Object obj) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        if (obj instanceof Boolean) {
            editor.putBoolean(key, (Boolean) obj);
        } else if (obj instanceof Float) {
            editor.putFloat(key, (Float) obj);
        } else if (obj instanceof Integer) {
            editor.putInt(key, (Integer) obj);
        } else if (obj instanceof Long) {
            editor.putLong(key, (Long) obj);
        } else {
            editor.putString(key, (String) obj);
        }
        editor.commit();
    }


    /**
     * 获取指定数据
     */
    public static Object get(Context context, String key, Object defaultObj) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
        if (defaultObj instanceof Boolean) {
            return sp.getBoolean(key, (Boolean) defaultObj);
        } else if (defaultObj instanceof Float) {
            return sp.getFloat(key, (Float) defaultObj);
        } else if (defaultObj instanceof Integer) {
            return sp.getInt(key, (Integer) defaultObj);
        } else if (defaultObj instanceof Long) {
            return sp.getLong(key, (Long) defaultObj);
        } else if (defaultObj instanceof String) {
            return sp.getString(key, (String) defaultObj);
        }
        return null;
    }

    /**
     * 删除指定数据
     */
    public static void remove(Context context, String key) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.remove(key);
        editor.commit();
    }


    /**
     * 返回所有键值对
     */
    public static Map<String, ?> getAll(Context context) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
        Map<String, ?> map = sp.getAll();
        return map;
    }

    /**
     * 删除所有数据
     */
    public static void clear(Context context) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.clear();
        editor.commit();
    }

    /**
     * 检查key对应的数据是否存在
     */
    public static boolean contains(Context context, String key) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, context.MODE_PRIVATE);
        return sp.contains(key);
    }

}
SharedPreference工具类

 

 

SQLite数据库

背景介绍

Ref: http://www.runoob.com/w3cnote/android-tutorial-sqlite-intro.html

一个保守稳定的选择,全平台移植。

Ref: Access、SQLite、HSQLDB、Sybase、MySQL、DB4O

db4o,另一个选择:

db4o OVER 500 TIMES FASTER THAN SQLITE

also Look at Here; you yourself will decide :

http://polepos.sourceforge.net/results/html/barcelona_write.html

 

① SQLite是一个轻量级的关系型数据库

② 前面我们学习了使用文件与SharedPreference来保存数据,但是在很多情况下, 文件并不一定是有效的,如多线程并发访问是相关的

③ SQLite支持五种数据类型:NULL, INTEGER, REAL(浮点数), TEXT(字符串文本)和BLOB(二进制对象)

    虽然只有五种,但是对于varchar,char等其他数据类型都是可以保存的;因为SQLite有个最大的特点: 你可以各种数据类型的数据保存到任何字段中而不用关心字段声明的数据类型是什么,

    比如你 可以在Integer类型的字段中存放字符串,当然除了声明为主键INTEGER PRIMARY KEY的字段只能够存储64位整数!

    另外, SQLite 在解析CREATE TABLE 语句时, 会忽略 CREATE TABLE 语句中跟在字段名后面的数据类型信息如下面语句会忽略 name字段的类型信息: CREATE TABLE person (personid integer primary key autoincrement, name varchar(20))

 

In the nutshell:

SQlite通过文件来保存数据库,一个文件就是一个数据库,数据库中又包含多个表格,表格里又有 多条记录,每个记录由多个字段构成,每个字段有对应的,每个值我们可以指定类型,也可以不指定 类型(主键除外)。

 

导出数据库文件时遇到的问题:Android Device Monitor Cannot Open Data Folder Resolve Method【权限问题 --> 需adb root】

 

Save data into SQLite database [Beginner Android Studio Example]

Android SQLite Course [not free]

mitchtabian/Android-SQLite-Beginner-Course [work]

 

public void AddData(String newEntry) {
  boolean insertData = mDatabaseHelper.addData(newEntry);

  if (insertData) {
    toastMessage("Data Successfully Inserted!");
  } else {
    toastMessage("Something went wrong");
  }
}

需要一个业界潜规则的类来管理数据处理:

mDatabaseHelper = new DatabaseHelper(this);

数据处理类的定义:

public class DatabaseHelper extends SQLiteOpenHelper {

    private static final String TAG = "DatabaseHelper";

    private static final String TABLE_NAME = "people_table";
    private static final String COL1 = "ID";
    private static final String COL2 = "name";


    public DatabaseHelper(Context context) {
        super(context, TABLE_NAME, null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String createTable = "CREATE TABLE " + TABLE_NAME + " (ID INTEGER PRIMARY KEY AUTOINCREMENT, " + COL2 +" TEXT)";
        db.execSQL(createTable);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int i, int i1) {
        db.execSQL("DROP IF TABLE EXISTS " + TABLE_NAME);
        onCreate(db);
    }

    public boolean addData(String item) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues contentValues = newContentValues();
        contentValues.put(COL2, item);                      // (key, value)
long result = db.insert(TABLE_NAME, null, contentValues); //if date as inserted incorrectly it will return -1 if (result == -1) { return false; } else { return true; } } /** * Returns all the data from database * @return */ public Cursor getData(){ SQLiteDatabase db = this.getWritableDatabase(); String query = "SELECT * FROM " + TABLE_NAME; Cursor data = db.rawQuery(query, null); return data; } /** * Returns only the ID that matches the name passed in * @param name * @return */ public Cursor getItemID(String name){ SQLiteDatabase db = this.getWritableDatabase(); String query = "SELECT " + COL1 + " FROM " + TABLE_NAME + " WHERE " + COL2 + " = '" + name + "'"; Cursor data = db.rawQuery(query, null); return data; } /** * Updates the name field * @param newName * @param id * @param oldName */ public void updateName(String newName, int id, String oldName){ SQLiteDatabase db = this.getWritableDatabase(); String query = "UPDATE " + TABLE_NAME + " SET " + COL2 + " = '" + newName + "' WHERE " + COL1 + " = '" + id + "'" + " AND " + COL2 + " = '" + oldName + "'"; Log.d(TAG, "updateName: query: " + query); Log.d(TAG, "updateName: Setting name to " + newName); db.execSQL(query); } /** * Delete from database * @param id * @param name */ public void deleteName(int id, String name){ SQLiteDatabase db = this.getWritableDatabase(); String query = "DELETE FROM " + TABLE_NAME + " WHERE " + COL1 + " = '" + id + "'" + " AND " + COL2 + " = '" + name + "'"; Log.d(TAG, "deleteName: query: " + query); Log.d(TAG, "deleteName: Deleting " + name + " from database."); db.execSQL(query); } }

 

 

自从有了aws, firebase之后,netty server的价值还剩多少? (ing ...)


 

Android项目开发中的网络通信设计

Part 1 

一部分基础知识,但实际应用中,我们还是更愿意选择高层次的封装提供的简单点的api:

 

Part 2

JDK强迫你必须用socket来写服务器,实际上是很繁琐的。缺乏一个高层次的api。
Netty说,我来写jdk的socket,并给你一个新的更简洁的api,你傻瓜式的就能写好一个网络服务器(而且是event-driven/proactor/reactor等等)。

当然,netty通过jni,引入了epoll这样的linux系统调用,使得它不单单是jdk的一个简单包裹,的确也加了些东西进去。

 

Part 3

这个东西貌似有点老旧,现在流行什么呢?也可能穿了马甲换了外貌。

Spring boot, vertx

 

RxJava+Rxandroid+retrofit

为什么要学 RxJava?

就目前的情况,Android 的网络库基本被 Retrofit + OkHttp 一统天下了,而配合上响应式编程 RxJava 可谓如鱼得水。

springboot和netty如何整合呢

 

/* implement */ 


Ref: 给 Android 开发者的 RxJava 详解【较详细】

Ref: RxJava,你好【0基础】

 

 

posted @ 2017-12-19 15:19  郝壹贰叁  阅读(242)  评论(0)    收藏  举报