使用带有sqlite查询的Loaders返回对象

时间:2016-11-01 17:39:12

标签: android android-sqlite loader

这是我从数据库中获取数据时所做的一切 - 我的片段将调用dbhelper,它将运行查询并将结果数据传递给对象。

我应该使用一个加载器,以便在UI线程中不进行查询,但之前我没有打扰,因为我的应用程序的数据库非常小。继续前进,这是一个坏主意,因为我的应用程序变得越来越大,数据库也越来越大。所以现在我正在阅读有关Loaders的所有内容,包括CursorLoader和自定义加载器,但我仍然无法在我的应用程序中实现它。

所以这是我当前的代码(简化,只是为了显示相关部分):

public class CategoryEdit extends Fragment{

private DbControl dbc;
private ObjectCategory objCategory;
private int categoryID = 3;
private EditText etName;

@Override
    public void onActivityCreated(Bundle savedInstanceState) {
        dbc = new DbControl(getActivity());
        try{
            dbc.open();
            objCategory = new ObjectCategory();
            objCategory = dbc.getCategoryData(categoryID);
            dbc.close();
        }catch (SQLException e){
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }

        etName.setText(objCategory.name);
    }
}

public class DbControl {

    public static final String FILE_NAME = "DbControl.java";
    private SQLiteDatabase database;
    private MySQLiteHelper dbHelper;

    public DbControl(Context context) {
        dbHelper = new MySQLiteHelper(context);
    }

    public void open() throws SQLException {
        database = dbHelper.getWritableDatabase();
    }

    public void close() {
        dbHelper.close();
    }

    public ObjectCategory getCategoryData(int itemID) throws Exception{
        ObjectCategory objCategory = new ObjectCategory();
        Cursor cursor = database.query(MySQLiteHelper.TABLE_CATEGORY, new String[] {"name"},
                "id = " + itemID, null, null, null, null);
        if (cursor!=null && cursor.getCount()>0 && cursor.moveToFirst()) {
            objCategory.name = cursor.getString(cursor.getColumnIndex("name"));
        }
        return objCategory;
    }
}

有人能指出我正确的方向,在我的案例中以正确的方式实施Loader吗?如果可能的话,我不想过多地改变类DbControl中的代码 - 特别是它内部函数的返回数据类型。

在我读过的关于加载器的教程中,有以下几点:

无论如何,这是我到目前为止所做的:

public class CategoryEdit extends Fragment implements LoaderManager.LoaderCallbacks<ObjectCategory> {

@Override
    public Loader<ObjectCategory> onCreateLoader(int id, Bundle args){
        try{
            dbc.open();
            objCategory = new ObjectCategory();
            objCategory = dbc.getCategoryData(categoryID);
            dbc.close();
        }catch (SQLException e){
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }

        return objCategory; //this returns error, it says required Loader<ObjectCategory> instead of objCategory, but I'm not sure how to do that here
    }

@Override
    public void onLoadFinished(Loader<ObjectCategory> cursorLoader, ObjectCategory cursor) {
//Since onCreateLoader has error above, I'm not sure how to implement this part
    }

@Override
    public void onLoaderReset(Loader<ObjectCategory> cursorLoader) {
        //Can I just leave this empty?
    }
}

2 个答案:

答案 0 :(得分:1)

我会说 - 不要使用Loaders。当我刚接触Android时,我在我的应用中使用了CursorLoader,结果证明这是一个糟糕的决定。

如果CursorLoader足以满足您的需求,那么您就可以了(尽管您的ActivitiesFragments会受到与DB相关的逻辑的污染),但是一旦您尝试实现你自己的Loader,它应该封装与DB相关的逻辑,只返回一个构造的对象,你将不可避免地遇到LoaderManager框架的一百万个问题。 @CommonsWare写了一篇关于它的nice article - 在将Loaders集成到你的应用程序之前阅读它。

现在我经常使用&#34;经理&#34;用于从SQLite或Internet加载数据。我&#34;经理的一般形式&#34;是这样的(请注意这些&#34;经理&#34;不是单身人士):

public class TutorialManager {

    interface TutorialManagerListener {
        void onDataFetchCompleted(SomeData data);
    }
    private BackgroundThreadPoster mBackgroundThreadPoster;
    private MainThreadPoster mMainThreadPoster;


    private Set<TutorialManagerListener> mListeners = new HashSet<>(1);

    public TutorialManager(@NonNull BackgroundThreadPoster backgroundThreadPoster,
                           @NonNull MainThreadPoster mainThreadPoster) {
        mBackgroundThreadPoster = backgroundThreadPoster;
        mMainThreadPoster = mainThreadPoster;
    }

    @UiThread
    public void registerListener(@NonNull TutorialManagerListener listener) {
        mListeners.add(listener);
    }

    @UiThread
    public void unregisterListener(@NonNull TutorialManagerListener listener) {
        mListeners.remove(listener);
    }

    @UiThread
    private void notifyFetchCompleted(SomeData data) {
        for (TutorialManagerListener listener : mListeners) {
            listener.onDataFetchCompleted(data);
        }
    }

    /**
     * Call to this method will fetch data on background thread and then notify all registered
     * listeners about new data on UI thread.
     */
    public void fetchData() {
        mBackgroundThreadPoster.post(new Runnable() {
            @Override
            public void run() {

                // logic that loads the data on background thread goes here
                // final SomeData data = ... ;

                mMainThreadPoster.post(new Runnable() {
                    @Override
                    public void run() {
                        notifyFetchCompleted(data);
                    }
                });
            }
        });
    }
}

MainThreadPoster的实施:

/**
 * This class should be used in order to execute {@link Runnable} objects on UI thread
 */
public class MainThreadPoster {

    private final Handler mMainHandler;

    public MainThreadPoster() {
        mMainHandler = new Handler(Looper.getMainLooper());
    }

    /**
     * Post {@link Runnable} for execution on main thread
     */
    public void post(Runnable runnable) {
        mMainHandler.post(runnable);
    }

}

BackgroundThreadPoster的实现取决于您。我通常使用以下简单实现(除非需要进行一些微调):

/**
 * This class should be used in order to execute {@link Runnable} objects on background threads
 */
public class BackgroundThreadPoster {

    private final ExecutorService mBackgroundExecutor;

    public BackgroundThreadPoster() {
        mBackgroundExecutor = Executors.newCachedThreadPool();
    }

    /**
     * Post {@link Runnable} for execution on a random background thread
     */
    public void post(Runnable runnable) {
        mBackgroundExecutor.execute(runnable);
    }

}

我将MainThreadPosterBackgroundThreadPoster个对象注入&#34;经理&#34;为了:

  1. 制造&#34;经理&#34;单元可测试
  2. 在整个应用程序中执行背景线程的集中管理和优化

答案 1 :(得分:1)

  

任何人都可以指出我正确的方向,在我的案例中以正确的方式实施Loader吗?

你必须像这样使用AsyncTaskLoader:

public class CategoryEdit extends Fragment implements LoaderManager.LoaderCallbacks<ObjectCategory> {

    @Override
    public Loader<ObjectCategory> onCreateLoader(int id, Bundle args) {
        return new AsyncTaskLoader<ObjectCategory>(getActivity()) {
            @Override
            public ObjectCategory loadInBackground() {
                try {
                    dbc.open();
                    objCategory = new ObjectCategory();
                    objCategory = dbc.getCategoryData(categoryID);
                    dbc.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                }

                return objCategory;
            }

            @Override
            protected void onStartLoading() {
                forceLoad();
            }
        };

    }

    @Override
    public void onLoadFinished(Loader<ObjectCategory> loader, ObjectCategory data) {
        //do some stuff here
    }

    @Override
    public void onLoaderReset(Loader<ObjectCategory> loader) {
        //Yes, you can leave it empty
    }
}