2011-01-08 25 views
6

Tôi đã viết một ListActivity có bộ điều hợp danh sách tùy chỉnh. Danh sách đang được cập nhật từ một ContentProvider khi onCreate được chạy. Tôi cũng có một dịch vụ được bắt đầu khi tôi chạy ứng dụng và nó lần đầu tiên cập nhật ContentProvider, sau đó gửi một Broadcast rằng nội dung đã được cập nhật.
Danh sách của tôiKích hoạt nhận được chương trình phát và cố gắng cập nhật ListView của tôi. Vấn đề của tôi là, tôi nhận được lỗi liên tục về dữ liệu adapter ListView thay đổi mà không có ListView được thông báo. Tôi gọi phương thức notifyDataSetChanged() trên bộ điều hợp danh sách của tôi ngay sau khi tôi cập nhật nó. Những gì nó có vẻ như đang xảy ra là danh sách vẫn đang trong quá trình được cập nhật sau khi lần đầu tiên gọi onCreate khi nó nhận được phát sóng từ dịch vụ để cập nhật, vì vậy nó sẽ cố gắng cập nhật ListView của tôi trước khi nó hoàn thành cập nhật từ lần chạy đầu tiên. Điều này có nghĩa không? Đây là một số mã của tôi.Thay đổi dữ liệu bộ điều hợp ListView mà không ListView được thông báo

LƯU Ý: Dịch vụ hoạt động bình thường, dịch vụ này nhận dữ liệu mới và cập nhật Trình hiển thị nội dung của tôi và tôi sẽ phát sóng trong hoạt động của mình khi cập nhật.

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    ctx = this; 
    getPrefs(); 
    setContentView(R.layout.main); 

    // Setup preference listener 
    preferences = PreferenceManager.getDefaultSharedPreferences(this); 
    preferences.registerOnSharedPreferenceChangeListener(listener); 


    // Setup report list adapter 
    ListView nzbLv = (ListView) findViewById(R.id.report_list); 
    nzbla = new NZBReportListAdaptor(ctx); 
    getReports(); 
    nzbla.setListItems(report_list);    
    nzbLv.setAdapter(nzbla);   
    // Broadcast receiver to get notification from NZBService to update ReportList 
    registerReceiver(receiver, 
      new IntentFilter(NZBService.BROADCAST_ACTION)); 

    startService(new Intent(ctx, NZBService.class)); 
} 

@Override 
public void onResume() { 
    super.onResume(); 
    timerHandler.resume();  
new updateSabQueue().execute(); 
    //updateList(); 
} 

@Override 
public void onPause() { 
    super.onPause(); 
    timerHandler.pause(); 
    unregisterReceiver(receiver); 
} 


private BroadcastReceiver receiver = new BroadcastReceiver() { 
    public void onReceive(Context context, Intent intent) { 
     Toast.makeText(ctx, "NZBService broadcast recieved", Toast.LENGTH_SHORT).show(); 
     updateReportList(); 
    } 
}; 


private void updateReportList() { 
    new updateReportList().execute(); 
} 



private class updateReportList extends AsyncTask<Void, Void, Boolean> { 

    /* (non-Javadoc) 
    * @see android.os.AsyncTask#onPreExecute() 
    * Show progress dialog 
    */ 
    protected void onPreExecute() { 
    } 

    /* (non-Javadoc) 
    * @see android.os.AsyncTask#doInBackground(Params[]) 
    * Get new articles from the internet 
    */ 
    protected Boolean doInBackground(Void...unused) { 
     getReports(); 
     return true; 
    } 

    /** 
    * On post execute. 
    * Close the progress dialog 
    */ 
    @Override 
    protected void onPostExecute(Boolean updated) { 
     if (updated) { 
      Log.d(TAG, "NZB report list adapter updated"); 
      synchronized(this) { 
       nzbla.setListItems(report_list);    
      } 
      Log.d(TAG, "NZB report list notified of change"); 
      nzbla.notifyDataSetChanged();       
     } 
    } 
} 

Bây giờ câu hỏi này được trả lời, tôi sẽ đăng mã cập nhật của mình nhằm giúp những người khác có thể gặp phải.

@Override 
    public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    ctx = this; 
    getPrefs(); 
setContentView(R.layout.main); 

    // Setup preference listener 
    preferences = PreferenceManager.getDefaultSharedPreferences(this); 
    preferences.registerOnSharedPreferenceChangeListener(listener); 

    // Setup report list adapter 
    ListView nzbLv = (ListView) findViewById(R.id.report_list); 
    nzbla = new NZBReportListAdaptor(ctx); 
    report_list.addAll(getReports()); 
    nzbla.setListItems(report_list);    
    nzbLv.setAdapter(nzbla);   
    // Broadcast receiver to get notification from NZBService to update ReportList 
    registerReceiver(receiver, 
      new IntentFilter(NZBService.BROADCAST_ACTION)); 

    startService(new Intent(ctx, NZBService.class)); 
} 


private class updateReportList extends AsyncTask<Void, Void, ArrayList<Report>> { 

    /* (non-Javadoc) 
    * @see android.os.AsyncTask#onPreExecute() 
    * Show progress dialog 
    */ 
    protected void onPreExecute() { 
    } 

    /* (non-Javadoc) 
    * @see android.os.AsyncTask#doInBackground(Params[]) 
    * Get new articles from the internet 
    */ 
    protected ArrayList<Report> doInBackground(Void...unused) { 
     return getReports(); 
    } 

    /** 
    * On post execute. 
    * Close the progress dialog 
    */ 
    @Override 
    protected void onPostExecute(ArrayList<Report> updated) { 
     nzbla.setListItems(updated);    
     nzbla.notifyDataSetChanged();       
    } 
} 


private ArrayList<Report> getReports() { 
    ArrayList<Report> reports = new ArrayList<Report>(); 
    ContentResolver r = getContentResolver(); 
    Cursor c = r.query(NZBReportProvider.CONTENT_URI, null, null, null, NZBReportProvider.ARTICLE_KEY_ROWID + " DESC"); 
    startManagingCursor(c); 
    Log.d(TAG, "NZBReport cursor.getCount=" + c.getCount()); 
    int title = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_TITLE); 
    int desc = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_DESCRIPTION); 
    int cat = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_CAT); 
    int size = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_SIZE); 
    int link = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_LINK); 
    int catid = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_CATID); 
    int date = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_DATE_ADDED); 
    int group = c.getColumnIndex(NZBReportProvider.ARTICLE_KEY_GROUP); 

    if (c.getCount() > 0) { 
     c.moveToFirst(); 
     do { 
      URL url = null; 
      try { 
       url = new URL(c.getString(link)); 
      } catch (MalformedURLException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
      reports.add(new Report(c.getString(title), url, c.getString(desc), c.getString(cat), c.getString(date), c.getString(size), c.getInt(catid), c.getString(group)));    
     } while (c.moveToNext());     
    } 
    return reports; 
} 

Trả lời

11

Bạn phải cập nhật dữ liệu bộ điều hợp trên chuỗi giao diện người dùng để không cần khối đồng bộ. Nó cũng vô ích kể từ khi bạn đồng bộ hóa trên AsyncTask được tạo mới mỗi khi nó được thực thi.

Một vấn đề khác là bạn đang gọi notifyDataSetChanged bên ngoài vào số Adapter. Bạn nên gọi nó ở cuối phương thức setListItems của bạn. Điều đó không nên gây ra lỗi mặc dù nó đang được thực thi trên luồng giao diện người dùng nhưng nó không nên được gọi theo cách đó.

Bạn nên đảm bảo rằng phương thức getReports của bạn không sửa đổi cửa hàng sao lưu của Adapter bằng bất kỳ cách nào. Vì nó đang chạy trên một sợi riêng biệt nên nó không thể sửa đổi bất cứ điều gì mà Adapter cũng có quyền truy cập. Ngay cả khi nó được bảo vệ bằng khóa. Những gì bạn cần làm là trong phương pháp doInBackground của bạn là tạo danh sách các bản cập nhật hoặc danh sách mới, v.v. và chuyển nó vào onPostExecute, sau đó cam kết dữ liệu mới đến Adapter trên chuỗi giao diện người dùng. Vì vậy, nếu chức năng getReports của bạn đang thay đổi report_listAdapter của bạn có tham chiếu đến report_list bạn đang làm sai. getReports phải tạo một report_list mới và sau đó chuyển lại số đó Adapter khi hoàn tất việc tạo nó trên chuỗi giao diện người dùng.

Để nhắc lại, bạn chỉ có thể sửa đổi dữ liệu Adapter và sau đó là ListView cũng có quyền truy cập trên chuỗi giao diện người dùng. Sử dụng đồng bộ/khóa không thay đổi yêu cầu này.

+1

Đây chính xác là vấn đề. Bộ điều hợp danh sách của tôi được hỗ trợ bởi một ArrayList của các đối tượng "report_list" và tôi đang sửa đổi nó trực tiếp trong phương thức getReports của tôi đang thực hiện trên một luồng riêng biệt.Tôi đã sửa đổi phương thức getReports của mình để hoạt động trên một ArrayList cục bộ và trả về nó (và chuyển nó vào phương thức onPostExecute của tôi). Trong khi tôi nhận thức được thực tế rằng tôi không thể hoạt động trên dữ liệu bộ điều hợp danh sách của mình bên ngoài chuỗi giao diện người dùng, tôi đã không nắm bắt nó 100% cho đến khi nó thất bại và buộc tôi tìm ra chính xác (hoặc hỏi chính xác hơn để được giúp đỡ ở đây!) Cảm ơn Qberticus! – brockoli

-1

Nếu bạn muốn cập nhật listview UI từ một dịch vụ thì bạn nên gọi notifyDataSetChanged() trên onDestroy của dịch vụ

làm adapter tĩnh từ hoạt động chính của bạn và gọi đó là adaptername.notifyDataSetChanged()

như thế này

@Override 
     public void onDestroy() { 

       if (MainActivity.isInFront == true) { 
         if (MainActivity.adapter != null) 
           MainActivity.adapter.notifyDataSetChanged(); 
         MainActivity.listView.setAdapter(MainActivity.adapter); 
       } 
}    
+0

Huh? Bạn không thể làm cho bộ điều hợp tĩnh và gọi các phương thức không tĩnh như notifyDataSetChanged từ nó. Heck, thậm chí không có một phương pháp gọi là isInFront, ngay cả khi có, bạn không thể chỉ gọi là trên lớp vì nó sẽ không tĩnh. – AfzalivE

+0

là ở phía trước là một phương pháp mà bạn phải thực hiện .. không có thức ăn thìa đang xảy ra ở đây .. hiểu được mã đầu tiên sau đó downvote ... –

+0

Bạn không thể xác định một phương pháp tĩnh để kiểm tra trạng thái của một thể hiện. Ngoài ra, bạn không thể truy cập vào cá thể của bất kỳ hoạt động nào mà không có ít nhất một tham chiếu đến nó, hoặc thiết lập một giao diện người nghe để cập nhật hoạt động thông qua đó. Một lý do khác khiến tôi downvoted là bởi vì bạn phải có tất cả các tác vụ đang chạy để biết hoạt động nào ở phía trước, hãy để một mình tìm nạp một cá thể của Activity đó, điều này hoàn toàn không cần thiết. Ngay cả khi bạn tự duy trì một boolean isInFront, bạn sẽ không thể tìm nạp cá thể một cách dễ dàng như vậy. – AfzalivE

Các vấn đề liên quan