这是我从数据库中获取数据时所做的一切 - 我的片段将调用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?
}
}
答案 0 :(得分:1)
我会说 - 不要使用Loaders
。当我刚接触Android时,我在我的应用中使用了CursorLoader
,结果证明这是一个糟糕的决定。
如果CursorLoader
足以满足您的需求,那么您就可以了(尽管您的Activities
和Fragments
会受到与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);
}
}
我将MainThreadPoster
和BackgroundThreadPoster
个对象注入&#34;经理&#34;为了:
答案 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
}
}