Insert and Retrieve Image from SQLite Database in Android

Here we learn how to retrieve json using retrofit get request and using SQLite DB, perform insert and retrieve operation for images in android.

Insert and retrieve an image in SQLite database using BLOB data type which stores binary data nothing but byte array data. Here we need to convert our image to byte array and store into SQLite.

screenshot_2016-10-27-12-49-11_nexus6p-portrait

Prerequisites:
Android studio, basic knowledge of Retrofit, SQLite and Recycler view.

Getting started:
Create android project and retrieve json using retrofit get request (here we use androidhive api). We will insert some values like images to SQLite database and retrieve it.

Step 1. Add dependencies and permission.
Add the following dependencies (in build.gradle)

compile 'com.android.support:recyclerview-v7:24.2.0'
compile 'com.squareup.retrofit2:retrofit:2.+'
compile 'com.squareup.retrofit2:converter-gson:2.+'
compile 'com.squareup.picasso:picasso:2.

Now because we will be doing a networking operation we need internet permission. So add it to manifest file.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Step 2. Model class
We need to create POJO or Model class.The Json from server cannot be use directly in Java, so we need to use model class. You do not need to do much in the model class. You only need to define variables that are in your json. In our json we are having 2 properties (name and urllist(i.e,medium)) so I created 2 variables here.

public class DataModel {
    private String name;
    private URLlist url;
    private boolean isFromDataBase;
    private bitmap picture;
    //getters and setters..

}

public class URLlist{
    private String medium;
    //getters and setters...

}

In the above model we use 2 extra variable, those are

  • isFromDataBase : This boolean variable is used to notify data from database.
  • picture : This bitmap variable store images in bitmap.

Step 3. Creating interface DataService
Here create interface to send HTTP request using retrofit

public interface DataService {

    /*Retrofit get annotation with our URL
      And our method that will return us the list of data*/

    @GET("/json/glide.json")
    Call<List<DataModel>> getAllData();
}

Step 4. Creating Retrofit instance
To send network requests to an API, we need to use the Retrofit Builder class and specify the base URL for the service. So, create a class named RestManager.java

public class RestManager {
    private DataService dataService;
    public static final String BASE_URL = "http://api.androidhive.info";
    public DataService getFlowerService() {
        if(dataService == null){
            //Retrofit setup
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
            
            //Service setup
            dataService = retrofit.create(DataService.class);
        }
        return dataService;
    }
}

Here BASE_URL – it is basic URL of our API. We will use this URL for all requests.

Step 5. Making request
Let’s make the request from MainActivity.class in loadData() . Each line commented well.

private void loadData() {
    // Prepare the HTTP request
    Call<List<DataModel>> listData = mRestManager.getFlowerService().getAllData();
    // Asynchronously execute HTTP request
    listData.enqueue(new Callback<List<DataModel>>() {
        /**
         * onResponse is called when any kind of response has been received.
         */
        @Override
        public void onResponse(Call<List<DataModel>> call, Response<List<DataModel>> response) {
            // isSuccess is true if response code => 200 and <= 300
            if(response.isSuccessful()){
            // if parsing the JSON body failed,response.body() returns null
                List<DataModel> datalist = response.body();
                //Traversing through the whole list to get all data to save database
                for(int i = 0 ; i<datalist.size() ; i++){
                    DataModel data = datalist.get(i);
                    SaveIntoDatabase task = new SaveIntoDatabase();
                    task.execute(data);
                    dataAdapter.addData(data);
                }
            }
        }
        /**
         * onFailure gets called when the HTTP request didn't get through.
         * For instance if the URL is invalid / host not reachable
         */
        @Override
        public void onFailure(Call<List<DataModel>> call, Throwable t) {
        }
    });

}

SaveIntoDatabase() method is used to save database. Here image url is converted to bitmap.

public class SaveIntoDatabase extends AsyncTask<DataModel,Void,Void> {
    // can use UI thread here
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }
    // automatically done on worker thread (separate from UI thread)
    @Override
    protected Void doInBackground(DataModel... params) {
        DataModel dataModel = params[0];
        try {
            InputStream inputStream = new URL(dataModel.getUrl().getMedium()).openStream();
            Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
            //set bitmap value to Picture
            dataModel.setPicture(bitmap);
            //add data to database (shows in next step)
            mDatabase.addData(dataModel);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

Step 6. Create SQLite Database Handler
We need a class to handle all database CRUD (Create, Read, Update and Delete) operation.
Create class extend with SQLiteOpenHelper. Now we override 2 methods onCreate() and onUpgrage().

  • onCreate() : It is called first time when database is created. Here we need to write create table statements.
  • onUpgrage() : It is called when database is upgraded / changed, like adding constraints to database, modifying the table structure etc.,

In below code we are inserting text and images. In our json we don’t have unique integer value to set as primary key. For temporary we setting text variable as primary key.

public class Database extends SQLiteOpenHelper {
    // All Static variables
    // Database Name
    private static final String DB_NAME = "images";
    // Database Version
    private static final int DB_VERSION = 1;
    // Table name
    public static final String TABLE_NAME = "image";
    // Drop table query
    public static final String DROP_QUERY = "DROP TABLE IF EXIST " + TABLE_NAME;
    // Select all query
    public static final String GET_IMAGE_QUERY = "SELECT * FROM " + TABLE_NAME;
    // image table column names
    public static final String PHOTO_URL = "photo_url";
    public static final String PHOTO = "photo";
    public static final String TITLE = "title";
    // Create table
    //
    public static final String CREATE_TABLE_QUERY = "CREATE TABLE " + TABLE_NAME + "" +
            "(" +
            PHOTO_URL + " TEXT PRIMARY KEY not null," +
            PHOTO + " blob not null," +
            TITLE + " TEXT not null)";
    public Database(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }
    // Creating tables
    @Override
    public void onCreate(SQLiteDatabase db) {
            db.execSQL(CREATE_TABLE_QUERY);
    }
    // Upgrading tables
    @Override
    public void onUpgrade(SQLiteDatabase db, int i, int i1) {
        // Drop older table if existed
        db.execSQL(DROP_QUERY);
        // Create tables again
        this.onCreate(db);
    }
}

Insert new records to database:
addData() method accepts DataModel as parameter. We need build ContentValues parameters using DataModel object. Once we inserted data in database we need to close the database connection.

public void addData(DataModel dataModel){
    SQLiteDatabase db = this.getWritableDatabase();
    ContentValues values = new ContentValues();
    values.put(PHOTO_URL,dataModel.getUrl().getMedium());
    values.put(PHOTO, Utility.getPictureByteOfArray(dataModel.getPicture()));
    values.put(TITLE,dataModel.getName());
    // Inserting row
    db.insert(TABLE_NAME,null,values);
    // Closing DataBase connection
    db.close();
}

Inserting text to the sql database is simple. However inserting images to SQLite database has blob data type to store binary data including image data. To insert an image to a blob field, we need to convert the image to byte array using compress() method of the Bitmap object that represents your image.

getPictureByteOfArray() method is used to convert the image to byte array using compress() method of the Bitmap object.

public static byte[] getPictureByteOfArray(Bitmap bitmap) {
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.PNG, 0, byteArrayOutputStream);
    return byteArrayOutputStream.toByteArray();
}

Step 7. Fetching data
First we need to check network connection using below code

public static boolean isNetworkAvailable(Context context) {
    ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
    return networkInfo != null && networkInfo.isConnectedOrConnecting();
}

if network connection is available need to call loadData() (showed in step 5) if not call fetchData() method shown in below.
add fetchData() method in Databasa class used to fetch data.

// fetchData accepts parameter as DataFetchListner (shows in next step)
public void fetchData(DataFetchListner listener) {
//DataFecher method accepts listener and writable database(step 6)  
    DataFetcher fetcher = new DataFetcher(listener, this.getWritableDatabase());
    fetcher.start();
}

DataFetcher is used to get data runnig in thread.To get the image from the database and display it in an ImageView or RecyclerView, we use the getBlob() method of a Cursor object to get the byte array of data from the blob field.

public class DataFetcher extends Thread{
 private final DataFetchListner mListener;
 private final SQLiteDatabase mDb;

 public DataFetcher(DataFetchListner listener, SQLiteDatabase db) {
   mListener = listener;
   mDb = db;
 }
 @Override
 public void run() {
     //Select all data
     Cursor cursor = mDb.rawQuery(GET_IMAGE_QUERY, null);
     // checking database is not empty
     if (cursor.getCount() > 0) {
          // looping through all values and adding to list
          if (cursor.moveToFirst()) {
               do {
                      DataModel data = new DataModel();
                      data.setFromDatabase(true);
                      //getting bitmap value( shown in below)
                      data.setPicture(Utility.getBitmapFromByte(cursor.getBlob(cursor.getColumnIndex(PHOTO))));
                      data.setName(cursor.getString(cursor.getColumnIndex(TITLE)));
                      // adding data 
                      publishFlower(data);
                  } while (cursor.moveToNext());
               }
      }
 }
 // used to pass all data 
 public void publishFlower(final DataModel data) {
 Handler handler = new Handler(Looper.getMainLooper());
      handler.post(new Runnable() {
          @Override
          public void run() {
               // passing data through onDeliverData()
               mListener.onDeliverData(data);
          }
    });
  }
}

Then convert the byte array to Bitmap object using the decodeByteArray method of BitmapFactory.

public static Bitmap getBitmapFromByte(byte[] image) {
    return BitmapFactory.decodeByteArray(image, 0, image.length);
}

Create interface used to get data

public interface DataFetchListner {
    void onDeliverData(DataModel dataModel);
    void onHideDialog();
}

implement DataFetcherListner in MainActivity.java and override onDeliverData() and onHideDialog().

@Override
public void onDeliverData(DataModel dataModel) {
// pass data to adapter to display in recycler
    mDataAdapter.addData(dataModel);
}

@Override
public void onHideDialog() {
// hide dialog here
}

Step 8. Setting up RecyclerView.ViewHolder in DataAdapter
In the below code set data over view.

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    DataModel currFlower = dataModels.get(position);
//check data present in database
    if(currFlower.isFromDatabase()){
        holder.image.setImageBitmap(currFlower.getPicture());
        holder.title.setText(currFlower.getName()+"");
    }else {
        holder.title.setText(currFlower.getName());
//set imageview using picasso
Picasso.with(holder.itemView.getContext()).load(currFlower.getUrl().getMedium()).into(holder.image);
    }
}

Conclusion
Final result will look like this

Download code : here

One thought on “Insert and Retrieve Image from SQLite Database in Android

  1. sir,
    thanks to explain consume retrofit data and store it to sqlite database.but how to update for new upcoming data to save again.

Leave a Reply

Your email address will not be published. Required fields are marked *