2016-11-01 1 views
1

Voici ce que j'ai fait pendant tout ce temps pour obtenir des données de ma base de données - mon fragment appellera un dbhelper qui exécutera la requête et transmettra les données résultantes dans un objet.Utilisation de Loaders avec une requête sqlite qui renvoie l'objet

J'aurais dû utiliser un chargeur pour que l'interrogation ne se fasse pas dans le thread de l'interface utilisateur, mais auparavant, je n'ai pas pris la peine car la base de données de mon application est très petite. Aller de l'avant, c'était une mauvaise idée depuis que mon application est devenue plus grande et la base de données aussi. Alors maintenant, je lis tout ce que je peux sur les chargeurs, y compris CursorLoader et les chargeurs personnalisés, mais j'ai toujours des problèmes de mise en œuvre dans mon application.

Alors, voici mon code actuel (simplifié, juste pour montrer les parties pertinentes):

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; 
    } 
} 

Quelqu'un peut-il me diriger dans la bonne direction, dans la mise en œuvre Loader la bonne façon dans mon cas ici? Si possible, je ne veux pas trop modifier les codes dans la classe DbControl - en particulier le type de données de retour des fonctions à l'intérieur.

BTW parmi les tutoriels sur les chargeurs que j'ai lu est:

Quoi qu'il en soit voici ce que je l'ai fait jusqu'à présent:

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? 
    } 
} 

Répondre

1

Quelqu'un peut-il me pointer dans la bonne direction, en mettant en œuvre Loader la bonne façon dans mon cas ici?

Vous devez utiliser un AsyncTaskLoader comme ceci:

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 
    } 
} 
+0

J'ai essayé d'utiliser votre méthode, mais les codes à l'intérieur de loadInBackground ne sont pas appelés. J'ai ajouté forceload comme suggéré ici http://stackoverflow.com/questions/8606048/asynctaskloader-doesnt-run mais cela ne semble pas être une bonne idée. – imin

+0

OK J'ai ajouté '@Override protected void onStartLoading() { if (takeContentChanged()) forceLoad(); } @Override protected void onStopLoading() { cancelLoad(); } 'et ça marche maintenant. – imin

1

Je dirais - ne pas utiliser Loaders. J'ai utilisé CursorLoader dans mon application quand j'étais nouveau sur Android, et il s'est avéré être une mauvaise décision.

Si CursorLoader est suffisant pour vos besoins, alors vous êtes bien (si votre Activities et Fragments deviendra polluée par DB logique liée), mais une fois que vous essayez de mettre en œuvre votre propre Loader, qui devrait encapsuler la logique liée DB et juste retourner un objet construit, vous aurez inévitablement tomber sur un million de problèmes avec LoaderManager cadre. @CommonsWare a écrit un nice article à ce sujet - lisez-le avant d'intégrer Loaders dans votre application.

De nos jours, j'utilise généralement des «gestionnaires» pour charger des données à partir de SQLite ou d'Internet. La forme générale de mon « manager » est comme ça (s'il vous plaît noter que ces « gestionnaires » ne sont pas singletons):

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); 
        } 
       }); 
      } 
     }); 
    } 
} 

Mise en œuvre de 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); 
    } 

} 

La mise en œuvre de BackgroundThreadPoster est à vous.J'utilise généralement la mise en œuvre simple suivante (à moins qu'un réglage fin est nécessaire):

/** 
* 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); 
    } 

} 

injecter MainThreadPoster et BackgroundThreadPoster objets en « gestionnaires » afin de:

  1. Marque « gestionnaires » unité testable
  2. Effectuer une gestion centralisée et l'optimisation de fond enfilage sur l'application entière
+0

wow merci pour une autre perspective sur les chargeuses et une autre approche .. Je pourrais regarder plus tard. Btw IINM J'ai lu quelque part suggérant que RxJava est une autre meilleure approche par rapport aux chargeurs ... vous pensiez à cela? – imin

+0

@imin, je ne suis pas un expert sur RxJava, mais je pense que c'est plus qu'une simple alternative aux chargeurs - c'est un style architectural totalement différent pour l'application. D'après ce que je comprends, RxJava sera quelque peu similaire à l'approche des «managers» que j'ai décrite dans cette réponse - elle est basée sur les Observables et les Notifications (aka. – Vasiliy