Android Sqlite multi threading save data with DBFlow + Retrofit

吃可爱长大的小学妹 提交于 2019-12-12 14:23:21

问题


I'm using DBFlow to save into the database and Retrofit to call my web service. My retrofit class is the same class as my Database table. But I have a problem when 2 threads or more are launched at the same time to save my data into the table. The insert into my table duplicate my data and the primary key is duplicated too. Then my thread is stopped because it crashes.

Have you a solution for that ?

Class for Retrofit and DBFlow

@Table(database = LocalDB.class)
@Root(name = "picture_infos")
public class PictureInfos extends BaseModel {

@PrimaryKey
@Element(name = "id_picture")
private int idPicture;

@Column
@Element(name = "id_account")
private String idAccount;

@Column
@Element(name = "folder_path")
private String folderPath;

@Column
@Element(name = "filename")
private String filename;

@Column
@Element(name = "legend", required = false)
private String legend;

public int getIdPicture() {
    return idPicture;
}

public void setIdPicture(int idPicture) {
    this.idPicture = idPicture;
}

public String getIdAccount() {
    return idAccount;
}

public void setIdAccount(String idAccount) {
    this.idAccount = idAccount;
}

public String getFolderPath() {
    return folderPath;
}

public void setFolderPath(String folderPath) {
    this.folderPath = folderPath;
}

public String getFilename() {
    return filename;
}

public void setFilename(String filename) {
    this.filename = filename;
}

public String getLegend() {
    return legend;
}

public void setLegend(String legend) {
    this.legend = legend;
}
}

Thread in retrofit response

public void onResponse(Call<AdminPictures> call, Response<AdminPictures> response) {
            AdminPictures apResponse = response.body();
            final List<PictureInfos> pictureInfos = apResponse.getPicturesList();
            new Thread(new Runnable() {
                @Override
                    public void run() {
                        try {
                            for (PictureInfos infos : pictureInfos) {
                    // This save duplicate when I've 2 or more threads
                              synchronized (infos){
                                if(!infos.exists()){
                                   infos.save();
                                 }
                              }
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }).start();

Stacktrace

    03-18 12:01:18.950 15696-19086/com.vigizen.client.kiosqueadmin E/SQLiteLog: (1555) abort at 12 in [INSERT INTO `PictureInfos`(`idPicture`,`idAccount`,`folderPath`,`filename`,`legend`) VALUES (?,?,?,?,?)]: UNIQUE constraint failed: PictureInfos.idPicture
    03-18 12:01:18.951 15696-19086/com.vigizen.client.kiosqueadmin W/System.err: android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: PictureInfos.idPicture (code 1555)
    03-18 12:01:18.951 15696-19086/com.vigizen.client.kiosqueadmin W/System.err:     at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
    03-18 12:01:18.951 15696-19086/com.vigizen.client.kiosqueadmin W/System.err:     at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:788)
    03-18 12:01:18.951 15696-19086/com.vigizen.client.kiosqueadmin W/System.err:     at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
    03-18 12:01:18.951 15696-19086/com.vigizen.client.kiosqueadmin W/System.err:     at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
    03-18 12:01:18.951 15696-19086/com.vigizen.client.kiosqueadmin W/System.err:     at com.raizlabs.android.dbflow.structure.database.AndroidDatabaseStatement.executeInsert(AndroidDatabaseStatement.java:77)
    03-18 12:01:18.951 15696-19086/com.vigizen.client.kiosqueadmin W/System.err:     at com.raizlabs.android.dbflow.sql.SqlUtils.insert(SqlUtils.java:370)
    03-18 12:01:18.951 15696-19086/com.vigizen.client.kiosqueadmin W/System.err:     at com.raizlabs.android.dbflow.sql.SqlUtils.save(SqlUtils.java:327)
    03-18 12:01:18.951 15696-19086/com.vigizen.client.kiosqueadmin W/System.err:     at com.raizlabs.android.dbflow.structure.ModelAdapter.save(ModelAdapter.java:60)
    03-18 12:01:18.951 15696-19086/com.vigizen.client.kiosqueadmin W/System.err:     at com.raizlabs.android.dbflow.structure.BaseModel.save(BaseModel.java:52)
    03-18 12:01:18.951 15696-19086/com.vigizen.client.kiosqueadmin W/System.err:     at com.vigizen.client.kiosqueadmin.GalleryActivity$1$1.run(GalleryActivity.java:188)
    03-18 12:01:18.951 15696-19086/com.vigizen.client.kiosqueadmin W/System.err:     at java.lang.Thread.run(Thread.java:818)

回答1:


What you are doing here is the first "dont' do" in the docs of DBFlow. What you should use here is a transaction to store the data. This locks the database for an exclusive batch operation and should be a lot faster than iterating over all you models and save them one by one.

Basically, you want to use FastStoreModelTransaction.saveBuilder() here which basically does a INSERT OR UPDATE.

Usage would be like that:

public void onResponse(Call<AdminPictures> call, Response<AdminPictures> response) {
    AdminPictures apResponse = response.body();
    final List<PictureInfos> pictureInfos = apResponse.getPicturesList();

    FastStoreModelTransaction transaction = FastStoreModelTransaction.saveBuilder(FlowManager.getModelAdapter(PictureInfos.class))
        .addAll(pictureInfos)
        .build();

    database.executeTransaction(transaction);
}

Please note that this would automatically runs in the background, so no need to wrap it in an extra thread. If you need a listener on the storing process, use the ProcessModelTransaction instead.




回答2:


Reading database can be done in parallel without interrupting each other, but writing in to database should be done in a synchronized way if you have keys.

You should write the save within a synchronized method. Synchronize will allow only one thread to do changes at a time.

Note: By default android SQLiteDatabase Object is safe for multithreading. DBFlow is also. Make sure you are using the same instances of helpers in multiple threads.

Details



来源:https://stackoverflow.com/questions/36081015/android-sqlite-multi-threading-save-data-with-dbflow-retrofit

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!