三、实现ServerNoteRepository
ServerNoteRepository作为新的数据仓库实现类,它通过访问服务端的各个操作接口来实现数据的提交和获取。我们仍然使用okhttp3开源框架来完成这些基于HTTP协议的网络操作。
3.1 修改HttpHelper类
首先打开帮助类HttpHelper,向里面添加相关的常量和URL获取方法。
增加如下的常量定义:
private static final String NOTE_URL = HTTP_PREFIX + "note.php";
private static final String SAVE_NOTE_URL = NOTE_URL + "?p=save";
private static final String FIND_NOTE_URL = NOTE_URL + "?p=find&id=";
private static final String FIND_ALL_NOTE_URL = NOTE_URL + "?p=findall";
private static final String NOTEBOOK_URL = HTTP_PREFIX + "notebook.php";
private static final String SAVE_NOTEBOOK_URL = NOTEBOOK_URL + "?p=save";
private static final String FIND_NOTEBOOK_URL = NOTEBOOK_URL + "?p=find&id=";
private static final String FIND_ALL_NOTEBOOK_URL = NOTEBOOK_URL + "?p=findall";
增加如下的方法:
// 获取保存笔记页面地址
public static String getSaveNoteUrl() {
return SAVE_NOTE_URL;
}
// 获取根据id查找笔记页面地址
public static String getFindNoteUrl(long id) {
return FIND_NOTE_URL + id;
}
// 获取查找全部笔记页面地址
public static String getFindAllNoteUrl() {
return FIND_ALL_NOTE_URL;
}
// 获取保存笔记本页面地址
public static String getSaveNotebookUrl() {
return SAVE_NOTEBOOK_URL;
}
// 获取根据id查找笔记本页面地址
public static String getFindNotebookUrl(long id) {
return FIND_NOTEBOOK_URL + id;
}
// 获取查找全部笔记本页面地址
public static String getFindAllNotebookUrl() {
return FIND_ALL_NOTEBOOK_URL;
}
3.2 完成ServerNoteRepository类
3.2.1 增加构造方法以获取Context对象
为ServerNoteRepository类增加Context类型的成员,并为其创建构造方法,从外界接收一个Context对象进行初始化:
private Context context;
public ServerNoteRepository(Context context) {
this.context = context;
}
3.2.2 构造单例模式
仍然按照基于本地数据库的NoteRepository的方式构造成单例。向ServerNoteRepository类中添加如下代码:
private static ServerNoteRepository sInstance;
//获取单例对象
public static ServerNoteRepository getInstance(Context context) {
if (sInstance == null) {
sInstance = new ServerNoteRepository(context);
}
return sInstance;
}
3.2.3 getAllNotes()
这里应当访问对应的获取全部笔记记录的接口,并提供当前email账号。对返回的结果(JSON)进行解析,得到笔记对象列表。在编写代码如下:
@Override
public ArrayList<Note> getAllNotes() {
ArrayList<Note> notes = new ArrayList<>();
String email = Utils.getUserEmail(context);
// 当前并没有登录
if (TextUtils.isEmpty(email)) {
return notes;
}
OkHttpClient client = new OkHttpClient();
// 创建包含email账号的表单,
FormBody.Builder fb = new FormBody.Builder();
FormBody formBody = fb.add("email", email)
.build();
// 创建获取全部笔记的请求
Request request = new Request.Builder()
.url(HttpHelper.getFindAllNoteUrl())
.post(formBody)
.build();
Call call = client.newCall(request);
try {
// 执行请求并返回结果
Response response = call.execute();
if (response != null) {
String json = response.body().string();
if (!TextUtils.isEmpty(json)) {
JSONObject jo = new JSONObject(json);
int code = jo.optInt("code", 0);
if (code == 1) {
JSONArray array = jo.optJSONArray("noteList");
// 逐个解析出获取到的笔记,并插入列表
for (int i = 0; i < array.length(); i++) {
JSONObject jnote = array.getJSONObject(i);
long id = jnote.optLong("id");
String title = jnote.optString("title");
String content = jnote.optString("content");
long createTime = jnote.optLong("createTime");
long notebookId = jnote.optLong("notebookId");
Note note = new Note(id, title, content, createTime, notebookId);
notes.add(note);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return notes;
}
3.2.4 saveNote()
在这里,要将参数传来的Note对象信息提交到服务器端并存储。成功后将返回新存储的笔记的id。将此id设置给被保存的note对象并作为方法返回值。
编写代码如下:
@Override
public Note saveNote(Note note) {
String email = Utils.getUserEmail(context);
if (note == null || TextUtils.isEmpty(email)) {
return note;
}
OkHttpClient client = new OkHttpClient();
// 创建包含被保存笔记信息和email账号的表单,
FormBody.Builder fb = new FormBody.Builder();
FormBody formBody = fb.add("email", email)
.add("title", note.getTitle())
.add("content", note.getContent())
.add("createTime", String.valueOf(note.getCreateTime()))
.add("notebookId", String.valueOf(note.getNotebookId()))
.build();
// 创建保存笔记的请求
Request request = new Request.Builder()
.url(HttpHelper.getSaveNoteUrl())
.post(formBody)
.build();
Call call = client.newCall(request);
try {
Response response = call.execute();
if (response != null) {
String json = response.body().string();
if (!TextUtils.isEmpty(json)) {
JSONObject jo = new JSONObject(json);
int code = jo.optInt("code", 0);
if (code == 1) { // 提交成功
long id = jo.optLong("id");
if (id <= 0) {
return null;
}
note.setId(id);
} else { // 保存失败,返回null
return null;
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return note;
}
3.2.4 getNote()
这里根据参数指定的笔记id提交服务器查询,在根据返回的数据生成笔记对象。编写代码如下:
@Override
public Note getNote(long noteId) {
// 检查登录状态及参数合法性
String email = Utils.getUserEmail(context);
if (noteId <= 0 || TextUtils.isEmpty(email)) {
return null;
}
OkHttpClient client = new OkHttpClient();
// 创建包含email账号的表单,
FormBody.Builder fb = new FormBody.Builder();
FormBody formBody = fb.add("email", email)
.build();
// 创建查找笔记的请求
Request request = new Request.Builder()
.url(HttpHelper.getFindNoteUrl(noteId))
.post(formBody)
.build();
Call call = client.newCall(request);
try {
Response response = call.execute();
if (response != null) {
String json = response.body().string();
if (!TextUtils.isEmpty(json)) {
JSONObject jo = new JSONObject(json);
int code = jo.optInt("code", 0);
if (code == 1) { // 提交成功
JSONObject jnote = jo.optJSONObject("note");
long id = jnote.optLong("id");
String title = jnote.optString("title");
String content = jnote.optString("content");
long createTime = jnote.optLong("createTime");
long notebookId = jnote.optLong("notebookId");
return new Note(id, title, content, createTime, notebookId);
} else { // 保存失败,返回null
return null;
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
3.2.5 saveNotebook()
向服务器提交并保存参数给出的笔记本对象。与保存笔记对象思路基本一致。编写代码如下:
@Override
public Notebook saveNotebook(Notebook notebook) {
String email = Utils.getUserEmail(context);
if (notebook == null || TextUtils.isEmpty(email)) {
return notebook;
}
OkHttpClient client = new OkHttpClient();
// 创建包含被保存笔记本信息和email账号的表单,
FormBody.Builder fb = new FormBody.Builder();
FormBody formBody = fb.add("email", email)
.add("name", notebook.getName())
.build();
// 创建保存笔记本的请求
Request request = new Request.Builder()
.url(HttpHelper.getSaveNotebookUrl())
.post(formBody)
.build();
Call call = client.newCall(request);
try {
Response response = call.execute();
if (response != null) {
String json = response.body().string();
if (!TextUtils.isEmpty(json)) {
JSONObject jo = new JSONObject(json);
int code = jo.optInt("code", 0);
if (code == 1) { // 提交成功
long id = jo.optLong("id");
if (id <= 0) {
return null;
}
notebook.setId(id);
} else { // 保存失败,返回null
return null;
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return notebook;
}
3.2.6 getAllNotebooks()
编写代码如下:
@Override
public ArrayList<Notebook> getAllNotebooks() {
ArrayList<Notebook> notebooks = new ArrayList<>();
String email = Utils.getUserEmail(context);
// 当前并没有登录
if (TextUtils.isEmpty(email)) {
return notebooks;
}
OkHttpClient client = new OkHttpClient();
// 创建包含email账号的表单,
FormBody.Builder fb = new FormBody.Builder();
FormBody formBody = fb.add("email", email)
.build();
// 创建获取全部笔记本的请求
Request request = new Request.Builder()
.url(HttpHelper.getFindAllNotebookUrl())
.post(formBody)
.build();
Call call = client.newCall(request);
try {
// 执行请求并返回结果
Response response = call.execute();
if (response != null) {
String json = response.body().string();
if (!TextUtils.isEmpty(json)) {
JSONObject jo = new JSONObject(json);
int code = jo.optInt("code", 0);
if (code == 1) {
JSONArray array = jo.optJSONArray("notebookList");
// 逐个解析出获取到的笔记本,并插入列表
for (int i = 0; i < array.length(); i++) {
JSONObject jnote = array.getJSONObject(i);
long id = jnote.optLong("id");
String name = jnote.optString("name");
Notebook nb = new Notebook(id, name);
notebooks.add(nb);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return notebooks;
}
3.2.7 getNotebook()
这里同样根据给出的id获取笔记本,实际上我们目前没有用到这个方法。为了使架构完备,还是补齐它的实现:
@Override
public Notebook getNotebook(long notebookId) {
// 检查登录状态及参数合法性
String email = Utils.getUserEmail(context);
if (notebookId <= 0 || TextUtils.isEmpty(email)) {
return null;
}
OkHttpClient client = new OkHttpClient();
// 创建包含email账号的表单,
FormBody.Builder fb = new FormBody.Builder();
FormBody formBody = fb.add("email", email)
.build();
// 创建查找笔记的请求
Request request = new Request.Builder()
.url(HttpHelper.getFindNotebookUrl(notebookId))
.post(formBody)
.build();
Call call = client.newCall(request);
try {
Response response = call.execute();
if (response != null) {
String json = response.body().string();
if (!TextUtils.isEmpty(json)) {
JSONObject jo = new JSONObject(json);
int code = jo.optInt("code", 0);
if (code == 1) { // 提交成功
JSONObject jnote = jo.optJSONObject("note");
long id = jnote.optLong("id");
String name = jnote.optString("name");
return new Notebook(id, name);
} else { // 保存失败,返回null
return null;
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
3.2.8 修改对笔记本数据访问的调用
在任务6.1中实现的笔记本功能相对比较草率,忽略了对耗时操作的处理。我们来进行修正。
打开NotebooksActivity类。找到其中的onNewNotebook()方法,按如下的方式改写这个方法:
public void onNewNotebook(View view) {
// 创建编辑框对象
final EditText notebookNameEdit = new EditText(this);
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setTitle(R.string.new_notebook)
.setView(notebookNameEdit) // 将编辑框对象加入对话框
.setNegativeButton("取消", null)
.setPositiveButton("确认", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final String name = notebookNameEdit.getEditableText().toString();
if (!TextUtils.isEmpty(name)) {
// 异步方式处理笔记本数据提交
AsyncTask<Void, Void, Notebook> task = new AsyncTask<Void, Void, Notebook>() {
@Override
protected Notebook doInBackground(Void... voids) {
Notebook nb = new Notebook(0, name);
nb = NoteRepositoryFactory.getNoteRepository(getApplicationContext()).saveNotebook(nb);
return nb;
}
@Override
protected void onPostExecute(Notebook notebook) {
if (notebook != null) {
// 插入成功,刷新笔记本列表
asyncLoadNotebooks();
} else {
// 插入失败,提示用户
Toast.makeText(NotebooksActivity.this, "保存笔记本失败", Toast.LENGTH_SHORT).show();
}
}
};
task.execute();
}
}
});
builder.show();
}
同时找到以下代码:
ArrayList<Notebook> nbs = NoteDAO.getInstance(NotebooksActivity.this.getApplicationContext()).queryAllNotebooks();
将其替换为:
ArrayList<Notebook> nbs = NoteRepositoryFactory.getNoteRepository(getApplicationContext()).getAllNotebooks();
3.2.9 在NoteRepositoryFactory中增加对ServerNoteRepository的支持
打开工厂类NoteRepositoryFactory,按如下代码改写getNoteRepository()方法:
public static INoteRepository getNoteRepository(Context context) {
switch (BuildConfig.USE_TEST_REPO) {
case "test":
return TestNoteRepository.getInstance();
case "database":
return NoteRepository.getInstance(context);
case "server":
return ServerNoteRepository.getInstance(context);
}
return null;
}
3.2.10 修改build.gradle脚本
打开build.gradle文件,找到如下的区域:
buildTypes {
...
}
改写为如下形式:
buildTypes {
debug {
buildConfigField "String", "USE_TEST_REPO", '"server"'
}
release {
buildConfigField "String", "USE_TEST_REPO", '"server"'
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
repoTest {
buildConfigField "String", "USE_TEST_REPO", '"test"'
}
}
到此为止,基本的服务器端数据存储访问就完成了,可以运行程序体验效果。