Android has a different paradigm for application development. So , In this post, I will explain a few of those concepts (as I understand) that lie at its core.
- Cursor: To draw parallels, a cursor object is just like a ResultSet in JDBC. It is used for iterating over the underlying data and just like a JDBC ResultSet object, it must also be closed. The following snippet shows how to iterate over the result set.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
try { cursor = getDatabase().query(tableName, columns, whereClause, whereArgs, groupBy, having, orderBy); if (cursor != null) { while (cursor.moveToNext()) { cursor.getString(cursor.getColumnIndexOf("COLUMN_NAME_TO_SELECT")); } } } catch (Exception e) { Log.e("[" + tag+ "] ", "Exception Occured while fetching data", e); throw e; } finally { // make sure to close the cursor cursor.close(); }
- Loader : if you are aware of swing development, then you will know that the best practice is to perform long running tasks such as running a query, fetching data from a service, etc. on a separate thread, this is even more important to follow in android because if you don’t your app will just crash. A loader is basically what you use to perform those long running tasks . So this is what you basically have to do, In your activity or fragment implement the Loader callback methods, which would be invoked when the loader has finished performing its long running tasks. The long running tasks are performed in the loader class’s loadinbackground method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/** The loader class */ public class ArticleLoader extends AsyncTaskLoader<Map<String,String>> { /** * This is where the articles are loaded */ @Override public Map<String,String> loadInBackground() { //Perform the data fetch here } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
//the fragment class where you create the loader and wait for the loader to do its job public class ArticleFragment extends Fragment implements LoaderCallbacks<Object>{ @Override public void onActivityCreated(Bundle savedInstanceState) { //See if the loader is there Loader<Object> loader = getActivity().getSupportLoaderManager().getLoader(LOADER_ARTICLE_SUMMARY); if (loader != null && !loader.isReset()) { //reset it as data needs to be loaded again because activity was created again getActivity().getSupportLoaderManager().restartLoader(LOADER_ARTICLE_SUMMARY, args, this); } else { //instantiate the loader getActivity().getSupportLoaderManager().initLoader(LOADER_ARTICLE_SUMMARY, args, this); } } @Override public Loader<Object> onCreateLoader(int id, Bundle args) { //instantiate the loader here depending upon loader id there could be several loaders that you might instantiate if(id==LOADER_ARTICLE_SUMMARY){ String rawQuery="select _id , "+ FeedSQLLiteHelper.COLUMN_ARTICLE_TITLE+ " , "+ FeedSQLLiteHelper.COLUMN_ARTICLE_READ+ " , "+ FeedSQLLiteHelper.COLUMN_ARTICLE_IS_FAVORITE +" , "+FeedSQLLiteHelper.COLUMN_ARTICLE_DOWNLOADED+ " , "+ FeedSQLLiteHelper.COLUMN_ARTICLE_SUMMARY +" , "+FeedSQLLiteHelper.COLUMN_ARTICLE_FULL_CONTENT+" , "+FeedSQLLiteHelper.COLUMN_ARTICLE_CREATED_DATE +" , "+FeedSQLLiteHelper.COLUMN_ARTICLE_URL +" from "+FeedSQLLiteHelper.TABLE_ARTICLES + " where _id = ?"; String articleId= args.getString(FeedSQLLiteHelper.COLUMN_ID); Loader loader=new SQLiteCursorLoader(this.getActivity().getApplicationContext(),fdb.getDbHelper(), rawQuery, new String []{articleId}); return loader; } else { Loader loader= new ArticleLoader(getActivity().getApplicationContext(),id,args); return loader; } } @Override public void onLoadFinished(Loader<Object> loader, Object data) { /** Here you are supplied with the data, so you could update the cursor of the adapter Just make sure you check the loader id before updating the adapter */ int id=loader.getId(); if(id==LOADER_ARTICLE_SUMMARY){ Cursor cursor=(Cursor)data; if(!cursor.isClosed()){ //do pojo conversion article=cursorToFeedArticle(cursor); loadDataIntoWebView(article); } } else{ Map<String,String> result=(Map)data; article.setArticleContent(result.get("ARTICLE_FULL_CONTENT")); article.setArticleDownloaded(result.get("ARTICLE_DOWNLOADED")); loadDataIntoWebView(article); } } @Override public void onLoaderReset(Loader<Object> arg0) { /** remove references to the data of the loader if its a cursor just dereference it * In this case since we do not have a cursor leave it empty. */ } }
Adapter: This is used to hold the data to view binding. In other words, this object holds data which can be accessed by the view and the data to view binding information which tells how that data is mapped to the view components of the view.
Activity: An activity is like a page. It represents a view which might be composed of several child views (fragments) or just hold its own view.
- Fragment: It is a child view, it has its own layout, but it exists only within an activity. As a fragment can be reused within different activities, so it should not hold any activity specific references. You can attach the fragment to an activity at runtime using FragmentManager API’s , but if the fragment is listed in the activity’s layout then it cannot be removed. So, generally it is better to use the FragmentManager API’s then to have this restriction.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
articleFragment = (ArticleFragment) getSupportFragmentManager() .findFragmentByTag(ArticleFragment.tag); if (articleFragment == null) { //create a new instance of the fragment articleFragment = new ArticleFragment(); Bundle args = new Bundle(); args.putString("KEY","VALUE"); articleFragment.setArguments(args); //replace the framelayout with the article fragment's content getSupportFragmentManager() .beginTransaction() .replace(R.id.content_frame_article_fragment, articleFragment, ArticleFragment.tag).commit(); } else { // create a method onUpdateFromActivity, just supply it with new arguments articleFragment.onUpdateFromActivity(args); }
Also Fragment instances can be retained, but in that case its lifecycle is different. It is better to have them retained because obviously it speeds up your application. Intent and Intent Filter: Intent is basically an object which is used to invoke another activity, a service instance, etc. You generally supply it with an action when invoking IntentService instance. IntentFilter on the other hand is used to filter out what actions can a particular service, activity or a fragment can handle.
- IntentService and BroadcastReceiver: IntentService listens for intent actions and maintains a queue of requests which are handled one after the other sequentially. It runs on a separate background thread, so one can perform synchronous tasks on it. You must implement its onHandleIntent method to perform the tasks required. Once the task is completed, you should broadcast an intent signifying so and in your activity or fragment listen for the intent using BroadcastReceiver.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
//1. start an intentservice Intent feedPullServiceIntent = new Intent(this.getActivity().getApplicationContext(), PullFeedsService.class); feedPullServiceIntent.setAction(ACTION_UPDATE_FEEDS); feedPullServiceIntent.putExtra("feedName", feedName); getActivity().getApplicationContext().startService(feedPullServiceIntent); public class PullFeedsService extends IntentService { //2. handle the action @Override protected void onHandleIntent(Intent intent) { if(action==ACTION_UPDATE_FEEDS){ if(isConnected()){ //fetch and parse the feeds ..... //3. notify completion notifyFetchComplete(ACTION_FEED_FETCH_COMPLETE,feedName); } } if(action==DELETE_FEEDS){ //delete the feeds in the batch notifyFetchComplete(ACTION_DELETE_COMPLETE,feedName); } } private void notifyFetchComplete(String action, String feedName) { Intent i = new Intent(); i.setAction(action); Bundle result=new Bundle(); if(action==ACTION_FEED_FETCH_COMPLETE){ Bundle result=new Bundle(); result.putString("KEY", "VALUE"); } else if(action==ACTION_DELETE_COMPLETE){ Bundle result=new Bundle(); result.putString("KEY", "VALUE"); } i.putExtras(result); //4. Brodcast the event LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(i); } //in your fragment or activity private BroadcastReceiver receiver = new BroadcastReceiver() { //5. called only when the fragment receives the specific intents as specificied in the intent filter while registering @Override public void onReceive(Context context, Intent intent) { if(intent.getAction()==PullFeedsService.ACTION_FEED_FETCH_COMPLETE){ if(refreshItem!=null){ refreshItem.getActionView().clearAnimation(); refreshItem.setActionView(null); } Bundle result=intent.getExtras(); String refreshForFeed=result.getString("RefreshForFeed"); if(refreshForFeed.equals("ALL")||refreshForFeed.equals(feedName)){ //reset loaders resetLoaders(); } } } }; @Override public void onPause() { ... //fragment paused so unregister the receiver LocalBroadcastManager.getInstance(getActivity().getApplicationContext()).unregisterReceiver(receiver); } @Override public void onResume() { ... //0. fragment running so register the receiver to listen for events //specify a particular intent filter IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(PullFeedsService.ACTION_FEED_FETCH_COMPLETE); intentFilter.addAction(PullFeedsService.ACTION_NOT_CONNECTED); LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext()).registerReceiver(receiver, intentFilter); }