2011-12-22 17 views
29

Tôi muốn triển khai AsyncTaskLoader trong dự án của mình bằng Gói Tương thích, vì vậy tôi đã làm theo hướng dẫn Trình tải trong Tài liệu Android.AsyncTaskLoader không chạy

Vấn đề là Loader không có gì, có vẻ như loadInBackground() không bao giờ được gọi là

Bất kỳ ý tưởng về những gì sai trái trong mã của tôi? (ExpandableListFragment kéo dài Fragment, nhưng không ghi đè lên bất kỳ phương pháp quan trọng)

Cảm ơn bạn :-)

/** EDIT:

tôi nhận ra (cuối, tôi là một moron) mà AsyncTaskLoader là một lớp trừu tượng vì vậy tôi cần phải phân lớp nó ... m (__) m tôi rời khỏi câu hỏi trong trường hợp ai đó đến đây phía sau tôi, ai mà biết được ...

public class AgendaListFragment extends ExpandableListFragment implements 
     LoaderManager.LoaderCallbacks<JSONArray> { 

    private TreeMap<Integer, ArrayList<Evento>> mItems = new TreeMap<Integer, ArrayList<Evento>>(); 
    private AgendaListAdapter mAdapter; 
    private ProgressBar mProgressBar; 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
      Bundle savedInstanceState) { 
     View root = inflater.inflate(R.layout.fragment_agenda, container); 
     mProgressBar = (ProgressBar) root.findViewById(R.id.loading); 
     return root; 

    } 

    @Override 
    public void onActivityCreated(Bundle savedInstanceState) { 
     super.onActivityCreated(savedInstanceState); 

     mAdapter = new AgendaListAdapter(getActivity()); 
     setListAdapter(mAdapter); 

     getLoaderManager().initLoader(0, null, this); 

    } 

    @Override 
    public Loader<JSONArray> onCreateLoader(int arg0, Bundle arg1) { 
     mProgressBar.setVisibility(View.VISIBLE); 
     return new AsyncTaskLoader<JSONArray>(getActivity()) { 
      @Override 
      public JSONArray loadInBackground() { 

       return getDataFromService(AgendaServices.LISTADO_MES); 

      } 

     }; 
    } 

    @Override 
    public void onLoadFinished(Loader<JSONArray> loader, JSONArray data) { 

     // Some stuff to turn JSONArray into TreeMap 

     mProgressBar.setVisibility(View.GONE); 
     mAdapter.setItems(mItems); 

    } 

    @Override 
    public void onLoaderReset(Loader<JSONArray> arg0) { 
     mAdapter.setItems(null); 
     mProgressBar.setVisibility(View.VISIBLE); 

    } 

} 
+3

'AsyncTaskLoader' của bạn có vẻ thiếu nhiều thứ, như 'deliverResults()'. Tôi có hai triển khai 'AsyncTaskLoader' trong dự án' LoaderEx' của bạn mà bạn có thể muốn kiểm tra cho mục đích so sánh: https://github.com/commonsguy/cwac-loaderex – CommonsWare

+0

Có vẻ như tôi là một mớ hỗn độn và tôi đã không nhận ra nó một lớp trừu tượng ... Vì vậy, bây giờ tôi biết tại sao tôi chỉ tìm thấy một vài ví dụ phân lớp nó. Cảm ơn bạn! –

Trả lời

62

Tôi nghĩ rằng giải pháp tốt nhất cho các gói tương thích là để ghi đè lên AsyncTaskLoader.onStartLoading phương pháp.

ví dụ:

@Override 
protected void onStartLoading() { 
    if(dataIsReady) { 
    deliverResult(data); 
    } else { 
    forceLoad(); 
    } 
} 
+0

Tôi đã làm điều này ở cuối :-D. Cảm ơn! –

+0

Vâng cảm ơn david!Đã viết bài đăng này vào một bài đăng có mã nguồn: http://blog.blundell-apps.com/tut-asynctask-loader-using-support-library/ – Blundell

+2

Kiểm tra 'takeContentChanged' có vẻ là một bước quan trọng. –

7

Đây chính xác là một sửa chữa nhưng nó sẽ hoạt động. Tôi khá chắc chắn rằng thư viện tương thích bị hỏng. Hãy thử điều này:

getLoaderManager().initLoader(0, null, this).forceLoad(); 
+0

Đây là một cách khác để thực hiện :-D. –

+5

Điều này sẽ hiệu quả, nhưng có lẽ thực hành tốt hơn để thực hiện 'Loader ' đúng cách (như davidshen84 gợi ý, bằng cách implemenitng phương thức 'onStartLoading()' thay vì gọi 'forceLoad()' trực tiếp trong 'Activity' và/hoặc 'Fragment'. –

1

Nhìn vào thảo luận tại https://code.google.com/p/android/issues/detail?id=14944, kiểm tra takeContentChanged có vẻ là bước quan trọng quá.

protected void onStartLoading() { 
    if (mCursor != null) { 
     deliverResult(mCursor); 
    } 
    if (takeContentChanged() || mCursor == null) { 
     forceLoad(); 
    } 
} 
+0

Không có điểm nào trong việc sử dụng 'takeContentChanged' ở đây - nó sẽ không ảnh hưởng đến kết quả. Nếu 'mCursor' là null -' forceLoad' sẽ được gọi. Nếu không - 'deliverResult' sẽ được gọi. –

+1

Tại sao bạn nói 'takeContentChanged' không có điểm? http://developer.android.com/reference/android/content/AsyncTaskLoader.html Ví dụ của Google thực hiện việc sử dụng điều đó. –

+0

Bởi vì khi một hoạt động nhập 'onPause()' và quay trở lại với 'onResume()', trình nạp được thông báo và sẽ gọi lại 'onStartLoading()'. Vì vậy, 'takeContentChanged()' (nếu 'onContentChanged()' được gọi trước đó) sẽ buộc phải cập nhật 'mCursor' với các thay đổi có sẵn cuối cùng. Tuy nhiên, trên 'if' đầu tiên tôi đặt thay vào đó' if (mCursor! = Null &&! TakeContentChanged()) '. – AxeEffect

-2

Tôi vẫn gặp sự cố tải dữ liệu không được gọi. Cuối cùng tôi đã xóa AsyncTaskLoader (phiên bản thư viện hỗ trợ) và chỉ sử dụng AsyncTask (không phải từ thư viện hỗ trợ) để thực hiện công việc. Va no đa hoạt động.

Cũng có thể đủ cho nhu cầu của bạn.

Mô tả và ví dụ: http://developer.android.com/reference/android/os/AsyncTask.html.

Bạn phải mở rộng lớp AsyncTask.

Phương pháp này doInBackground sẽ làm việc và trong phương pháp onPostExecute bạn sẽ nhận được kết quả. Để bắt đầu AsyncTask, bạn sẽ gọi phương thức thực hiện trên trường hợp của nó. Xem liên kết.

+1

AsyncTask và AsyncTaskLoader là những thứ khá khác nhau ... AsyncTaskLoader có một số lợi thế. Bạn nên nghiên cứu thêm một chút –

3

Cheok Yan Cheng là hoàn toàn đúng:

Kiểm tra takeContentChanged dường như một bước quan trọng quá.

Nếu bạn viết phương pháp của bạn như thế này:

protected void onStartLoading() { 
    forceLoad(); 
} 

bạn '' sẽ nhận thấy rằng khi một hoạt động trẻ đi lên và sau đó bạn quay lại mẹ một, onStartLoading (và do đó loadInBackground) được gọi là lần nữa!

Bạn có thể làm gì? Đặt biến nội bộ (mContentChanged) thành đúng bên trong hàm tạo; sau đó kiểm tra biến này bên trong onStartLoading. Chỉ khi đó là sự thật, hãy bắt đầu tải thật:

package example.util; 

import android.content.Context; 
import android.support.v4.content.AsyncTaskLoader; 

public abstract class ATLoader<D> extends AsyncTaskLoader<D> { 

    public ATLoader(Context context) { 
     super(context); 
     // run only once 
     onContentChanged(); 
    } 

    @Override 
    protected void onStartLoading() { 
     // That's how we start every AsyncTaskLoader... 
     // - code snippet from android.content.CursorLoader (method onStartLoading) 
     if (takeContentChanged()) { 
      forceLoad(); 
     } 
    } 
} 
0

tôi lấy mã nguồn của CursorLoader từ khuôn khổ android, và đã viết một lớp CustomTaskLoader<T> để giảm bớt công việc.

https://github.com/Palatis/danbooru-gallery-android/blob/new_api/DanbooruGallery/src/main/java/tw/idv/palatis/danboorugallery/android/content/CustomTaskLoader.java

bạn về cơ bản thực hiện hai chức năng:

public abstract T runTaskInBackground(CancellationSignal signal); 
public abstract void cleanUp(T oldResult); 

xem cách sử dụng trong các hoạt động và các mảnh vỡ, ví dụ này: (tốt mã của tôi chỉ lờ đi CancellationSignal, đó là một TODO trong danh sách của tôi, nhưng bạn được tự do sử dụng nó.)

https://github.com/Palatis/danbooru-gallery-android/blob/new_api/DanbooruGallery/src/main/java/tw/idv/palatis/danboorugallery/PostListFragment.java

return new CustomTaskLoader<Cursor>(getActivity().getApplicationContext()) 
{ 
    @Override 
    public Cursor runTaskInBackground(CancellationSignal signal) 
    { 
     return SiteSession.getAllPostsCursor(PostListAdapter.POST_COLUMNS); 
    } 

    @Override 
    public void cleanUp(Cursor oldCursor) 
    { 
     if (!oldCursor.isClosed()) 
      oldCursor.close(); 
    } 
} 
0

Tôi đã gặp sự cố tương tự sau khi di chuyển từ CursorLoader sang AsyncTaskLoader.

documentation says: lớp con của Loader<D> thường phải thực hiện ít nhất onStartLoading(), onStopLoading(), onForceLoad(), và onReset().

AsyncTaskLoader mở rộng Trình tải nhưng không thực hiện onStartLoading(), onStopLoading(), onReset(). Bạn phải tự mình thực hiện nó!

@ davidshen84 đề xuất giải pháp tốt. Tôi chỉ thêm kiểm tra cho takeContentChanged.

@Override 
protected void onStartLoading() { 
    try { 
     if (data != null) { 
      deliverResult(data); 
     } 
     if (takeContentChanged() || data == null) { 
      forceLoad(); 
     } 

     Log.d(TAG, "onStartLoading() "); 
    } catch (Exception e) { 
     Log.d(TAG, e.getMessage()); 
    } 
} 

Sử dụng forceLoad() là ok (không phải là thực hành không tốt). Xem tài liệu nào says:
Bạn thường chỉ nên gọi điều này khi bộ nạp được khởi động - tức là isStarted() trả về giá trị true.

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