विभिन्न प्रकार की वस्तुओं और विभिन्न डेटा प्रदाताओं के साथ सूची

प्रस्तावना



एक बार जब मुझे विभिन्न प्रकारों के एक सूची दृश्य कार्ड में प्रदर्शित करने की आवश्यकता होती है, और यहां तक ​​कि अलग-अलग एपीआई का उपयोग करके सर्वर से प्राप्त किया जाता है। जैसे, उपयोगकर्ता को आनन्दित होने दें और एक समाचार फ़ीड में देखें:



जाहिर है, एक बड़े लेआउट के साथ छेड़छाड़ करना जिसमें कार्ड के लिए सभी कल्पनीय विकल्पों को ध्यान में रखना बुरा है, और यह इतना विस्तार करेगा।







दूसरी कठिनाई यह थी कि कार्ड के लिए डेटा स्रोत पूरी तरह से अलग-अलग सर्वर संसाधन हो सकते हैं, विभिन्न प्रकार के डेटा वापस करने वाले कई एपीआई के लिए एक साथ अनुरोधों का उपयोग करके सूची एकत्र की जानी थी।







खैर, ताकि जीवन शहद की तरह न लगे, सर्वर एपीआई को बदला नहीं जा सकता है।



API से लेकर ListView तक



Google I / O 2010 में Virgil Dobjanschi ने पूरी तरह से यह बताया कि REST API के साथ सहभागिता को कैसे लागू किया जाए। बहुत पहला पैटर्न पढ़ता है:

  1. गतिविधि ऐसी सेवा बनाती है जो REST API के लिए अनुरोध करती है;
  2. सेवा प्रतिक्रिया को पार्स करती है और कन्टैंटप्रॉइडर के माध्यम से डेटाबेस में डेटा को सहेजती है;
  3. गतिविधि डेटा परिवर्तन की सूचना प्राप्त करती है और दृश्य को अपडेट करती है।




UPD यहां सेवा का उपयोग करने के विषय पर एक छोटी सी होलिवर उठी है, इसलिए इस शब्द को "एक पुस्तकालय जो HTTP अनुरोधों को लागू करता है" के साथ प्रतिस्थापित करना बेहतर है - यह कोई रास्ता नहीं है।



इसलिए अंत में, सब कुछ काम करता है: हम एपीआई के लिए अनुरोधों का एक गुच्छा बनाते हैं, सामग्री का उपयोग करके डेटा को REST संसाधनों के प्रकारों से जुड़े अलग-अलग तालिकाओं में डालें, फ़ीड में नए डेटा की उपलब्धता के बारे में सूचित करें। लेकिन, हमेशा की तरह, दो समस्याएं हैं:







हम विभिन्न प्रकार के कार्ड प्रदर्शित करते हैं



सबसे पहले, हम सरल के साथ सौदा करते हैं। समाधान आसानी से Google में स्थित है, इसलिए मैं इसे संक्षेप में देता हूं।

कार्ड सूची एडाप्टर में, हम तरीकों को फिर से परिभाषित करते हैं:



@Override int getViewTypeCount() { //   ,       return VIEW_TYPE_COUNT; } @Override int getItemViewType(int position) { //          Cursor c = (Cursor)getItem(position); int columnIndex = c.getColumnIndex(VIEW_TYPE_COLUMN); return c.getInt(columnIndex); } @Override void bindView(View view, Context context, Cursor c) { //           int columnIndex = c.getColumnIndex(VIEW_TYPE_COLUMN); int viewType = c.getInt(columnIndex); switch(viewType) { case VIEW_TYPE_VIDEO: bindVideoView(view); break; case VIEW_TYPE_SUBSCRIPTION: //    } } @Override View newView(Context context, Cursor cursor, ViewGroup parent) { //        int columnIndex = c.getColumnIndex(VIEW_TYPE_COLUMN); int viewType = c.getInt(columnIndex); switch(viewType) { case VIEW_TYPE_VIDEO: return newVideoView(cursor); case VIEW_TYPE_SUBSCRIPTION: //    } }
      
      







इसके अलावा, अद्भुत CursorAdapter



वर्ग अपने आप ही सब कुछ कर देगा: यह विभिन्न प्रकार के दृश्यों के लिए अलग-अलग दृश्य कैश को इनिशियलाइज़ करेगा, यह पता VIEW_TYPE_COLUMN



कि क्या नए या पुराने विचारों का पुन: उपयोग करना है ... सामान्य तौर पर, सब कुछ बहुत अच्छा है, आपको बस कर्सर में VIEW_TYPE_COLUMN



कॉलम प्राप्त करने की आवश्यकता है।



हम टेप के लिए SQL क्वेरी एकत्र करते हैं



निश्चितता के लिए, डेटाबेस में टेबल होने दें:







कुल, आपको एक क्वेरी बनाने की आवश्यकता है जो निम्नलिखित कॉलम लौटाती है:



स्तंभ वीडियो लेखक टैग टिप्पणी
आईडी video_id author_id tag_id इसी तालिका में प्राथमिक कुंजी
view_type वीडियो सदस्यता सदस्यता प्रदर्शित करने के लिए कार्ड का प्रकार
content_type वीडियो लेखकों टैग सामग्री प्रकार - या तालिका का नाम, यदि यह अधिक सुविधाजनक है
शीर्षक video_title शून्य शून्य वीडियो शीर्षक
नाम शून्य AUTHOR_NAME TAG_NAME लेखक का नाम या टैग नाम
चित्र लिंक लिंक लिंक तस्वीर के लिए लिंक
अद्यतन टाइमस्टैम्प टाइमस्टैम्प टाइमस्टैम्प सर्वर अद्यतन समय




मैं थोड़ा और समझाऊंगा।





Sqlite में, क्वेरी काफी सरल है:



 SELECT 0 as view_type, 'videos' as content_type, title, NULL as name, picture, updated FROM videos UNION ALL SELECT 1 as view_type, 'authors' as content_type, NULL as title, name, picture, updated FROM authors UNION ALL SELECT 1 as view_type, 'tags' as content_type, NULL as title, name, picture, updated FROM tags ORDER BY updated
      
      







बेशक, आप "हाथ से" इस तरह की क्वेरी का निर्माण कर सकते हैं, लेकिन SQLiteQueryBuilder में थोड़ी छोटी गाड़ी है, लेकिन ऐसी क्वेरी बनाने के लिए काम करने के तरीके।



इसलिए, गतिविधि हमारे ContentProvider से फ़ीड का अनुरोध कर रही है:



 Cursor c = getContext().getContentResolver().query(Uri.parse("content://MyProvider/feed/"));
      
      







उसी समय, MyProvider.query



विधि में, MyProvider.query



निर्धारित करना आवश्यक है कि अनुरोध उरी टेप से किया गया है, और "बुद्धिमान" क्वेरी निर्माण मोड पर स्विच करें।



 Cursor query(Uri contentUri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { if (isFeedUri(contentUri)) return buildFeedUri(); //       // ... } Cursor buildFeedUri() { //   "-"      HashSet<String> unionColumnsSet = new HashSet<String>(); //  Uri  ,    (videos, authors  tags) List<Uri>contentUriList = getSubqueryContentUriList(); //       viewType String[] viewTypeColumns = new String[contentUriList.size()]; //      contentType String[] contentTypeColumns = new String[contentUriList.size()]; for (int i=0; i<contentUriList.size(); i++) { Uri contentUri = contentUriList.get(i); //       viewTypeColumns[i] = getViewTypeExpr(contentUri); // "0 as view_type" //   content_type contentTypeColumns[i] = getContentTypeExpr(contentUri); // "'videos' as content_type" //      List<String> projection = getProjection(contentUri); //       unionColumnsSet.addAll(projection); } // ,       ,  :  , //  content-type    ,    . String[] subqueries = new String[contentUriList.size()]; for (int i=0; i<contentUriList.size(); i++) { Uri contentUri = contentUriList.get(i); SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); builder.setTables(getTable(contentUri)); //         "1 as content_type" //     ,  builder   //  "SELECT X as Y"   String[] unionColumns = prependContentTypeExpr(contentTypeColumns[i], unionColumnSet); //    ""     "0 as view_type" //  ,       Set<String> projection = prependViewTypeExpr(viewTypeColumns[i], getProjection(contentUri)); //  ,   String selection = computeWhere(contentUri); subqueries[i] = builder.buildUnionSubQuery( "content_type", // typeDiscriminatorColumn -   , //        unionColumns, projection, 0, getTable(contentUri), //    content_type // (      ) selection, null, // selectionArgs -   buildUnionSubQuery    // (   API level 1,  API level 11 -   ) null, // groupBy null // having ); } //   ,        . SQLiteQueryBuilder builder = new SQLiteQueryBuilder() String orderBy = "updated DESC"; String query = builder.buildUnionQuery( subqueries, orderBy, null // limit -   ,  . ); return getDBHelper().getReadableDatabase().rawQuery( query, null // selectionArgs -    ); }
      
      







सामान्य तौर पर, यदि content://MyProvider/feed/



एक्सेस करते समय उदाहरण को सही ढंग से लिखा गया content://MyProvider/feed/



हमारा ContentProvider UNION अनुरोध उत्पन्न करेगा जो हमें चाहिए और एडॉप्टर को आवश्यक डेटा देता है।



हमें सर्वर से डेटा अपडेट प्राप्त होता है





लेकिन यह क्या है? हम एपीआई वीडियो के दूसरे पृष्ठ का अनुरोध करते हैं, डेटा, लॉग्स को देखते हुए, डेटाबेस में संग्रहीत किया जाता है, लेकिन सूची दृश्य का उपयोग नहीं किया जाता है ...

बिंदु लोडर कॉलबैक का कार्यान्वयन है



 @Override public Loader<Cursor> onCreateLoader(int loaderId, Bundle params) { return new CursorLoader( getContext(), Uri.parse("content://MyContentProvider/feed/"), ... ); }
      
      







जब कोई गतिविधि किसी ContentProvider का अनुरोध करती है, CursorLoader एक ContentObserver बनाता है जो Uri content://MyProvider/feed/



लिए देखता content://MyProvider/feed/



; जब हमारी सेवा सर्वर के एपीआई के लिए अनुरोध के परिणामों को सहेजती है, तो ContentProvider स्वचालित रूप से आपको किसी अन्य Uri के लिए डेटा परिवर्तन की सूचना देता है, content://MyProvider/videos/







इस समस्या को कैसे सही ढंग से और अंत में हल करें, मुझे नहीं पता। मेरे आवेदन में, यह कोड में पर्याप्त था जो डेटाबेस में क्वेरी के परिणामों को फ़ीड में डेटा में परिवर्तन के बारे में स्पष्ट रूप से सूचित करने के लिए (विशिष्ट तालिका में परिवर्तन की सूचना प्रदाता पर पड़ता है) को संग्रहीत करता है:



 getContext.getContentResolver().notifyChange(Uri.parse("content://MyProvider/feed/", null));
      
      







वैकल्पिक समाधान






All Articles