AI智能
改变未来

Android 网络请求+数据库操作实现数据的读取及增删改查

文章目录

  • 概述
  • 运行效果
  • 分析
  • 代码实现
  • JSON
  • Book
  • BookDao
  • Book_Async
  • MainActivity
  • BookActivity

概述

综合ListView、SQLite、网络操作来完成一下需求:

  • 程序第一次启动使用GET方式获取JSON数据并展示到ListView控件中,链接:http://www.imooc.com/api/teacher?type=2&page=1
  • 程序第一次启动除了获取展示数据外,还需要创建数据表book,建立列id、learner、name,并将数据插入到book表中
  • 程序每次启动,先从本地数据表book中获取数据,如果有数据则展示数据表book中的数据,如果没有则从网络获取数据展示并更新本地数据表book
    要求:
  1. 使用SQLiteOpenHelper实现book表的创建、增、删、查操作
  2. 使用AsyncTask从网络获取json数据,构造书籍集合并更新数据表book
  3. 在MainActivity类的onCreate()方法中,查询book表中有无数据,如果有数据则绑定ListView并显示,如果没有则从网络获取并显示

运行效果

分析

需要涉及到网络请求,先完成网络权限的配置。功能分为以下几块来实现:

Book: 书籍类,实现序列化接口
BookDao: 书籍数据表,实现书籍数据表的创建和存储,同时提供对外的增删改查功能的接口
Book_Async: 异步类,从网络获取JSON数据,并设置相应适配器

两个Activity
MainActivity: 程序主界面,实现基本UI布局、数据加载、控件点击事件
BookActivity: 添加和修改数据的界面,实现书籍数据表的更新

代码实现

JSON


要获取的是

data

里的JSON数组,通过遍历该数组来获取每一项的数据,数组长度是10,同时只需要id、name、learner三个属性

Book

书籍表类实现了序列化接口,同时声明了三个属性:_id,name,learner

这里一定要有_id!这里一定要有_id!这里一定要有_id!
id前面不能没有 _ !不能没有 _ !不能没有 _!

数据库操作需要使用到游标对象,游标对象的查询是基于_id来查询的,如果没有会报错,所以创建对象的时候要么带有_id属性,要么在后续的数据库操作中选择一个属性来代替_id作为主键
可以在创建数据库的时候将_id设置为自动增长,这样在添加新的

Book

数据的时候就不用管这个_id属性了

public class Book implements Serializable {private String _id, name,learner;public Book() {}public Book(String _id, String name, String learner) {this._id = _id;this.learner = learner;this.name = name;}public String get_id() {return _id;}public void set_id(String _id) {this._id = _id;}public String getLearner() {return learner;}public void setLearner(String learner) {this.learner = learner;}public String getName() {return name;}public void setName(String name) {this.name = name;}}

BookDao

将数据库存放在内部存储中,并实例化一个

SQLiteOpenHelper

辅助来来创建书籍数据库,并创建共有的增删改查方法
查询方法返回的就是一个游标对象,通过游标对象来定位表中对应的数据行

private SQLiteDatabase db;public BookDao(Context context) {// 数据库放置路径,这里放置在内部存储中String path = context.getFilesDir().getAbsolutePath() + \"/book.db\";// 实例化辅助类// 参数1:上下文  参数2:数据库路径   参数3:游标工厂,null 参数4:版本,是否调用升级方法SQLiteOpenHelper helper = new SQLiteOpenHelper(context, path, null, 1) {// 创建@Overridepublic void onCreate(SQLiteDatabase db) {// 创建book表String sql = \"create table book (\" +\"_id integer primary key autoincrement,\" +\"name varchar(50),\" +\"learner varchar(10))\";db.execSQL(sql);}// 升级@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}};// 获取数据库对象/// 如果数据库不存在,则先创建,再打开(第一次运行)/// 如果数据库存在,但是版本没变化,则直接打开/// 如果数据库存在,但是版本号升高了,则调用升级方法,再打开db = helper.getReadableDatabase();}// 增public void add(Book book) {String sql = \"insert into book (name ,learner) values(?,?)\";Object[] args = {book.getName(), book.getLearner()};db.execSQL(sql, args);// 无返回值}// 删public void delete(String learner) {String sql = \"delete from book where learner = \" + learner;db.execSQL(sql);}// 改public void update(Book book) {String sql = \"update book set learner = ?,\" +\"name = ? where _id = ?\";Object[] args = {book.getLearner(), book.getName(), book.get_id()};db.execSQL(sql, args);}// 查public Cursor getAllBooks() {String sql = \"select * from book\";Cursor c = db.rawQuery(sql, null);// 游标对象return c;}

Book_Async

该类继承了AsyncTask父类,因为不涉及结果和过程的传参,后两个参数设置成

Void

类型
这里设置的是从外部传入网络链接,所以第一个参数设置成

String

类型,也可以在

doInBackground

方法里面写链接,这样三个参数都可以设置成

Void

类型

doInBackground()

方法中完成从网络获取数据,将所需数据放入

Book

对象中,并加入数据表

private Book book;private String[] from = {\"learner\", \"name\"};private int[] to = {R.id.learner, R.id.name};@Overrideprotected Void doInBackground(String... strings) {try {// 从网络获取JSON数据URL url = new URL(strings[0]);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod(\"GET\");conn.setConnectTimeout(6000);if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {InputStream is = conn.getInputStream();byte[] b = new byte[1024];int len = 0;ByteArrayOutputStream baos = new ByteArrayOutputStream();while ((len = is.read(b)) > -1) {baos.write(b, 0, len);}String str = new String(baos.toByteArray());// 通过JSONObject来解析JSON数据// 参数:满足JSON格式要求的字符串JSONObject jo = new JSONObject(str);// 获取JSON对象数组JSONArray ary = jo.getJSONArray(\"data\");for (int i = 0; i < ary.length(); i++) {// 获取单个JSON对象JSONObject obj = ary.getJSONObject(i);String id = obj.getString(\"id\");String name = obj.getString(\"name\");String learner = obj.getString(\"learner\");// 向book表添加数据book = new Book(id, name, learner);// 创建book表,添加数据MainActivity.dao.add(book);}}} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (JSONException e) {e.printStackTrace();}return null;}

onPostExecute()

方法中创建简单游标适配器,更新主界面中的ListView的布局

@Overrideprotected void onPostExecute(Void aVoid) {super.onPostExecute(aVoid);// 设置游标适配器MainActivity.c = MainActivity.dao.getAllBooks();MainActivity.adapter = new SimpleCursorAdapter(MainActivity.context, R.layout.item, MainActivity.c, from, to, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);MainActivity.books.setAdapter(MainActivity.adapter);}

MainActivity

为了代码整洁,在

onCreate()

方法中分别实现了初始化数据和初始化事件两个方法:

public static ListView books;public static BookDao dao;public static Map<String, Book> map;public static Context context;public static Cursor c;public static SimpleCursorAdapter adapter;private String learner;private Book_Async ba;private Book book;private int lastPostion = -1;private String[] from = {\"learner\", \"name\"};private int[] to = {R.id.learner, R.id.name};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initData();initEvent();}

initData()

完成基本的初始化,同时完成数据库的判断,如果

book.db

不存在则调用异步类从网络获取数据并创建数据库,如果

book.db

存在,则直接加载本地数据

private void initData() {books = findViewById(R.id.books);context = this;map = new HashMap<>();// 判断数据库是否存在,如果存在则不再通过异步类获取网络数据File f = new File(getFilesDir(),\"book.db\");if (!f.exists()) {dao = new BookDao(this);ba = new Book_Async();ba.execute(\"http://www.imooc.com/api/teacher?type=2&page=1\");}else{dao = new BookDao(this);}}

initEvent()

设置ListView控件的点击事件,用户选中子项时,该子项背景色变为灰色,并保存子项信息,提供给其它方法使用

private void initEvent() {// 为ListView子项添加点击事件books.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {// 将游标移动到指定的book数据处c.moveToPosition(position);learner = c.getString(2);// 获得选中对象book = new Book(c.getString(0), c.getString(1), c.getString(2));// 设置选中子项背景色为灰色view.setBackgroundColor(Color.GRAY);// 将上一次选择的子项颜色恢复为白色if (lastPostion != -1 && lastPostion != position) {books.getChildAt(lastPostion).setBackgroundColor(Color.WHITE);}lastPostion = position;}});}

要实现当用户在修改添加界面完成操作后,返回主界面时主界面数据的更新,所以要把ListView的布局放在MainActivity的

onResume()

方法中

@Overrideprotected void onResume() {super.onResume();// 设置游标适配器c = dao.getAllBooks();adapter = new SimpleCursorAdapter(this, R.layout.item, c, from, to, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);books.setAdapter(adapter);}

最后就是底部添加、删除、修改三个按钮的点击事件
**添加:**点击后直接跳转至添加界面BookActivity
**删除:**获取ListView子项点击事件所记录下的

learner

属性,通过该属性调用BookDao中的删除方法删除数据,并更新游标对象,显示相应提示
**修改:**当用户选中子项后获取对应的

Book

对象,通过实例化一个意图对象,将该对象传输至修改界面,并实现界面跳转,显示相应提示

// 按钮点击事件public void myClick(View v) {switch (v.getId()) {case R.id.add:startActivity(new Intent(this, BookActivity.class));book = null;break;case R.id.delete:dao.delete(learner);// 更新游标对象c.requery();lastPostion = -1;book = null;break;case R.id.change:if (book != null) {Intent it = new Intent(this, BookActivity.class);it.putExtra(\"book\", book);startActivity(it);book = null;} else {Toast.makeText(this, \"请选择要修改的信息\", Toast.LENGTH_SHORT).show();}break;}}

BookActivity

因为控件不多,所以直接在该界面的

onCreate()

方法中进行初始化,同时接受上一个界面传过来的

Book

对象,如果该对象为空,则说明用户是通过添加按钮进入,若对象不为空,这说明用户是通过修改按钮进入

private EditText learnerEdit, nameEdit;private Book b;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_book);learnerEdit = findViewById(R.id.learner_edit);nameEdit = findViewById(R.id.name_edit);// 获取从上一个界面传过来的对象b = (Book) getIntent().getSerializableExtra(\"book\");if (b != null) {nameEdit.setText(b.getName());learnerEdit.setText(b.getLearner());}}

commit()

在该方法中完成提交按钮的单击事件,同样通过

Book

对象是否为空来判断是添加还是修改操作。添加直接调用添加方法,在原有的数据库的最后直接添加数据,修改需要获取当前传入

Book

对象的id,再调用修改方法修改对应id的数据。完成后均弹出相应提示

public void commit(View v) {v.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String learner = learnerEdit.getText().toString();String name = nameEdit.getText().toString();Book book = new Book(\"0\", name, learner);if (b == null) {// 添加操作dao.add(book);Toast.makeText(BookActivity.this, \"添加成功\", Toast.LENGTH_SHORT).show();} else {// 修改操作book.set_id(b.get_id());dao.update(book);Toast.makeText(BookActivity.this, \"修改成功\", Toast.LENGTH_SHORT).show();}learnerEdit.setText(\"\");nameEdit.setText(\"\");}});}
赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Android 网络请求+数据库操作实现数据的读取及增删改查