2010-12-02 4 views
7

के लिए ListView एडाप्टर के साथ कर्सर का उपयोग करके मैं SQLite डेटाबेस से डेटा प्राप्त करने और इसे सूचीदृश्य में दिखाने के लिए कस्टम कर्सर एडाप्टर का उपयोग कर रहा हूं। डेटाबेस में लगभग 8.000 पंक्तियों के साथ 2 कॉलम हैं। इसलिए मैं जितनी जल्दी हो सके सभी डेटा पूछने और दिखाने के लिए एक रास्ता तलाश रहा हूं। मैं यहाँ asyncTask साथ यह किया है कोड है:बड़ी संख्या में डेटा

private class PrepareAdapter extends AsyncTask<Void,Void,CustomCursorAdapter > { 

@Override 
protected void onPreExecute() { 
    dialog.setMessage("Wait"); 
    dialog.setIndeterminate(true); 
    dialog.setCancelable(false); 
    dialog.show(); 


    Log.e("TAG","Posle nov mAdapter"); 
} 

@Override 
protected CustomCursorAdapter doInBackground(Void... unused) { 

    Cursor cursor = myDbNamesHelper.getCursorQueryWithAllTheData(); 
    mAdapter.changeCursor(cursor); 
    startManagingCursor(cursor); 
    Log.e("TIME","posle start managing Cursor" + String.valueOf(SystemClock.elapsedRealtime()-testTime)+ " ms"); 
    testTime=SystemClock.elapsedRealtime(); 

    mAdapter.initIndexer(cursor); 
    return mAdapter; 
} 

protected void onPostExecute(CustomCursorAdapter result) { 

    TabFirstView.this.getListView().setAdapter(result); 
    Log.e("TIME","posle adapterSet" + String.valueOf(SystemClock.elapsedRealtime()-testTime)+ " ms"); 
    testTime=SystemClock.elapsedRealtime(); 
    dialog.dismiss(); 
} 

}

इस हिस्से को छोड़कर अच्छा काम करता है जब मैं एक एडाप्टर में परिणाम सेट करना होगा। मैंने कुछ समय परीक्षण किए हैं और इसे प्रारंभ करने से पहले 700 एमएस लगते हैं। प्रबंधन कर्सर। समस्या यह है कि इसे सेट एडाप्टर (परिणाम) से पहले बनाने में लगभग 7 सेकंड लगते हैं और यह यूआई थ्रेड में चल रहा है, इसलिए यह मेरे ऐप को उत्तरदायी बनाता है (प्रगति संवाद फ्रीज करता है और कभी-कभी ऐप करता है)। मैं इस बार कम कैसे कर सकता हूँ? क्या मैं इसे पृष्ठभूमि में या उत्तरदायित्व बढ़ाने के किसी भी तरीके से भी चला सकता हूं?

tnx।

public class CustomCursorAdapter extends SimpleCursorAdapter implements OnClickListener,SectionIndexer,Filterable, 
            android.widget.AdapterView.OnItemClickListener{ 

    private Context context; 
    private int layout; 
    private AlphabetIndexer alphaIndexer; 

    public CustomCursorAdapter (Context context, int layout, Cursor c, String[] from, int[] to) { 
     super(context, layout, c, from, to); 
     this.context = context; 
     this.layout = layout; 

    } 
    public void initIndexer(Cursor c){ 
     alphaIndexer=new AlphabetIndexer(c, c.getColumnIndex(DataBaseNamesHelper.COLUMN_NAME), " ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 
    } 

    @Override 
    public View newView(Context context, Cursor cursor, ViewGroup parent) { 

     Cursor c = getCursor(); 

     final LayoutInflater inflater = LayoutInflater.from(context); 
     View v = inflater.inflate(layout, parent, false); 

     int nameCol = c.getColumnIndex(DataBaseNamesHelper.COLUMN_NAME); 

     String name = c.getString(nameCol); 

     /** 
     * Next set the name of the entry. 
     */ 
     TextView name_text = (TextView) v.findViewById(R.id.name_entry); 
     if (name_text != null) { 
      name_text.setText(name); 
     } 

     int favCol = c.getColumnIndex(DataBaseNamesHelper.COLUMN_FAVOURITED); 
     int fav = c.getInt(favCol); 

     int idCol = c.getColumnIndex(DataBaseNamesHelper.COLUMN_ID); 

     Button button = (Button) v.findViewById(R.id.Button01); 
     button.setOnClickListener(this); 
     button.setTag(c.getInt(idCol)); 
     if(fav==1){ 
      button.setVisibility(View.INVISIBLE); 
     } 
     else button.setVisibility(View.VISIBLE); 

     return v; 
    } 

    @Override 
    public void bindView(View v, Context context, Cursor c) { 

     int nameCol = c.getColumnIndex(DataBaseNamesHelper.COLUMN_NAME); 

     String name = c.getString(nameCol); 

     /** 
     * Next set the name of the entry. 
     */ 
     TextView name_text = (TextView) v.findViewById(R.id.name_entry); 
     if (name_text != null) { 
      name_text.setText(name); 
     } 
     int favCol = c.getColumnIndex(DataBaseNamesHelper.COLUMN_FAVOURITED); 
     int fav = c.getInt(favCol); 

     Button button = (Button) v.findViewById(R.id.Button01); 
     button.setOnClickListener(this); 
     int idCol = c.getColumnIndex(DataBaseNamesHelper.COLUMN_ID); 
     button.setTag(c.getInt(idCol)); 
     // Log.e("fav",String.valueOf(fav)); 
     if(fav==1){ 
      button.setVisibility(View.INVISIBLE); 
     } else button.setVisibility(View.VISIBLE); 
    } 



    @Override 
    public int getPositionForSection(int section) { 
     return alphaIndexer.getPositionForSection(section); 
    } 

    @Override 
    public int getSectionForPosition(int position) { 
      return alphaIndexer.getSectionForPosition(position); 
    } 

    @Override 
    public Object[] getSections() { 
      return alphaIndexer.getSections(); 
    } 
    @Override 
    public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { 
     Log.e("item Click", arg1.toString()+ " position> " +arg2); 
    } 

    @Override 
    public void onClick(View v) { 
      if(v.getId()==R.id.Button01){ 
       //Log.e("Button Click", v.toString()+ " position> " +v.getTag().toString()); 
       v.setVisibility(View.INVISIBLE); 
       DataBaseNamesHelper dbNames = new DataBaseNamesHelper(context); 
       dbNames.setFavouritesFlag(v.getTag().toString()); 
      } 

     } 



} 
+1

क्या आपके उपयोगकर्ता वास्तव में ListView में 8000 पंक्तियों के माध्यम से स्क्रॉल करना चाहते हैं? क्या पहले सूची को फ़िल्टर करने का कोई तरीका है? – EboMike

+0

मेरे पास इंडेक्सिंग लागू करने के साथ एक fastscroll है। और मैं फ़िल्टर करने के लिए बाद में एक खोज विकल्प जोड़ दूंगा। – DArkO

+0

किसी भी मामले में, आप क्वेरी को एक अलग थ्रेड में कर सकते हैं और फिर एडाप्टर को असाइन कर सकते हैं, लेकिन यह अभी भी 7 सेकंड के लिए आपके ऐप को काला छोड़ देगा। जब तक आपके पास 7 सेकंड के लिए उपयोगकर्ता का मनोरंजन करने का कोई तरीका न हो? – EboMike

उत्तर

25

एडाप्टर लोड करने में धीमी गति का कारण आंतरिक कॉल कर्सर एडाप्टर Cursor.getCount() को बनाता है।

एंड्रॉइड में कर्सर आलसी लोड हो गए हैं। परिणाम तब तक लोड नहीं होते जब तक उनकी आवश्यकता न हो। जब कर्सर एडाप्टर getCount() को कॉल करता है तो यह क्वेरी को पूरी तरह से निष्पादित करने के लिए मजबूर करता है और परिणाम गिना जाता है।

नीचे इस मुद्दे पर चर्चा करने वाले कुछ लिंक हैं।

http://groups.google.com/group/android-developers/browse_thread/thread/c1346ec6e2310c0c

http://www.androidsoftwaredeveloper.com/2010/02/25/sqlite-performance/

मेरे सुझाव आपकी क्वेरी को विभाजित करने के लिए होगा। स्क्रीन पर केवल दृश्य सूची आइटमों की संख्या लोड करें। चूंकि उपयोगकर्ता स्क्रॉल अगले सेट को लोड करता है। जीमेल और बाजार अनुप्रयोगों की तरह बहुत अधिक है। दुर्भाग्य से मैं काम एक उदाहरण :(

यह आपके सवाल का जवाब नहीं है नहीं है, लेकिन उम्मीद है कि यह कुछ अंतर्दृष्टि :)

2

ठीक है, मैं केवल आप एक गूंगा सुझाव इस बिंदु पर की पेशकश कर सकते हैं - एक अलग थ्रेड कि पूरे डेटाबेस के माध्यम से जाने के लिए और अपने 8000-पंक्ति कर्सर पैदा करेगा में एक प्रश्न शुरू करते हैं।

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

एक बार आपका दूसरा धागा पूरा हो जाने के बाद एडाप्टर को प्रतिस्थापित करें।

वैकल्पिक रूप से, आपके पास एक सरणी एडाप्टर या कुछ समान हो सकता है, प्रत्येक पंक्ति में 100 पंक्तियों के साथ प्रश्नों का एक गुच्छा करें, और उन्हें अपने सरणी एडाप्टर में जोड़ें जैसे वे अंदर आते हैं। इससे भी जोड़ना आसान हो जाएगा नीचे एक डमी पंक्ति जो उपयोगकर्ताओं को अपने घोड़ों को पकड़ने के लिए कहती है।

एक नोट - मुझे लगता है कि डेटाबेस से एक ही समय में दो धागे से पढ़ने के लिए सुरक्षित है, लेकिन जब लिखने की बात आती है तो सावधान रहें। मेरे ऐप में एक अद्भुत बग था, जहां मैंने डाटाबेस को एक अलग थ्रेड में डेटाबेस ऑपरेशंस किया जब तक कि मैंने उचित लॉकिंग जोड़ा (जिसे आप डेटाबेस में भी सक्षम कर सकते हैं)।

संपादित करें: बेशक, AsyncQueryHandler, मुझे पता था कि ऐसा कुछ था, लेकिन मुझे नाम याद नहीं आया। निश्चित रूप से एक अलग थ्रेड से अधिक सुरुचिपूर्ण।

बीटीडब्ल्यू - आप डेटाबेस को कैसे संग्रहीत कर रहे हैं? क्या यह आपके आंतरिक भंडारण में एक नियमित डेटाबेस फ़ाइल है?

+0

हाँ बस एक नियमित आंतरिक डेटाबेस। मैं एक सामग्री प्रदाता का उपयोग नहीं कर रहा हूं क्योंकि मुझे अनुप्रयोगों में डेटा साझा करने की आवश्यकता नहीं है। मैं एडाप्टर को खाली करने के लिए पहले सेट करने की कोशिश कर रहा हूं और फिर एडाप्टर में कर्सर को बदलने के लिए, लेकिन अब तक कोई भाग्य नहीं है, यह केवल मुझे एक खाली सूची देता है। मुझे परेशान करने वाला यह है कि क्वेरी को पूरा होने में केवल 600 एमएस लगते हैं, लेकिन सूची असाइनमेंट 7 सेकंड होता है, इसलिए मुझे लगता है कि धीमी प्रतिक्रिया क्वेरी से सही नहीं होती है या क्या मुझे कुछ याद आ रहा है? – DArkO

+0

ओह भयानक, मैंने इसे गलत समझा। आप किस प्रकार का एडाप्टर उपयोग कर रहे हैं? मैं कस्टम कर्सर एडाप्टर देखता हूं - कस्टम हिस्सा क्या है? – EboMike

+0

यह SimpleCursorAdapter को बढ़ाता है। यहां मैं इसे बनाने के लिए उपयोग किया जाता है। http://thinkandroid.wordpress.com/2010/01/11/custom-cursoradapters/ – DArkO

3

सुनिश्चित नहीं है कि सेट एडाप्टर इतना लंबा क्यों लेना चाहिए। मैंने उससे लगभग 5000 पंक्तियों को बहुत तेज़ किया है। हालांकि, मैं आमतौर पर कर्सर के बिना एडाप्टर बना देता हूं, "एडाप्टर" के साथ सेट एडाप्टर को कॉल करता हूं, फिर एक AsyncQueryHandler सबक्लास का उपयोग करके एक एसिंक्रोनस क्वेरी को लात मारता हूं। फिर, ONQueryComplete में, मैं एडाप्टर पर परिवर्तन कर्सर कॉल करता हूं।

1

हाय im 50000rows से अधिक के लिए के साथ इस हासिल की कोशिश कर सकते हैं। मुझे लगता है कि मेरा कोड आपको आपके लिए बेहतर परिणाम दे सकता है क्योंकि आपके पास केवल 8000 रिकॉर्ड हैं।

1) एसक्लाइट के बजाय सामग्री प्रदाता का उपयोग करें। 2) कर्सर एसिंक्रोनस 3 लोड करने के लिए लोडरमेनगर कॉलबैक का उपयोग करें) एफटीएस टेबल का उपयोग करने के लिए। 4) इंडेक्सिंग के साथ भी यूआरबी अनुकूलित करें।

मेरा विश्वास करो कि यह धारा इंडेक्सर और फास्ट स्क्रॉल के साथ भी 8000 पंक्तियों के साथ बहुत बढ़िया काम करता है।

अगर आपको कोई संदेह है तो मुझे बताएं।

पीएस अगर आपको एक बेहतर समाधान मिला, जो आपको लगता है कि 50000 पंक्तियों को संभाल सकता है तो कृपया मुझे बताएं।