Android SQLite+网络请求Demo

本文详细介绍了一个结合ListView、SQLite和网络操作的应用案例,包括广告图片显示、文章内容展示、收藏功能及阅读界面设计,实现了从网络获取数据、本地存储与检索的全过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

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

  • 页显示内容:广告图片、文章标题、书本、内容
  • 点击【上一篇】显示上一篇文章(实时修改页面文章标题、作者、内容,图片不变),到了最前面直接显示最后一篇。点击【下一篇】显示下一篇文章,到了最后一篇再点击显示第一批(一共30篇)
  • 点击【收藏】按钮,如果该文章已被收藏,则不进行重复收藏并给出相关提示。若未被收藏过并此次收藏成功,则提示“收藏成功”.
  • 点击【我的收藏】进入新界面,显示所有被收藏的文章标题列表,点击列表,进入详情显示。

运行效果

分析

需要涉及到网络请求,先完成网络权限的配置。功能分为以下几块来实现:
Book: 书本类
BookDbHelper: 数据库辅助类,创建数据库,并使用单例模式创建数据库对象
BookDao: 用于向外公布对Book数据库的操作方法
BookBiz: 书本业务类,获取、解析书本网络数据
ImageFile: 图片文件操作类,向外公布对图片对象的下载及存储方法
ImageBiz: 图片业务类,获取、解析图片网络数据


三个Activity
MainActivity: 程序主界面,显示广告图片、文章标题、作者、内容
MyCollectionActivity: 我的收藏,显示收藏的书本信息
ReadActivity: 阅读界面,显示书本标题、作者和内容
使用到的链接:
广告图片
http://www.imooc.com/api/teacher?type=1
文章
参数(cid=?):书本id:可选数字1~30
http://www.imooc.com/api/teacher?type=3&cid=1

代码实现

因为涉及到网络操作,所以先在manifest文件里完成相关的配置,同时图片的加载我希望是只在第一次运行的时候从网络获取,然后保存到本地,第二次运行可以直接从本地获取,所以在manifest文件中也要完成对外部存储权限的申请

    <!--外部存储权限-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Book

此类声明了_id、title、author、content四个String类型的私有属性,同时也声明了五个跟数据库列名有关的常量
因为_id这个属性我希望在数据库中它是自增的,实例化Book对象的时候并不需要赋值,所以这里声明了三个参数的构造方法

public class Book {
    private String _id,title,author,content;

    public static final String TABLE_NAME = "tb_book";
    public static final String COL_ID = "_id";
    public static final String COL_TITLE = "title";
    public static final String COL_AUTHOR = "author";
    public static final String COL_CONTENT = "content";

    public Book() {

    }

    public Book(String title, String author, String content) {
        this.title = title;
        this.author = author;
        this.content = content;
    }

    public String get_id() {
        return _id;
    }

    public void set_id(String _id) {
        this._id = _id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public static String getTableName() {
        return TABLE_NAME;
    }

    public static String getColId() {
        return COL_ID;
    }

    public static String getColTitle() {
        return COL_TITLE;
    }

    public static String getColAuthor() {
        return COL_AUTHOR;
    }

    public static String getColContent() {
        return COL_CONTENT;
    }
}

BookDbHelper

数据库辅助类,继承了SQLiteOpenHelper,实现创建数据库,并使用单例模式创建数据库对象
因为整个程序运行过程中有且仅有一个书本数据库,所以将它设为单例模式,也方便后续操作
创建数据表的时候可以使用Book类里声明的变量

public class BookDbHelper extends SQLiteOpenHelper {

    private static final String DB_NAME = "db_book.db";
    private static final int VERSION = 1;

    // 使用懒汉式单例模式,只存在一个BookDbHelper
    private static BookDbHelper bookDbHelper;

    public static synchronized BookDbHelper getInstance(Context context) {
        if (bookDbHelper == null) {
            bookDbHelper = new BookDbHelper(context.getApplicationContext());
        }
        return bookDbHelper;
    }

	// 构造函数
    public BookDbHelper(@Nullable Context context) {
        super(context, DB_NAME, null, VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // 数据库表格创建
        db.execSQL("CREATE TABLE IF NOT EXISTS " + Book.TABLE_NAME
                + "("
                + Book.COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
                + Book.COL_TITLE + " VARCHAR,"
                + Book.COL_AUTHOR + " VARCHAR,"
                + Book.COL_CONTENT + " VARCHAR"
                + ")");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

BookDao

用于对外公布对Book数据库的操作方法:
loadFromDb: 从数据库中获取数据
insert2Db: 将数据存入数据库

public class BookDao {

    // 从数据库中获取
    public List<Book> loadFromDb(Context context) {

        BookDbHelper dbHelper = BookDbHelper.getInstance(context);
        SQLiteDatabase db = dbHelper.getReadableDatabase();

        List<Book> bookList = new ArrayList<>();

        Cursor cursor = db.rawQuery("SELECT * FROM " + Book.TABLE_NAME, null);
        Book book = null;
        while(cursor.moveToNext()){
            String id = cursor.getString(cursor.getColumnIndex(Book.COL_ID));
            String title = cursor.getString(cursor.getColumnIndex(Book.COL_TITLE));
            String author = cursor.getString(cursor.getColumnIndex(Book.COL_AUTHOR));
            String content = cursor.getString(cursor.getColumnIndex(Book.COL_CONTENT));
            book = new Book(title,author,content);
            bookList.add(book);
        }
        cursor.close();
        return bookList;
    }

    // 缓存入数据库
    public void insert2Db(Context context, List<Book> bookList) {
        // 校验传入数据
        if (context == null) {
            // context不能为空
            throw new IllegalArgumentException("context can not be null.");
        }

        if (bookList == null || bookList.isEmpty()) {
            return;
        }

        BookDbHelper dbHelper = BookDbHelper.getInstance(context);
        SQLiteDatabase db = dbHelper.getWritableDatabase();

        db.beginTransaction();

        ContentValues contentValues = null;

        for (Book book : bookList) {
            contentValues = new ContentValues();
            contentValues.put(Book.COL_ID, book.get_id());
            contentValues.put(Book.COL_TITLE, book.getTitle());
            contentValues.put(Book.COL_AUTHOR, book.getAuthor());
            contentValues.put(Book.COL_CONTENT, book.getContent());
            db.insertWithOnConflict(Book.TABLE_NAME,
                    null,
                    contentValues,
                    SQLiteDatabase.CONFLICT_REPLACE);
        }

        db.setTransactionSuccessful();
        db.endTransaction();
    }
}

本身没有相关需求,这里省略了删除和修改操作

BookBiz

书本业务类,通过异步方式获取、解析书本网络数据,并根据获取情况通过一个回调接口来返回相应对象

public class BookBiz {

    private BookDao bookDao = new BookDao();

    // 获取网络数据
    // 参数1:环境上下文    参数2:根据获取情况返回相应对象    参数3:是否使用缓存
    public void loadBookDatas(final Context context, final CallBack callBack, final boolean useCache) {

        // 通过异步类获取网络数据
        AsyncTask<Boolean, Void, List<Book>> asyncTask = new AsyncTask<Boolean, Void, List<Book>>() {

            private Exception e;

            @Override
            protected List<Book> doInBackground(Boolean... booleans) {
                boolean isUseCache = booleans[0];
                List<Book> bookList = new ArrayList<>();
                try {
                    if (isUseCache) {
                        // 从db中获取数据
                        bookList.addAll(bookDao.loadFromDb(context));
                    }
                    // isUseCache为false或者无法从缓存获取数据,bookList都是空的
                    // 当bookList为空时,直接从网络获取数据
                    if (bookList.isEmpty()) {
                        List<Book> bookListFromNet = loadFromNet(context);
                        bookList.addAll(bookListFromNet);
                        // 缓存至db
                        bookDao.insert2Db(context, bookListFromNet);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    this.e = e;
                }
                return bookList;
            }

            @Override
            protected void onPostExecute(List<Book> books) {
                super.onPostExecute(books);
                // 根据请求数据的情况调用接口来传递数据
                if (e != null) {
                    callBack.onFailed(e);
                    return;
                }
                callBack.onSuccess(books);
            }
        };

        asyncTask.execute(useCache);
    }

    // 从网络获取数据(三十个)
    private List<Book> loadFromNet(Context context) {
        List<Book> bookList = new ArrayList<>();
        for (int i = 1; i <= 30; i++) {
            String urlBook = "http://www.imooc.com/api/teacher?type=3&cid="+i;
            // 发请求获取json数据
            String jsonstr = HttpUtils.doGet(urlBook).toString();
            // 当jsonstr不为空时,解析json数据
            if (jsonstr != null) {
                bookList.add(parseJsonstr(jsonstr));
            }
        }
        return bookList;
    }

    // 解析数据
    private Book parseJsonstr(String jsonstr) {
        try {
            JSONObject root = new JSONObject(jsonstr);
            String msgstr = root.optString("msg");
            if (msgstr.equals("成功")) {
                root = root.getJSONObject("data");
                String title = root.getString("title");
                String author = root.getString("author");
                String content = root.getString("content");
                Book book = new Book(title,author,content);
                return book;
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return null;
    }

    // 返回接口
    public static interface CallBack {
        void onSuccess(List<Book> bookList);
        void onFailed(Exception e);
    }
}

ImageFile

图片文件操作类,向外公布对图片对象的下载及存储方法
这里存储图片对象优先选择外部存储,存储在手机相册中,如果外部存储不可用再使用内部存储
loadFromFile: 从外部存储(手机相册)或内部存储中读取图片对象
Insert2File: 将图片通过字节数组以JPG格式存储在外部存储(手机相册)或内部存储中

public class ImageFile {

    private File f;

    // 从外部存储中读取图片对象
    public Bitmap loadFromFile(Context context) {
        // 判断外部存储是否可用,不可用则使用内部存储
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
            if (f == null) {
                f = new File(context.getExternalFilesDir(Environment.DIRECTORY_DCIM), "image.jpg");
            }
        }else{
            f = new File(context.getFilesDir(),"img.jpg");
        }

        // 若文件存在则读取,并转换成Bitmap对象
        if (f.exists()) {
            Bitmap bm = BitmapFactory.decodeFile(f.toString(), new BitmapFactory.Options());
            return bm;
        }
        return null;
    }

    // 将字节数组以JPG格式存储到外部存储中
    public void Insert2File(Context context, Bitmap bitmap) {
        // 判断外部存储是否可用,不可用则使用内部存储
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
            if (f == null) {
                f = new File(context.getExternalFilesDir(Environment.DIRECTORY_DCIM), "image.jpg");
            }
        }else{
            f = new File(context.getFilesDir(),"img.jpg");
        }

        // 转换为字节流存储
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        byte[] bytes = baos.toByteArray();
        if (!f.exists()) {
            try {
                f.createNewFile();
                FileOutputStream fos = new FileOutputStream(f);
                fos.write(bytes);
                fos.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

ImageBiz

图片业务类,通过异步方式获取、解析图片网络数据,并设置控件显示

public class ImageBiz {

    private ImageFile imageFile = new ImageFile();

    public void loadImageDatas(final Context context, Boolean useCache) {
        // 通过异步类获取网络图片
        AsyncTask<Boolean, Void, Bitmap> asyncTask = new AsyncTask<Boolean, Void, Bitmap>() {
            @Override
            protected Bitmap doInBackground(Boolean... booleans) {
                Boolean isUseCache = booleans[0];
                Bitmap bm = null;
                if (isUseCache) {
                    // 在本地中获取Bitmap对象
                    bm = imageFile.loadFromFile(context);
                }
                if (bm == null) {
                    // 从网络获取数据
                    bm = loadFromNet(context);
                    // 存入本地文件
                    imageFile.Insert2File(context,bm);
                }
                return bm;
            }

            @Override
            protected void onPostExecute(Bitmap bitmap) {
                super.onPostExecute(bitmap);
                MainActivity.img.setImageBitmap(bitmap);
            }
        };
        asyncTask.execute(useCache);
    }

    // 从网络获取数据
    private Bitmap loadFromNet(Context context) {
        String urlImg = "http://www.imooc.com/api/teacher?type=1";
        Bitmap bm = null;
        // 发请求获取json数据
        String jsonstr = HttpUtils.doGet(urlImg).toString();
        // 当jsonstr不为空时,解析json数据
        if (jsonstr != null) {
            bm = parseJsonstr(jsonstr);
        }
        return bm;
    }

    // 解析数据获取图片链接,并下载图片
    private Bitmap parseJsonstr(String jsonstr) {
        JSONObject root;
        try {
            root = new JSONObject(jsonstr);
            String msgstr = root.optString("msg");
            if (msgstr.equals("成功")) {
                root = root.optJSONObject("data");
                String pic = root.optString("pic1");
                // 获取图片数据,并返回Bitmap对象
                byte[] b = HttpUtils.doGet(pic).toByteArray();
                if (b.length != 0) {
                    return BitmapFactory.decodeByteArray(b, 0, b.length);
                }
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return null;
    }
}

MainActivity

除了对控件进行声明以外,通过前面的类的调用,实现图片和书本内容的下载、存储,并设置相应的点击事件

public class MainActivity extends AppCompatActivity {

    public static final String TAG = "MainActivity";
    public static ImageView img;
    private List<Book> mbookList = new ArrayList<>();
    private TextView myCollection,title,author,content;
    private Button last,collection,next;
    private BookBiz bookBiz = new BookBiz();
    private ImageBiz imgBiz = new ImageBiz();
    private int index = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        loadImageDatas(true);
        loadBookDatas(true);
    }

    // 获取图片数据
    private void loadImageDatas(boolean useCache) {
        imgBiz.loadImageDatas(this,useCache);
    }

    // 获取书本数据
    private void loadBookDatas(boolean useCache) {
        bookBiz.loadBookDatas(this, new BookBiz.CallBack() {
            @Override
            public void onSuccess(List<Book> bookList) {
                Book book = bookList.get(0);
                title.setText(book.getTitle());
                author.setText(book.getAuthor());
                content.setText(book.getContent());
                mbookList = bookList;
            }

            @Override
            public void onFailed(Exception e) {
                e.printStackTrace();
                Toast.makeText(MainActivity.this,"网络加载失败",Toast.LENGTH_SHORT).show();
            }
        },useCache);
    }

    // 初始化控件
    private void initView() {
        myCollection = findViewById(R.id.mycollection_txt);
        title = findViewById(R.id.title_index);
        author = findViewById(R.id.author_index);
        content = findViewById(R.id.content_index);
        img = findViewById(R.id.img_index);
        last = findViewById(R.id.last_btn);
        collection = findViewById(R.id.collection_btn);
        next = findViewById(R.id.next_btn);
    }

    // 主页按钮单击事件
    public void indexClick(View v){
        Book book;
        switch (v.getId()){
            case R.id.mycollection_txt:
                startActivity(new Intent(this,MyCollectionActivity.class));
                break;
            case R.id.last_btn:
                index--;
                if(index < 0){
                    index += 30;
                }
                book = mbookList.get(index % 30);
                title.setText(book.getTitle());
                author.setText(book.getAuthor());
                content.setText(book.getContent());
                break;
            case R.id.collection_btn:
            	// 先判断MyCollectionActivity里的bookList里是否包含该对象,如果没有则添加,反之提示相关信息
                if (!MyCollectionActivity.bookList.contains(mbookList.get(index))) {
                    MyCollectionActivity.bookList.add(mbookList.get(index));
                    Toast.makeText(this, "保存成功", Toast.LENGTH_SHORT).show();
                }else{
                    Toast.makeText(this, "文章已存在", Toast.LENGTH_SHORT).show();
                }
                break;
            case R.id.next_btn:
                index++;
                book = mbookList.get(index % 30);
                title.setText(book.getTitle());
                author.setText(book.getAuthor());
                content.setText(book.getContent());
                break;
        }
    }
}

MyCollectionActivity

在这个类中只需要实现ListView控件的布局适配器以及对ListView子项的点击事件即可

public class MyCollectionActivity extends AppCompatActivity {

    public static List<Book> bookList = new ArrayList<>();
    private ListView books;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_collection);

        init();
        initEvent();

    }

    private void initEvent() {
        // ListView子项的点击事件
        books.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                ReadActivity.book = bookList.get(position);
                startActivity(new Intent(MyCollectionActivity.this,ReadActivity.class));
            }
        });
    }

    private void init() {
        books = findViewById(R.id.books_listview);
        String[] data = new String[bookList.size()];

        // 设置数组适配器
        for (int i = 0; i < bookList.size(); i++) {
            data[i] = bookList.get(i).getTitle();
            Log.e(MainActivity.TAG, "initView: "+bookList.get(i).getTitle());
        }
        ArrayAdapter adapter = new ArrayAdapter(this, R.layout.item_books_listview, data);

        books.setAdapter(adapter);
    }
}

ReadActivity

此类完成对我的收藏界面中选中书籍信息的显示

public class ReadActivity extends AppCompatActivity {

    public static Book book = null;
    private TextView title,author,content;

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_read);

        initView();
    }

    private void initView() {
        title = findViewById(R.id.title_read);
        author = findViewById(R.id.author_read);
        content = findViewById(R.id.content_read);

        title.setText(book.getTitle());
        author.setText(book.getAuthor());
        content.setText(book.getContent());
    }
}

这次编码参照慕课网的教学视频,对不同业务不同功能板块的代码进行了封装,降低了耦合度,这样做的好处一是可以使代码更加美观整洁、整个业务逻辑更加清晰,二是利于多人协作并行编码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值