2010-04-25 45 views
8

Tôi đang chạy vào một IllegalStateException cập nhật Danh sách cơ bản thành Bộ điều hợp (có thể là một ArrayAdapter hoặc phần mở rộng của BaseAdapter, tôi không nhớ). Tôi không có hoặc nhớ văn bản của ngoại lệ tại thời điểm này, nhưng nó nói một cái gì đó để ảnh hưởng của nội dung của Danh sách thay đổi mà không có Adapter đã được thông báo về sự thay đổi.Cách tốt nhất để cập nhật dữ liệu cơ bản của Adapter là gì?

Danh sách này/có thể/được cập nhật từ một chủ đề khác ngoài chuỗi giao diện người dùng (chính). Sau khi tôi cập nhật danh sách này (thêm một mục), tôi gọi notifyDataSetChanged. Vấn đề có vẻ là Adapter hoặc ListView được gắn vào Adapter cố gắng tự cập nhật trước khi phương thức này được gọi. Khi điều này xảy ra, IllegalStateException được ném.

Nếu tôi đặt chế độ hiển thị của Chế độ xem danh sách thành GONE trước khi cập nhật, sau đó VISIBLE lại, không xảy ra lỗi. Nhưng điều này không phải lúc nào cũng thực tế.

Tôi đọc ở đâu đó mà bạn không thể sửa đổi điều này từ chuỗi khác - điều này dường như giới hạn mẫu MVC, như với Danh sách cụ thể này, tôi muốn thêm các mục từ các chủ đề khác nhau. Tôi giả định rằng miễn là tôi gọi notifyDataSetChanged() tôi sẽ được an toàn - rằng Adapter đã không truy cập lại Danh sách cơ bản cho đến khi phương thức này được gọi nhưng điều này dường như không đúng.

Tôi cho rằng điều tôi đang hỏi là có an toàn để cập nhật Danh sách cơ bản từ các chủ đề khác ngoài giao diện người dùng không? Ngoài ra, nếu tôi muốn sửa đổi dữ liệu trong Bộ điều hợp, tôi có sửa đổi Danh sách cơ bản hoặc Bộ điều hợp (thông qua các phương thức add(), v.v.) của nó hay không. Sửa đổi dữ liệu thông qua Bộ điều hợp có vẻ sai.

Tôi đã xem một chuỗi trên một trang web khác từ một người có vẻ đang gặp vấn đề tương tự với tôi: http://osdir.com/ml/Android-Developers/2010-04/msg01199.html (đây là nơi tôi đã lấy ý tưởng Visibility.GONE và .VISIBLE).

Để cung cấp cho bạn ý tưởng tốt hơn về vấn đề cụ thể của tôi, tôi sẽ mô tả một chút về cách Danh sách, Bộ điều hợp, v.v. của tôi được thiết lập.

Tôi có một đối tượng có tên Hàng đợi có chứa một LinkedList. Queue mở rộng Observable, và khi mọi thứ được thêm vào danh sách nội bộ của nó thông qua các phương thức của nó, tôi gọi setChanged() và notifyListeners(). Đối tượng Queue này có thể có các mục được thêm vào hoặc loại bỏ khỏi bất kỳ số lượng chủ đề nào.

Tôi có một hoạt động "xem hàng đợi" duy nhất chứa Bộ điều hợp. Hoạt động này, trong phương thức onCreate() của nó, đăng ký trình lắng nghe Observer vào đối tượng Queue của tôi. Trong phương thức update() của Observer, tôi gọi notifyDataSetChanged() trên Adapter.

Tôi đã thêm nhiều đầu ra nhật ký và xác định rằng khi IllegalStateExcption này xảy ra thì lời gọi lại Observer của tôi chưa bao giờ được gọi. Vì vậy, nó giống như Adapter nhận thấy sự thay đổi của List trước khi Observer có cơ hội thông báo cho Observer của nó, và gọi phương thức của tôi để thông báo cho Adapter rằng nội dung đã thay đổi.

Vì vậy, tôi cho rằng điều tôi đang hỏi là, đây có phải là cách hay để lắp đặt bộ điều hợp không? Đây có phải là một vấn đề bởi vì tôi đang cập nhật nội dung của Adapter từ một chủ đề khác với chuỗi giao diện người dùng? Nếu đây là trường hợp, tôi có thể có một giải pháp trong đầu (cung cấp cho đối tượng Queue một Trình xử lý cho chuỗi giao diện người dùng khi nó được tạo và thực hiện tất cả các sửa đổi Danh sách bằng Trình xử lý đó, nhưng điều này có vẻ không đúng).

Tôi nhận thấy rằng đây là một bài đăng rất mở, nhưng tôi hơi lạc mất về điều này và sẽ đánh giá cao bất kỳ nhận xét nào về nội dung tôi đã viết.

Trả lời

8

Danh sách này/có thể/được cập nhật từ khác thread khác với giao diện người dùng chủ đề (chính)

Điều đó sẽ không làm việc.

Tôi đọc ở đâu đó rằng bạn không thể thay đổi cơ bản này từ thread khác - điều này có vẻ hạn một mô hình MVC, như với Danh sách đặc biệt này, tôi muốn thêm các mục từ chủ đề khác nhau

MVC không liên quan gì đến chủ đề.

có thể an toàn để cập nhật danh sách cơ bản từ chủ đề khác so với giao diện người dùng không?

số chủ đề khác có thể kích hoạt bản cập nhật cho các bộ chuyển đổi (ví dụ, thông qua post()), nhưng các bản cập nhật bản thân phải được xử lý trên thread ứng dụng chính, cho một adapter mà hiện tại đang gắn liền với một ListView.

Thêm vào đó, nếu tôi muốn sửa đổi dữ liệu trong một Adapter, làm tôi sửa đổi danh sách bên dưới hoặc Adaptor bản thân (thông qua add() của nó, vv phương pháp). Sửa đổi dữ liệu qua Bộ chuyển đổi có vẻ sai.

Bạn sửa đổi Adapter của mình qua số Adapter chính nó cho ArrayAdapter. Bạn sửa đổi số Adapter của mình thông qua nhà cung cấp nội dung/cơ sở dữ liệu cơ bản cho CursorAdapter. Các bộ điều hợp khác có thể khác nhau.

Tôi có một đối tượng có tên Hàng đợi chứa một LinkedList. Hàng đợi mở rộng Có thể quan sát và khi mọi thứ được thêm vào danh sách nội bộ thông qua các phương thức , tôi gọi setChanged() và notifyListeners().

Bạn đã xem xét sử dụng LinkedBlockingQueue, thay vì triển khai chủ đề an toàn của riêng mình Queue?

Phương thức này, trong phương thức onCreate() , đăng ký người nghe Observer vào đối tượng Queue của tôi. Trong phương thức cập nhật của Observer() tôi gọi thông báoDataSetChanged() trên Bộ điều hợp.

Adapters nên gọi notifyDataSetChanged() về bản thân (nếu thay đổi được thực hiện bởi chúng) hoặc có nó kêu gọi chúng bằng những thực thể đó đang thay đổi dữ liệu (ví dụ, một Cursor cho một CursorAdapter). Đó là là MVC. Các Activity không nên biết và cũng không quan tâm khi mô hình dữ liệu của bạn thay đổi.

Vì vậy, nó như thể Adaptor nhận thấy sự thay đổi các Danh sách trước khi Observer đã một cơ hội để thông báo cho nhà quan sát của nó, và gọi phương pháp của tôi để thông báo cho Adaptor rằng nội dung đã thay đổi.

Có thể bạn đang sử dụng ArrayAdapter, trong trường hợp này, tất cả những người quan sát/thông báo bổ sung này đang cản trở bạn, vì nó được xử lý cho bạn. Bạn chỉ cần sắp xếp để cập nhật ArrayAdapter trên chuỗi ứng dụng chính.

Vì vậy, tôi cho rằng điều tôi đang hỏi là đây có phải là cách hay để thiết lập bộ điều hợp không?

Không đặc biệt, IMHO.

Đây có phải là sự cố vì tôi đang cập nhật nội dung của Bộ điều hợp từ một sợi khác với chuỗi giao diện người dùng?

Nếu bạn không buộc cập nhật trở lại luồng ứng dụng chính, điều này cuối cùng sẽ bị lỗi, khi bạn xóa các vấn đề khác của mình.

cho Queue phản đối một Handler thread UI khi nó được tạo ra, và làm cho Liệt kê tất cả những thay đổi sử dụng mà Handler, nhưng điều này dường như không đúng

Bạn có thể sử dụng một Handler, hoặc bạn có thể gọi post() trên ListView đính kèm của bạn.

Tắt vòng đeo tay, tôi sẽ tạo một lớp con của ArrayAdapter có tên ThreadSafeArrayAdapter và sử dụng nó thay cho Queue. ThreadSafeArrayAdapter thay thế add(), insert()remove() với những người có siêu lớp làm điều của nó trên chuỗi ứng dụng chính, qua Handler hoặc post().

+0

Cảm ơn nhận xét của bạn, nhưng tôi vẫn còn bối rối về một vài điều, nếu bạn không ngại giúp đỡ thêm. Tên đối tượng "Hàng đợi" là một chút nhầm lẫn - đó là hàng đợi phương tiện để phát video hoặc phát bài hát. Đó là danh sách các mục có int hiện tại. Tôi hiểu Bộ điều hợp chỉ là cầu nối giữa Mô hình của tôi (Danh sách của tôi) và chế độ xem (ListView). Điều gì sẽ xảy ra nếu tôi cần cập nhật dữ liệu mà chế độ xem hiển thị từ một Hoạt động khác mà tôi không có quyền truy cập vào Bộ điều hợp? Nó có vẻ tự nhiên để cập nhật danh sách cơ bản, có lẽ từ một Runnable nếu nhiệm vụ thực hiện điều này có thể chặn. – skyler

+0

"Tôi hiểu Bộ điều hợp chỉ là cầu nối giữa Mô hình của tôi (Danh sách của tôi) và chế độ xem (ListView)". Điều đó thường đúng. "Điều gì xảy ra nếu tôi cần cập nhật dữ liệu mà chế độ xem hiển thị từ một Hoạt động khác mà tôi không có quyền truy cập vào Bộ điều hợp?" Rất có thể, bạn không có quyền truy cập vào Danh sách, vì vậy, bạn sẽ cần một chiến lược phức tạp hơn. "Có vẻ như tự nhiên để cập nhật Danh sách cơ bản, có lẽ từ một Runnable nếu nhiệm vụ thực hiện điều này có thể chặn." IMHO, vấn đề luồng là một vấn đề giao diện người dùng, đó là lý do tại sao tôi khuyên bạn nên Adapter của bạn là một trong những để đối phó với an toàn thread. – CommonsWare

+0

Tuy nhiên, bạn có thể thực hiện nó theo bất kỳ cách nào bạn muốn, miễn là bạn chỉ cập nhật Bộ điều hợp trên luồng ứng dụng chính. – CommonsWare

0

Nhìn chung lời khuyên tốt là http://developer.android.com/resources/articles/painless-threading.html

Cá nhân tôi sử dụng chủ đề tùy chỉnh của tôi (một lớp mở rộng chủ đề) nhưng gửi thư trả lời đến thread UI thông qua một tin nhắn. Vì vậy, trong hàm run() của luồng có:

Message msg; 
msg = Message.obtain(); 
msg.what = MSG_IMG_SET;      
mExtHandler.sendMessage(msg); 

MExtHandler được gán cho cá thể xử lý bên ngoài trong bộ tạo chuỗi. Chuỗi giao diện người dùng xác định trình xử lý tin nhắn:

private Handler mImagesProgressHandler; 

public void onCreate(Bundle bundle) { 

    mImagesProgressHandler = new Handler() { 
    @Override 
    public void handleMessage(Message msg) { 
     switch (msg.what) {    
     case LoadImagesThread.MSG_IMG_SET: 
      mArrayAdapter.setBitmapList(mImagesList); 
      mArrayAdapter.notifyDataSetChanged(); 
      break; 
     case LoadImagesThread.MSG_ERROR: 
      break; 
     } 
     super.handleMessage(msg); 
    } 
};     

Điều này thực sự dễ dàng hơn AsyncTask.

+0

Hãy cẩn thận với các lớp trình xử lý bên trong không tĩnh. Có thể là một nguy cơ rò rỉ bộ nhớ. – HAxxor

+0

Điểm tốt, cảm ơn. – Yar

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