1. isAdded Method in Fragment
When set Retain Instance (true) is set in Fragment, the Fragment can be temporarily separated from Activity when the device rotates, etc.
If Fragment holds background threads at this point, or AsyncTask and others need to use Fragment's Context and so on, errors may occur.
To this end, the isAdded interface is defined in Fragment to determine whether Fragment has been bound to an Activity.
2. Basic usage of AsyncTask
AsyncTask is mainly used to perform time-consuming operations in the background and return the execution results to UI threads.
The definition of AsyncTask is as follows. It can be seen that AsyncTask is an abstract class.
Therefore, in practice, a subclass inheriting this class is usually defined.
public abstract class AsyncTask<Params, Progress, Result> {
..........
}
As you can see from the above code, AsyncTask defines three generic types: Params, Progress and Result. Among them:
Params represents input parameters when starting AsyncTask, such as the URL of an HTTP request.
Progress represents the percentage of background tasks performed, such as Integer.
Result represents the final result of a background task, such as Long.
An example of Android's use is as follows:
//The input parameter is URL, the progress update parameter is Integer, and the return result is Long.
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
//Execute in the background thread, noting that the input parameter is URL and the output is long
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
//Perform download operations
totalSize += Downloader.downloadFile(urls[i]);
//Notify the main thread of update progress
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
//If AsyncTask is detected to be cancelled, the task is stopped
if (isCancelled()) break;
}
return totalSize;
}
//After calling publishProgress, call back the interface
protected void onProgressUpdate(Integer... progress) {
//Call methods in the main thread to update progress
setProgressPercent(progress[0]);
}
//After the doInBackground is executed, this interface is called
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
For the example above, using Download FilesTask is similar to:
.............
new DownloadFilesTask().execute(url1, url2, url3);
.............
The way to cancel Download FilesTask is similar to:
.............
//Download FilesTask saves the created AsyncTask
//When the parameter is false, the doInBackground detects isCancelled before stopping the task
//When the parameter is true, AsyncTask will stop immediately
downloadFilesTask.cancel(false);
.............
3. Basic usage of AsyncTaskLoader
When loading data with AsyncTask, if the device configuration changes or the user clicks the back button, etc.
The life cycle of AsyncTask needs to be managed, and download data needs to be saved.
At this point, consider using AsyncTaskLoader, which inherits from Loader (both native and support libraries are implemented).
Loader Manager helps us manage Loader and its loaded data properly when we encounter scenarios such as device rotation.
Furthermore, loader Manager is responsible for starting and stopping loaders and managing the lifecycle of loaders.
When the device configuration changes, if a loader that has loaded the data is initialized, it can submit the data immediately.
Instead of trying to retrieve data again.
The basic usage of AsyncTaskLoader is as follows:
public class PhotoGalleryFragment extends Fragment{
.............
//Specify the id corresponding to Loader
public static final int mLoaderId = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
//Define a callback interface, a callback can listen for multiple loader s
LoaderManager.LoaderCallbacks<List<GalleryItem>> callback =
new LoaderManager.LoaderCallbacks<List<GalleryItem>>() {
//When Loader Manager creates Loader, it calls back the interface
@Override
public Loader<List<GalleryItem>> onCreateLoader(int id, Bundle args) {
//Different Loader s can be created based on id
//The loader required to be created cannot be a non-static internal class
//The reason should be to avoid memory leaks
if (id == mLoaderId) {
Log.d("ZJTest", "onCreateLoader");
return new FetchItemsLoader(getActivity());
} else {
return null;
}
}
//When FetchItemsLoader loads the data, it calls back the interface
@Override
public void onLoadFinished(Loader<List<GalleryItem>> loader, List<GalleryItem> data) {
//You can also do different operations based on id
if (loader.getId() == mLoaderId) {
Log.d("ZJTest", "onLoadFinished");
//You can update the interface and so on.
mGalleryItems = data;
setupAdapter();
}
}
@Override
public void onLoaderReset(Loader<List<GalleryItem>> loader) {
}
};
//Start Loader with Loader Manager, specify the id, parameters and callback interface of the loader
getLoaderManager().restartLoader(mLoaderId, null, callback);
}
...................
//AsyncTaskLoader is also an abstract class, specifying the return value of loadInBackground in the template parameter
private static class FetchItemsLoader extends AsyncTaskLoader<List<GalleryItem>> {
FetchItemsLoader(Context context) {
super(context);
}
//This method must be implemented, forceLoad being the parent interface
//After calling the interface, the loadInBackground operation is executed
@Override
protected void onStartLoading() {
Log.d("ZJTest", "onStartLoading");
forceLoad();
}
//The return value will be returned to the onLoadFinished interface of callback
@Override
public List<GalleryItem> loadInBackground() {
Log.d("ZJTest", "loadInBackground");
return new ImageFetcher().fetchItems();
}
}
}
Let's run this code. log is similar to:
02-14 15:41:01.080 11297-11297/stark.a.is.zhang.photogallery D/ZJTest: onCreateLoader
02-14 15:41:01.089 11297-11297/stark.a.is.zhang.photogallery D/ZJTest: onStartLoading
02-14 15:41:01.095 11297-11313/stark.a.is.zhang.photogallery D/ZJTest: loadInBackground
02-14 15:41:01.562 11297-11297/stark.a.is.zhang.photogallery D/ZJTest: onLoadFinished
4. Examples of ViewTreeObserver usage
ViewTreeObserver can monitor the layout changes of View, and it has multiple monitoring interfaces.
Take OnGlobalLayoutListener as an example to briefly record how it is used:
//Let's take RecyclerView as an example to dynamically adjust the number of members per row of RecyclerView
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_photo_gallery, container, false);
//Find Recycler View
mPhotoRecyclerView = (RecyclerView) v.findViewById(R.id.fragment_photo_gallery_recycler_view);
//Get ViewTreeObserver for RecyclerView
ViewTreeObserver viewTreeObserver = mPhotoRecyclerView.getViewTreeObserver();
//Add an OnGlobalLayoutListener for ViewTreeObserver
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
private int last = 0;
//When the layout changes, that is, when drawing RecyclerView, the onGlobalLayout interface is called back.
@Override
public void onGlobalLayout() {
if (getView() != null) {
//In xml, we define the width of RecyclerView as match_parent
int width = getView().getWidth();
//Specify the width of each member to be 400 to get the number of members that can be placed in each column
int count = width / 400;
//When a change occurs, the Layout Manager of RecyclerView is reset
if (last != count) {
//Every time you change Layout Manger, RecyclerView is redrawn
//After switching between the horizontal and vertical screens, the number of members can be placed dynamically in each row.
mPhotoRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), count));
last = count;
}
}
}
});
setupAdapter();
return v;
}
5. RecyclerView Detection Method for Sliding to the Top or Bottom
When using RecyclerView, it is often necessary to detect whether the user is sliding to the top or bottom.
At this point, a method similar to the following can be used:
............
private void addOnScrollListener() {
//Add OnScrollListener for RecyclerView to monitor sliding events
mPhotoRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
private int lastVisibleItemPosition;
private int firstVisibleItemPosition;
//At the end of the slide, the onScrolled interface is called back
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy){
super.onScrolled(recyclerView, dx, dy);
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
//Get the current interface, the last sub-view corresponding to the position
lastVisibleItemPosition = ((GridLayoutManager) layoutManager)
.findLastVisibleItemPosition();
//Get the current interface, the position of the first subview
firstVisibleItemPosition = ((GridLayoutManager) layoutManager)
.findFirstVisibleItemPosition();
................
}
}
//Callback the interface when the sliding state changes
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
//Get the size of the visible data in the current interface
int visibleItemCount = layoutManager.getChildCount();
//Get the size of RecyclerView corresponding to all data
int totalItemCount = layoutManager.getItemCount();
//Judgment conditions can be adjusted according to actual needs
if (newState == RecyclerView.SCROLL_STATE_IDLE && visibleItemCount > 0) {
//When the position corresponding to the last view equals - 1, it means that the bottom is touched when the last slide is over.
if (lastVisibleItemPosition == totalItemCount - 1){
//Business on demand
startURLLoader();
//The position of the first view is equal to 0, indicating that the last slide ended when the top was touched.
} else if (firstVisibleItemPosition == 0) {
Toast.makeText(getActivity(),
"Already to the top",
Toast.LENGTH_SHORT)
.show();
}
}
}
});
}
..............
The specific use of OnScrollListener can be adjusted as needed.
But the main idea is to judge whether to slide to the top or bottom according to the position of the current view.
6. RecyclerView slides to the specified interface
..............
//When this interface is called, the view corresponding to mLastPosition will become the first view that the interface displays after sliding.
mPhotoRecyclerView.scrollToPosition(mLastPosition);
...............
RecyclerView also has other interfaces for sliding operations.
But it essentially depends on the operation corresponding to LayoutManager.
For example, scrollToPosition's source code is as follows:
..............
/**
* Convenience method to scroll to a certain position.
*
* RecyclerView does not implement scrolling logic, rather forwards the call to
* {@link android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)}
* @param position Scroll to this adapter position
* @see android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)
*/
public void scrollToPosition(int position) {
if (mLayoutFrozen) {
return;
}
stopScroll();
if (mLayout == null) {
Log.e(TAG, "Cannot scroll to position a LayoutManager set. " +
"Call setLayoutManager with a non-null argument.");
return;
}
//Depending on the implementation of the specific LayoutManager
mLayout.scrollToPosition(position);
awakenScrollBars();
}
..............
7. Basic usage of LruCache
Android provides a basic caching strategy, namely LRU (least recently used).
Based on this strategy, when the storage space is exhausted, the cache clears the least recently used objects.
An example of using LruCache is as follows:
public class ImageCache {
//Define LruCache, specify its key and the type of data it saves
private LruCache<String, Bitmap> mImageCache;
ImageCache() {
//Gets the memory size available to the current process, converted to KB in units
final int maxMemory = (int)(Runtime.getRuntime().maxMemory() / 1024);
//Take 1/4 of the total memory as the cache
final int cacheSize = maxMemory / 4;
//Initialize LruCache
mImageCache = new LruCache<String, Bitmap>(cacheSize) {
//Define the size of each storage object
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
};
}
//get data
public Bitmap getBitmap(String url) {
return mImageCache.get(url);
}
//Store data
public void putBitmap(String url, Bitmap bitmap) {
mImageCache.put(url, bitmap);
}
}
8. Simple Use of Picasso Library
Picasso library is a high-performance third-party image download library. When using this library, it can simply complete the functions of image download, caching and so on.
Its usage is as follows: Take RecyclerView as an example:
//Adapter defining RecyclerView
private class PhotoAdapter extends RecyclerView.Adapter<PhotoHolder> {
private List<GalleryItem> mGalleryItems;
PhotoAdapter(List<GalleryItem> galleryItems) {
mGalleryItems = galleryItems;
}
@Override
public PhotoHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(getActivity());
View view = inflater.inflate(R.layout.gallery_item, parent, false);
return new PhotoHolder(view);
}
@Override
public void onBindViewHolder(PhotoHolder holder, int position) {
GalleryItem galleryItem = mGalleryItems.get(position);
holder.bindGalleryItem(galleryItem, position);
}
@Override
public int getItemCount() {
return mGalleryItems.size();
}
}
//ViewHolder Defining RecyclerView
private class PhotoHolder extends RecyclerView.ViewHolder {
private ImageView mItemImageView;
private TextView mTextView;
PhotoHolder(View itemView) {
super(itemView);
mItemImageView = (ImageView) itemView.findViewById
(R.id.fragment_photo_gallery_image_view);
mTextView = (TextView) itemView.findViewById
(R.id.fragment_photo_gallery_text_view);
}
void bindGalleryItem(GalleryItem galleryItem, int position) {
mTextView.setText(getString(R.string.text_title, "" + position));
//Picasso library, pass Context in with
Picasso.with(getActivity())
//Address of incoming downloaded pictures in load
.load(galleryItem.getThumbURL())
//Selective use to specify a placeholder when the image is not downloaded
.placeholder(R.drawable.place_holder)
//Specify the layout where the downloaded image should be placed
.into(mItemImageView);
}
}
With the above method, the Picasso library will automatically read from the cache or download the view when it loads.