Suppose I have an android app with a Feed class, which is called by some Fragment implemented as follows and throwing : java.lang.ClassCastException: com.newsfeeder.ui.MainFragment cannot be cast to android.app.LoaderManager$LoaderCallbacks
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
public class MainFragment extends Fragment implements LoaderManager.LoaderCallbacks<List<Feed>>
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View v=inflater.inflate(R.layout.main_container, null);
listView = (ListView) v.findViewById(R.id.list);
getLoaderManager().initLoader(0, null, (android.app.LoaderManager.LoaderCallbacks<Object>) this);
return v;
}
public Loader<List<Feed>> onCreateLoader(int id, Bundle args) {
final List<Feed> initialItems = items;
return new ThrowableLoader<List<Feed>>(getActivity(), items) {
#Override
public List<Feed> loadData() throws Exception {
try {
if(getActivity() != null) {
return serviceProvider.getFeeds(); //some method fetching some `feeds`
} else {
return Collections.emptyList();
}
} catch (OperationCanceledException e) {
Activity activity = getActivity();
if (activity != null)
activity.finish();
return initialItems;
}
}
};
}
protected List<Feed> items = Collections.emptyList();
Btw ThrowableLoader class is implemented as follow
import android.content.Context;
public abstract class ThrowableLoader<D> extends AsyncLoader<D> {
private final D data;
private Exception exception;
/**
* Create loader for context and seeded with initial data
*
* #param context
* #param data
*/
public ThrowableLoader(Context context, D data) {
super(context);
this.data = data;
}
#Override
public D loadInBackground() {
exception = null;
try {
return loadData();
} catch (Exception e) {
Ln.d(e, "Exception loading data");
exception = e;
return data;
}
}
/**
* #return exception
*/
public Exception getException() {
return exception;
}
/**
* Clear the stored exception and return it
*
* #return exception
*/
public Exception clearException() {
final Exception throwable = exception;
exception = null;
return throwable;
}
/**
* Load data
*
* #return data
* #throws Exception
*/
public abstract D loadData() throws Exception;
}
and here's the `AsyncLoader`
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
public abstract class AsyncLoader<D> extends AsyncTaskLoader<D> {
private D data;
/**
* Create async loader
*
* #param context
*/
public AsyncLoader(Context context) {
super(context);
}
#Override
public void deliverResult(D data) {
if (isReset())
// An async query came in while the loader is stopped
return;
this.data = data;
super.deliverResult(data);
}
#Override
protected void onStartLoading() {
if (data != null)
deliverResult(data);
if (takeContentChanged() || data == null)
forceLoad();
}
#Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
#Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
data = null;
}
}
The problem is you are mixing android.support.v4.app.LoaderManager with android.app.LoaderManager.
I guess you want to use the support library and hence you're using the android.support.v4.app.Fragment class (those imports are missing), therefore you should not do the ((android.app.LoaderManager.LoaderCallbacks<Object>)this) cast but just implement the android.support.v4.app.LoaderManager.LoaderCallbacks<List<Feed>> interface in your android.support.v4.app.Fragment and pass it without cast.
Related
I'm using Realm and data binding.
Before MutableRealmInteger, I used regular primitive type and used its setter, and UI changed automatically with the new value.
Now I want to use MutableRealmInteger type, but I don't know how to notify the UI of changes.
This is a part of my model:
public class MyModel extends RealmObject implements Observable {
...
public final MutableRealmInteger NewWayAAA = MutableRealmInteger.valueOf(0);
private String oldWayAAA = "0";
...
#Bindable
public String getOldWayAAA() {
return oldWayAAA.isEmpty() ? "0" : oldWayAAA;
}
public void setOldWayAAA (String param) {
if (!param.isEmpty()) {
this.oldWayAAA= param;
notifyPropertyChanged(BR.oldwayaaa);
}
}
...
/**
* Notifies listeners that all properties of this instance have changed.
*/
public synchronized void notifyChange() {
if (mCallbacks != null) {
mCallbacks.notifyCallbacks(this, 0, null);
}
}
/**
* Notifies listeners that a specific property has changed. The getter for the property
* that changes should be marked with {#link Bindable} to generate a field in
* <code>BR</code> to be used as <code>fieldId</code>.
*
* #param fieldId The generated BR id for the Bindable field.
*/
public void notifyPropertyChanged(int fieldId) {
if (mCallbacks != null) {
mCallbacks.notifyCallbacks(this, fieldId, null);
}
}
#Override
public void addOnPropertyChangedCallback(OnPropertyChangedCallback onPropertyChangedCallback) {
if (mCallbacks == null) {
mCallbacks = new PropertyChangeRegistry();
}
mCallbacks.add(onPropertyChangedCallback);
}
#Override
public void removeOnPropertyChangedCallback(OnPropertyChangedCallback onPropertyChangedCallback) {
if (mCallbacks != null) {
mCallbacks.remove(onPropertyChangedCallback);
}
}
MutableRealmInteger doesn't have setter and the usage of it is:
myModelIns.NewWayAAA.increment(10);
public class MyModel extends RealmObject implements Observable {
...
private final MutableRealmInteger NewWayAAA = MutableRealmInteger.valueOf(0);
...
#Bindable
public int getNewWayAAA() {
return NewWayAAA.getValue();
}
public void incrementNewWayAAA() {
NewWayAAA.increment();
notifyPropertyChanged(BR.newwayaaa);
}
...
I was trying to refactor my code and move my code from Async Task to a loader. I came to know about the benefits of loaders through the Android Performance video series Loaders Android Performance
I know why Loaders are used and what classes it has and stuff (The theory). However I am unable to grasp the working concept and thus wrote this poor code. Thus I am also not able to debug it.
**EDIT: I was able to make it work but I still think I am calling it in a wrong manner.
new EarthquakeAsyncTaskLoader(this).forceLoad();
If anyone can help me out, on this.........**
import android.content.AsyncTaskLoader;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import java.io.IOException;
import java.util.ArrayList;
public class EarthQuakeActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<ArrayList<EarthQuakes>> {
ArrayList<EarthQuakes> earthquakes = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_earth_quake);
getSupportLoaderManager().initLoader(1, null, this);
}// End of onCreate
#Override
public Loader<ArrayList<EarthQuakes>> onCreateLoader(int id, Bundle args) {
**new EarthquakeAsyncTaskLoader(this).forceLoad();**
}
#Override
public void onLoadFinished(Loader<ArrayList<EarthQuakes>> loader, ArrayList<EarthQuakes> data) {
}
#Override
public void onLoaderReset(Loader<ArrayList<EarthQuakes>> loader) {
}
public class EarthquakeAsyncTaskLoader extends AsyncTaskLoader<ArrayList<EarthQuakes>> {
public EarthquakeAsyncTaskLoader(Context context) {
super(context);
}
#Override
protected void onStartLoading() {
// If the data is there, don't start again
if (earthquakes != null) {
deliverResult(earthquakes);
} else {
//Start the loader
forceLoad();
}
}
#Override
public ArrayList<EarthQuakes> loadInBackground() {
// Get the populated list from QueryUtils java class
try {
// Here in QueryUtils, I am making an HTTP network call
// Thus it has to be done in a helper background thread
earthquakes = QueryUtils.getArrayList();
} catch (IOException e) {
e.printStackTrace();
}
return earthquakes;
}
#Override
public void deliverResult(ArrayList<EarthQuakes> data) {
// Feed the adapter with data and display it
ListView earthquakesList = (ListView) findViewById(R.id.listV);
final EarthQuakeAdapter adapter = new EarthQuakeAdapter(getApplicationContext(), data);
earthquakesList.setAdapter(adapter);
earthquakesList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
EarthQuakes currentEarthquake = adapter.getItem(i);
Uri earthquakeUri = Uri.parse(currentEarthquake.getUrl());
// Create a new intent to view the earthquake URI
Intent websiteIntent = new Intent(Intent.ACTION_VIEW, earthquakeUri);
// Send the intent to launch a new activity
startActivity(websiteIntent);
}
});
}
}//End of Async Task Loader
//EarthQuakes is my class. I don't think you'll need this. But anyway:
public class EarthQuakes {
private double mMagnitude;
private String mLocationSmallText;
private String mLocationMainText;
private String mDateOfEarthquake;
private String mUrl;
// Default Constructor
public EarthQuakes(Double mag, String locationSmallText, String locationMainCityName, String dateE, String Url) {
this.mMagnitude = mag;
this.mLocationSmallText = locationSmallText;
this.mLocationMainText = locationMainCityName;
this.mDateOfEarthquake = dateE;
this.mUrl = Url;
}
// Public getters
public Double getMagnitude() {
return mMagnitude;
}
public String getLocationSmallTextEarthquake() {
return mLocationSmallText;
}
public String getLocationLargeTextEarthquake() {
return mLocationMainText;
}
public String getDateOfEarthquake() {
return mDateOfEarthquake;
}
public String getUrl() {
return mUrl;
}
}
This alternative will also work:
getSupportLoaderManager().initLoader(1, null, this).forceload();
However, this is just a way around an issue with loaders that is mentioned here.
This issue happens if you are using AsyncTaskLoader that is not a CursorLoader.
You need to implement onStartLoading() and handle calling forceLoad() there. Highly recommend going through the issue page.
If you are using multiple loaders throughout your app and don't want to implement onStartLoading() every time. Here's a custom loader class you can include in your app and inherit from this instead of the usual AsyncTaskLoader:
WrappedAsyncTaskLoader.java(Original Author: Alexander Blom)
public abstract class WrappedAsyncTaskLoader<D> extends AsyncTaskLoader<D> {
private D mData;
/**
* Constructor of <code>WrappedAsyncTaskLoader</code>
*
* #param context The {#link Context} to use.
*/
public WrappedAsyncTaskLoader(Context context) {
super(context);
}
/**
* {#inheritDoc}
*/
#Override
public void deliverResult(D data) {
if (!isReset()) {
this.mData = data;
super.deliverResult(data);
} else {
// An asynchronous query came in while the loader is stopped
}
}
/**
* {#inheritDoc}
*/
#Override
protected void onStartLoading() {
super.onStartLoading();
if (this.mData != null) {
deliverResult(this.mData);
} else if (takeContentChanged() || this.mData == null) {
forceLoad();
}
}
/**
* {#inheritDoc}
*/
#Override
protected void onStopLoading() {
super.onStopLoading();
// Attempt to cancel the current load task if possible
cancelLoad();
}
/**
* {#inheritDoc}
*/
#Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
this.mData = null;
}
}
Im making an app for a Shopify website using the Shopify Android API and currently the sample app just lists each product in a simple text ListView. Can anyone aid me in trying to get the images and display them next to the text in the ListView. Any help would be amazing, sorry kinda new to this.
ProductListActivity.java
public class ProductListActivity extends SampleListActivity {
static final String EXTRA_COLLECTION_ID = "ProductListActivity.EXTRA_COLLECTION_ID";
private String collectionId;
private ProductDetailsTheme theme;
private boolean useProductDetailsActivity;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState, R.layout.product_list_activity);
setTitle(R.string.choose_product);
useProductDetailsActivity = true;
theme = new ProductDetailsTheme(getResources());
Intent intent = getIntent();
if (intent.hasExtra(EXTRA_COLLECTION_ID)) {
collectionId = intent.getStringExtra(EXTRA_COLLECTION_ID);
}
theme.setStyle(ProductDetailsTheme.Style.LIGHT);
theme.setShowProductImageBackground(false);
}
#Override
protected void onResume() {
super.onResume();
// If we haven't already loaded the products from the store, do it now
if (listView.getAdapter() == null && !isFetching) {
isFetching = true;
showLoadingDialog(R.string.loading_data);
Callback<List<Product>> callback = new Callback<List<Product>>() {
#Override
public void success(List<Product> products, Response response) {
isFetching = false;
dismissLoadingDialog();
onFetchedProducts(products);
}
#Override
public void failure(RetrofitError error) {
isFetching = false;
onError(error);
}
};
if (collectionId != null) {
getSampleApplication().getProducts(collectionId, callback);
} else {
getSampleApplication().getAllProducts(callback);
}
}
}
/**
* Once the products are fetched from the server, set our listView adapter so that the products appear on screen.
*
* #param products
*/
private void onFetchedProducts(final List<Product> products) {
runOnUiThread(new Runnable() {
#Override
public void run() {
List<String> productTitles = new ArrayList<String>();
for (Product product : products) {
productTitles.add(product.getTitle());
}
listView.setAdapter(new ArrayAdapter<>(ProductListActivity.this, R.layout.simple_list_item, productTitles));
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (useProductDetailsActivity) {
launchProductDetailsActivity(products.get(position));
} else {
createCheckout(products.get(position));
}
}
});
}
});
}
private void launchProductDetailsActivity(Product product) {
getSampleApplication().launchProductDetailsActivity(this, product, theme);
}
/**
* When the user selects a product, create a new checkout for that product.
*
* #param product
*/
private void createCheckout(final Product product) {
showLoadingDialog(R.string.syncing_data);
getSampleApplication().createCheckout(product, new Callback<Checkout>() {
#Override
public void success(Checkout checkout, Response response) {
dismissLoadingDialog();
onCheckoutCreated(checkout);
}
#Override
public void failure(RetrofitError error) {
onError(error);
}
});
}
/**
* If the selected product requires shipping, show the list of shipping rates so the user can pick one.
* Otherwise, skip to the discounts activity (gift card codes and discount codes).
*
* #param checkout
*/
private void onCheckoutCreated(Checkout checkout) {
if (checkout.isRequiresShipping()) {
startActivity(new Intent(ProductListActivity.this, ShippingRateListActivity.class));
} else {
startActivity(new Intent(ProductListActivity.this, DiscountActivity.class));
}
}
}
SampleListActivity.java
public class SampleListActivity extends SampleActivity {
protected ListView listView;
protected boolean isFetching;
#Override
protected void onCreate(Bundle savedInstanceState) {
onCreate(savedInstanceState, R.layout.list_activity);
}
protected void onCreate(Bundle savedInstanceState, int layoutId) {
super.onCreate(savedInstanceState);
setContentView(layoutId);
listView = (ListView) findViewById(R.id.list_view);
isFetching = false;
}
}
SampleActivity.java
public class SampleActivity extends AppCompatActivity {
private static final String LOG_TAG = SampleActivity.class.getSimpleName();
// The amount of time in milliseconds to delay between network calls when you are polling for Shipping Rates and Checkout Completion
protected static final long POLL_DELAY = 500;
protected Handler pollingHandler;
private ProgressDialog progressDialog;
private boolean webCheckoutInProgress;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
pollingHandler = new Handler();
initializeProgressDialog();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
#Override
protected void onResume() {
super.onResume();
// If we are being launched by a url scheme, check the scheme and retrieve the checkout token if provided
Intent intent = getIntent();
Uri uri = intent.getData();
String scheme = getString(R.string.web_return_to_scheme);
if (uri != null && TextUtils.equals(uri.getScheme(), scheme)) {
webCheckoutInProgress = false;
// If the app was launched using the scheme, we know we just successfully completed an order
onCheckoutComplete();
} else {
// If a Web checkout was previously launched, we should check its status
if (webCheckoutInProgress && getSampleApplication().getCheckout() != null) {
pollCheckoutCompletionStatus(getSampleApplication().getCheckout());
}
}
}
#Override
protected void onDestroy() {
super.onDestroy();
if (progressDialog != null) {
progressDialog.dismiss();
}
}
protected SampleApplication getSampleApplication() {
return (SampleApplication) getApplication();
}
/**
* Initializes a simple progress dialog that gets presented while the app is communicating with the server.
*/
private void initializeProgressDialog() {
if (progressDialog != null) {
progressDialog.dismiss();
}
progressDialog = new ProgressDialog(this);
progressDialog.setIndeterminate(true);
progressDialog.setTitle(getString(R.string.please_wait));
progressDialog.setCancelable(true);
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
SampleActivity.this.finish();
}
});
}
/**
* Present the progress dialog.
*
* #param messageId The identifier (R.string value) of the string to display in the dialog.
*/
protected void showLoadingDialog(final int messageId) {
runOnUiThread(new Runnable() {
#Override
public void run() {
progressDialog.setMessage(getString(messageId));
progressDialog.show();
}
});
}
protected void dismissLoadingDialog() {
runOnUiThread(new Runnable() {
#Override
public void run() {
progressDialog.dismiss();
}
});
}
protected void onError(RetrofitError error) {
onError(BuyClient.getErrorBody(error));
}
/**
* When we encounter an error with one of our network calls, we abort and return to the previous activity.
* In a production app, you'll want to handle these types of errors more gracefully.
*
* #param errorMessage
*/
protected void onError(String errorMessage) {
progressDialog.dismiss();
Log.e(LOG_TAG, "Error: " + errorMessage);
Toast.makeText(this, R.string.error, Toast.LENGTH_LONG).show();
finish();
}
/**
* Use the latest Checkout objects details to populate the text views in the order summary section.
*/
protected void updateOrderSummary() {
final Checkout checkout = getSampleApplication().getCheckout();
if (checkout == null) {
return;
}
((TextView) findViewById(R.id.line_item_price_value)).setText('$' + checkout.getLineItems().get(0).getPrice());
double totalDiscount = 0;
Discount discount = checkout.getDiscount();
if (discount != null && !TextUtils.isEmpty(discount.getAmount())) {
totalDiscount += Double.parseDouble(discount.getAmount());
}
((TextView) findViewById(R.id.discount_value)).setText("-$" + Double.toString(totalDiscount));
double totalGiftCards = 0;
List<GiftCard> giftCards = checkout.getGiftCards();
if (giftCards != null) {
for (GiftCard giftCard : giftCards) {
if (!TextUtils.isEmpty(giftCard.getAmountUsed())) {
totalGiftCards += Double.parseDouble(giftCard.getAmountUsed());
}
}
}
((TextView) findViewById(R.id.gift_card_value)).setText("-$" + Double.toString(totalGiftCards));
((TextView) findViewById(R.id.taxes_value)).setText('$' + checkout.getTotalTax());
((TextView) findViewById(R.id.total_value)).setText('$' + checkout.getPaymentDue());
if (checkout.getShippingRate() != null) {
((TextView) findViewById(R.id.shipping_value)).setText('$' + checkout.getShippingRate().getPrice());
} else {
((TextView) findViewById(R.id.shipping_value)).setText("N/A");
}
}
/**
* Polls until the web checkout has completed.
*
* #param checkout the checkout to check the status on
*/
protected void pollCheckoutCompletionStatus(final Checkout checkout) {
showLoadingDialog(R.string.getting_checkout_status);
getSampleApplication().getCheckoutCompletionStatus(new Callback<Boolean>() {
#Override
public void success(Boolean complete, Response response) {
if (complete) {
dismissLoadingDialog();
onCheckoutComplete();
} else {
pollingHandler.postDelayed(new Runnable() {
#Override
public void run() {
pollCheckoutCompletionStatus(checkout);
}
}, POLL_DELAY);
}
}
#Override
public void failure(RetrofitError error) {
onError(error);
}
});
}
/**
* When our polling determines that the checkout is completely processed, show a toast.
*/
private void onCheckoutComplete() {
dismissLoadingDialog();
webCheckoutInProgress = false;
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(SampleActivity.this, R.string.checkout_complete, Toast.LENGTH_LONG).show();
}
});
}
}
This is probably also useful Shopify API Product
package com.shopify.buy.model;
import android.text.TextUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import com.shopify.buy.dataprovider.BuyClientFactory;
import com.shopify.buy.utils.DateUtility;
import com.shopify.buy.utils.DateUtility.DateDeserializer;
import java.util.ArrayList;
import java.util.Date;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* A {#code Product} is an individual item for sale in a Shopify store.
*/
public class Product extends ShopifyObject {
#SerializedName("product_id")
private String productId;
#SerializedName("channel_id")
private String channelId;
private String title;
private String handle;
#SerializedName("body_html")
private String bodyHtml;
#SerializedName("published_at")
private Date publishedAtDate;
#SerializedName("created_at")
private Date createdAtDate;
#SerializedName("updated_at")
private Date updatedAtDate;
private String vendor;
#SerializedName("product_type")
private String productType;
private List<ProductVariant> variants;
private List<Image> images;
private List<Option> options;
private String tags;
private Set<String> tagSet;
private boolean available;
private boolean published;
/**
* #return {#code true} if this product has been published on the store, {#code false} otherwise.
*/
public boolean isPublished() {
return published;
}
/**
* #return The unique identifier for this product.
*/
public String getProductId() {
return productId;
}
/**
* #return The unique identifier of the Mobile App sales channel for this store.
*/
public String getChannelId() {
return channelId;
}
/**
* #return The title of this product.
*/
public String getTitle() {
return title;
}
/**
* #return The handle of the product. Can be used to construct links to the web page for the product.
*/
public String getHandle() {
return handle;
}
/**
* #return The description of the product, complete with HTML formatting.
*/
public String getBodyHtml() {
return bodyHtml;
}
/**
* Use {#link #getPublishedAtDate() getPublishedAtDate()}.
*/
#Deprecated
public String getPublishedAt() {
return publishedAtDate == null ? null : DateUtility.createDefaultDateFormat().format(publishedAtDate);
}
/**
* Use {#link #getCreatedAtDate() getCreatedAtDate()}.
*/
#Deprecated
public String getCreatedAt() {
return createdAtDate == null ? null : DateUtility.createDefaultDateFormat().format(createdAtDate);
}
/**
* Use {#link #getUpdatedAtDate() getUpdatedAtDate()}.
*/
#Deprecated
public String getUpdatedAt() {
return updatedAtDate == null ? null : DateUtility.createDefaultDateFormat().format(updatedAtDate);
}
/**
* #return The date this product was published.
*/
public Date getPublishedAtDate() {
return publishedAtDate;
}
/**
* #return The date this product was created.
*/
public Date getCreatedAtDate() {
return createdAtDate;
}
/**
* #return The date this product was last updated.
*/
public Date getUpdatedAtDate() {
return updatedAtDate;
}
/**
* #return The name of the vendor of this product.
*/
public String getVendor() {
return vendor;
}
/**
* #return The categorization that this product was tagged with, commonly used for filtering and searching.
*/
public String getProductType() {
return productType;
}
/**
* #return A list of additional categorizations that a product can be tagged with, commonly used for filtering and searching. Each tag has a character limit of 255.
*/
public Set<String> getTags() { return tagSet; }
/**
* #return A list {#link ProductVariant} objects, each one representing a different version of this product.
*/
public List<ProductVariant> getVariants() {
return variants;
}
/**
* #return A list of {#link Image} objects, each one representing an image associated with this product.
*/
public List<Image> getImages() {
return images;
}
/**
* #return {code true} if this product has at least one image, {#code false} otherwise.
*/
public boolean hasImage() {
return images != null && !images.isEmpty();
}
/**
* #return A list of {#link Option} objects, which can be used to select a specific {#link ProductVariant}.
*/
public List<Option> getOptions() {
return options;
}
/**
* #return {#code true} if this product is in stock and available for purchase, {#code false} otherwise.
*/
public boolean isAvailable() { return available; }
/**
* For internal use only.
*/
public boolean hasDefaultVariant() {
return variants != null && variants.size() == 1 && variants.get(0).getTitle().equals("Default Title");
}
/**
* Returns the {#code Image} for the {#code ProductVariant} with the given id
* #param variant the {#link ProductVariant} to find the {#link Image}
* #return the {#link Image} corresponding to the {#link ProductVariant} if one was found, otherwise the {#code Image} for the {#link Product}. This may return null if no applicable images were found.
*/
public Image getImage(ProductVariant variant) {
if (variant == null) {
throw new NullPointerException("variant cannot be null");
}
List<Image> images = getImages();
if (images == null || images.size() < 1) {
// we did not find any images
return null;
}
for (Image image : images) {
if (image.getVariantIds() != null && image.getVariantIds().contains(variant.getId())) {
return image;
}
}
// The variant did not have an image, use the default image in the Product
return images.get(0);
}
/**
* #param optionValues A list of {#link OptionValue} objects that represent a specific variant selection.
* #return The {#link ProductVariant} that matches the given list of the OptionValues, or {#code null} if no such variant exists.
*/
public ProductVariant getVariant(List<OptionValue> optionValues) {
if (optionValues == null) {
return null;
}
int numOptions = optionValues.size();
for (ProductVariant variant : variants) {
for (int i = 0; i < numOptions; i++) {
if (!variant.getOptionValues().get(i).getValue().equals(optionValues.get(i).getValue())) {
break;
} else if (i == numOptions - 1) {
return variant;
}
}
}
return null;
}
public static class ProductDeserializer implements JsonDeserializer<Product> {
#Override
public Product deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return fromJson(json.toString());
}
}
/**
* A product object created using the values in the JSON string.
*/
public static Product fromJson(String json) {
Gson gson = BuyClientFactory.createDefaultGson(Product.class);
Product product = gson.fromJson(json, Product.class);
List<ProductVariant> variants = product.getVariants();
if (variants != null) {
for (ProductVariant variant : variants) {
variant.productId = Long.parseLong(product.productId);
variant.productTitle = product.getTitle();
}
}
// Create the tagSet.
product.tagSet = new HashSet<>();
// Populate the tagSet from the comma separated list.
if (!TextUtils.isEmpty(product.tags)) {
for (String tag : product.tags.split(",")) {
String myTag = tag.trim();
product.tagSet.add(myTag);
}
}
return product;
}
}
simple_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:padding="#dimen/spacing_16dp"
android:textColor="#android:color/black"
android:textSize="#dimen/text_large">
</TextView>
I have an issue with runOnuiThread and AsyncTask getting called together.
My AsynchTask gets data to populate a listView through runOnUIThread call.
This Asych Task can get data even when UI is not in focus . It starts from a UI screen and runs until application is logged out.
Now data coming from this Task can populate only a particular listview.
Now if i invoke another Asynch Task from another view using call executeOnExecutor call for AsynchTask, the Asynch Task does not run to compeltion. It locks up.
If I comment out code for the never ending AsychTask called Receiver.. then all UI's listview get populated and no Asych Task locks.
This Receiver waits on a REST API call for response to return but since I am running through executeonExecutor call, it should be parallel processing.
I need to have the receiver running all the time as that is an integral of my application.
What strategy can I use here to fix this issue.
Here are my code snippets.
public class Receiver {
private final static String QUEUE_NAME = "hello";
private String m_ErrorMessage;
private IRunOnUIThreadCallback iRunOnUIThreadCallback;
private Send m_Received;
private int m_TimeoutDuration;//how long the reading of new message waits in milli seconds
public void SetCallback(IRunOnUIThreadCallback runOnUIThreadCallback)
{
iRunOnUIThreadCallback = runOnUIThreadCallback;
}
public void SetTimeoutDuration(int timeout)
{
m_TimeoutDuration = timeout;
}
public void StartReceiver(Send receiverInfo)
{
String receivedInfo = null;
try {
new ReceiveInfo ().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, receiverInfo);
}
catch(Exception exp)
{
m_ErrorMessage = exp.getMessage();
}
}
private class ReceiveInfo extends AsyncTask<Send, Void, Send>
{
//initiate vars
public receive() {
super();
//my params here
}
protected Message doInBackground(Send... receiverInfo)
{
Send recv=null;
try {
PreferenceSingleton single = PreferenceSingleton.getInstance();
final User user = single.getUser();
final SvcApi svc = LoginAuthSvc.init();
Send send=(Send)receiverInfo[0];
send.setUserId(user.getUsername());
//dxbrem
while (true) {
recv=svc.receive(send);
String str= recv.get();
if ((str == null || (str.trim().length() == 0))) {
continue;
}
//DJ uncomment
iRunOnUIThreadCallback.RunAfterIsReceived(recv);
//messages.add(message);
System.out.println(" [x] Received '" + recv + "'");
}
}catch(Exception exp)
{
m_ErrorMessage = exp.getMessage();
}
return recv;
}
}
public String getErrorMessage() {
return m_ErrorMessage;
}
}
public interface IRunOnUIThreadCallback {
public void RunAfterIsReceived(ByteSent m);
public void RunAfterIsReceived(Send m);
}
The class that handles this.. has the following code and
public class MainFragment extends Fragment implements MFragment.OnFragmentInteractionListener, IRunOnUIThreadCallback {
private Receiver mReceiver;
public void SetUICallbackOnMessageReceiver()
{
mReceiver.SetCallback(this);
}
private void callRunUIThread(final SentInfo m) {
getActivity().runOnUiThread(new Runnable() {
public void run() {
if (m!= null) {
mGridArray.add(message);
if (mListAdapter != null) {
mListAdapter.notifyDataSetChanged();
mListView.setSelection(mListAdapter.getCount());
mListView.smoothScrollToPosition(mListAdapter.getCount());
}
}
}
}); // end of runOnUiThread
}
#Override
public void RunAfterIsReceived(ByteSent m) {
}
#Override
public void RunAfterIsReceived(Sent m) {
SentInfo m= new SentInfo(false, recv.getInfo());
callRunUIThread(msg);
}
mListAdapter is the ListAdapater
mListView is the ListView
Here is the AsynchTask code
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
public class CallableTask<T> extends AsyncTask<Void,Double,T> {
private static final String TAG = CallableTask.class.getName();
public static <V> void invoke(Callable<V> call,Activity activity, TaskCallback<V> callback){
new CallableTask<V>(activity,call, callback).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR );
}
private Callable<T> callable_;
private AsyncTask<Void, Void, String> asyncTask_;
private Context context;
private Activity activity;
private Fragment fragmentActivity;
private android.support.v4.app.Fragment dynamicFragment;
private TaskCallback<T> callback_;
private Exception error_;
public CallableTask(Fragment actvy,Callable<T> callable, TaskCallback<T> callback) {
callable_ = callable;
callback_ = callback;
fragmentActivity=actvy;
}
public CallableTask(Activity actvy,Callable<T> callable, TaskCallback<T> callback) {
callable_ = callable;
callback_ = callback;
activity=actvy;
}
#Override
protected T doInBackground(Void... ts) {
T result = null;
try{
result = callable_.call();
} catch (Exception e){
Log.e(TAG, "Error invoking callable in AsyncTask callable: " + callable_, e);
error_ = e;
}
return result;
}
#Override
protected void onPostExecute(T r) {
if(error_ != null){
callback_.error(error_);
}
else {
callback_.success(r,activity);
}
}
public static <V> void invoke(Callable<V> call, Fragment _frg, TaskCallback<V> callback) {
new CallableTask<V>(_frg,call, callback).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR );
}
// public CallableTask(android.support.v4.app.Fragment chatActivity,Callable<T> callable, TaskCallback<T> callback) {
// callable_ = callable;
// callback_ = callback;
// dynamicFragment=chatActivity;
// }
public CallableTask(android.support.v4.app.Fragment actvy,Callable<T> callable, TaskCallback<T> callback) {
callable_ = callable;
callback_ = callback;
dynamicFragment=actvy;
}
public static <V> void invoke(Callable<V> call, android.support.v4.app.Fragment _frg, TaskCallback<V> callback) {
new CallableTask<V>(_frg,call, callback).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR );
}
}
This gets called here... only when clicking on a button Send.
CallableTask.invoke(new Callable<Sent>() {
#Override
public Sent call() throws Exception {
}, this, new TaskCallback<Sent>() {
#Override
public void success(Sent result, Context context) {
mGridArray.add(result);
if (mListAdapter != null) {
mListAdapter.notifyDataSetChanged();
mListView.setSelection(mListAdapter.getCount());
mListView.smoothScrollToPosition(mListAdapter.getCount());
}
#Override
public void error(Exception e) {
}
});
Thanks
Dhiren
I finally resolved this by running a Asynch.cancel call on the thread from the activity fragment that started this thread. when I move away from activity. If I did not , it blocked any other tasks from running,
Is there an equivalent of the iOS class NSNotificationCenter in Android ? Are there any libraries or useful code available to me ?
In Android there is not a central notification center as in ios.
But you can basically use Observable and Observer objects to achieve your task.
You can define a class like something below, just modify it for singleton use and add synchronized for concurrent use but the idea is the same:
public class ObservingService {
HashMap<String, Observable> observables;
public ObservingService() {
observables = new HashMap<String, Observable>();
}
public void addObserver(String notification, Observer observer) {
Observable observable = observables.get(notification);
if (observable==null) {
observable = new Observable();
observables.put(notification, observable);
}
observable.addObserver(observer);
}
public void removeObserver(String notification, Observer observer) {
Observable observable = observables.get(notification);
if (observable!=null) {
observable.deleteObserver(observer);
}
}
public void postNotification(String notification, Object object) {
Observable observable = observables.get(notification);
if (observable!=null) {
observable.setChanged();
observable.notifyObservers(object);
}
}
}
Take a look at the Otto event bus from Square:
http://square.github.com/otto/
It has essentially the same features as NSNotificationCenter but thanks to annotations and static typing it is easier to follow the dependencies of components and paths that events follow. It's much simpler to use than the stock Android broadcast API, IMO.
i had the same wondrings.. so i wrote this:
public class NotificationCenter {
//static reference for singleton
private static NotificationCenter _instance;
private HashMap<String, ArrayList<Runnable>> registredObjects;
//default c'tor for singleton
private NotificationCenter(){
registredObjects = new HashMap<String, ArrayList<Runnable>>();
}
//returning the reference
public static synchronized NotificationCenter defaultCenter(){
if(_instance == null)
_instance = new NotificationCenter();
return _instance;
}
public synchronized void addFucntionForNotification(String notificationName, Runnable r){
ArrayList<Runnable> list = registredObjects.get(notificationName);
if(list == null) {
list = new ArrayList<Runnable>();
registredObjects.put(notificationName, list);
}
list.add(r);
}
public synchronized void removeFucntionForNotification(String notificationName, Runnable r){
ArrayList<Runnable> list = registredObjects.get(notificationName);
if(list != null) {
list.remove(r);
}
}
public synchronized void postNotification(String notificationName){
ArrayList<Runnable> list = registredObjects.get(notificationName);
if(list != null) {
for(Runnable r: list)
r.run();
}
}
}
and a usage for this will be:
NotificationCenter.defaultCenter().addFucntionForNotification("buttonClick", new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, "Hello There", Toast.LENGTH_LONG).show();
}
});
tried to make the interface as similar to IOS as possible, but simpler (no object registration needed).
hope that helps:)
If you don't want to use Observer - it can be problematic in cases you want a Fragment to be your Observer cause you can't extend more then one class-
You can use google's Guava Library (https://code.google.com/p/guava-libraries/)
for "Function" and "Multimap" - although you can use as well HashMap> for the subscibersCollection
and implement something like this:
import java.util.Collection;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.base.Function;
public class EventService {
ArrayListMultimap<String, Function<Object, Void>> subscibersCollection;
private static EventService instance = null;
private static final Object locker = new Object();
private EventService() {
subscibersCollection = ArrayListMultimap.create();
}
public static EventService getInstance() {
if (instance == null) {
synchronized (locker) {
if (instance == null) {
instance = new EventService();
}
}
}
return instance;
}
/**
* Subscribe to the notification, and provide the callback functions in case
* notification is raised.
*
* #param notification
* - notification name
* #param func
* - function to apply when notification is raised
*/
public void addSubscription(String notification, Function<Object, Void> func) {
synchronized (subscibersCollection) {
if (!subscibersCollection.containsEntry(notification, func)) {
subscibersCollection.put(notification, func);
}
}
}
/**
* Remove subscription from notification
*/
public void removeSubscription(String notification,
Function<Object, Void> func) {
synchronized (subscibersCollection) {
subscibersCollection.remove(notification, func);
}
}
/**
* raise notification for all its subscribers
*
* #param notification
* - notification name
* #param data
* - update data
*/
public void publish(String notification, Object data) {
Collection<Function<Object, Void>> observableList = subscibersCollection
.get(notification);
for (Function<Object, Void> func : observableList) {
func.apply(data);
}
}
}
On the basis of Behlül answer, I change the code to make it closer to iOS NSNotificationCenter.
Another thing: the notifications will fire on the main thread
package com.oxygen.utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.os.Handler;
public class NotificationCenter {
//---------------- event type list ---------------------
public static enum NotificationID{
IMAGES_CACHE_READY
}
//---------------- singelton ---------------------------
private static NotificationCenter instance = null;
private NotificationCenter() { observables = new HashMap<NotificationID, MyObservable>(); }
public static synchronized NotificationCenter singelton() {
if (instance == null) {
instance = new NotificationCenter ();
}
return instance;
}
//-------------------------------------------
public class Notification {
private Object poster; // the object that post the event
private Object info; // event specific data
private NotificationID id; // event name
public Notification(Object poster, NotificationID id, Object info) {
super();
this.poster = poster;
this.info = info;
this.id = id;
}
public Object getPoster() {
return poster;
}
public Object getInfo() {
return info;
}
public NotificationID getId() {
return id;
}
}
//-------------------------------------------
public interface Notifiable {
public void onNotification(Notification notify);
}
//-------------------------------------------
protected class MyObservable {
List<Notifiable> observers = new ArrayList<Notifiable>();
public MyObservable() {
}
public void addObserver(Notifiable observer) {
if (observer == null) {
throw new NullPointerException("observer == null");
}
synchronized (this) {
if (!observers.contains(observer))
observers.add(observer);
}
}
public int countObservers() {
return observers.size();
}
public synchronized void deleteObserver(Notifiable observer) {
observers.remove(observer);
}
public synchronized void deleteObservers() {
observers.clear();
}
public void notifyObservers(Notification notify) {
int size = 0;
Notifiable[] arrays = null;
synchronized (this) {
size = observers.size();
arrays = new Notifiable[size];
observers.toArray(arrays);
}
if (arrays != null) {
for (Notifiable observer : arrays) {
observer.onNotification(notify);
}
}
}
}
//-------------------------------------------
HashMap<NotificationID, MyObservable > observables;
public void addObserver(NotificationID id, Notifiable observer) {
MyObservable observable = observables.get(id);
if (observable==null) {
observable = new MyObservable ();
observables.put(id, observable);
}
observable.addObserver(observer);
}
public void removeObserver(NotificationID id, Notifiable observer) {
MyObservable observable = observables.get(id);
if (observable!=null) {
observable.deleteObserver(observer);
}
}
public void removeObserver(Notifiable observer) {
for (MyObservable observable : observables.values()) {
if (observable!=null) {
observable.deleteObserver(observer);
}
}
}
public void postNotification(final Object notificationPoster, final NotificationID id, final Object notificationInfo) {
final MyObservable observable = observables.get(id);
if (observable!=null) {
// notification post to the maim (UI) thread
// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(AppContext.get().getMainLooper());
Runnable myRunnable = new Runnable() {
#Override
public void run() {
observable.notifyObservers(new Notification(notificationPoster, id, notificationInfo) );
}
};
mainHandler.post(myRunnable);
}
}
}
Listener sample:
public class CustomGridViewAdapter extends ArrayAdapter<Category> implements Notifiable {
int layoutResourceId;
public CustomGridViewAdapter(Context context, int layoutResourceId) {
super(context, layoutResourceId);
this.layoutResourceId = layoutResourceId;
loadCategories(false);
NotificationCenter.singelton().addObserver(NotificationID.IMAGES_CACHE_READY, this);
}
public void onDestroy() {
NotificationCenter.singelton().removeObserver(this);
}
#Override
public void onNotification(Notification notify) {
switch (notify.getId()) {
case IMAGES_CACHE_READY:
loadCategories(true);
break;
}
}
...
}