So, I'm trying to connect to Scryfall's API and do an autocomplete call. I've been able to use their other call properly but this one I think where I'm having issue.
Here is the call: https://api.scryfall.com/cards/autocomplete?q=fire
q is the query and it will return a list of up to 20 items that could be auto-completed with the word 'fire'.
{
"object":"catalog",
"total_values":20,
"data": [
"Fire // Ice","Fire Imp","Firefly","Fire Whip","Fire Ants","Firebolt","Fireball","Fire Drake","Fire Snake","Firespout","Firestorm","Fireblast","Fire-Field Ogre","Fire Urchin","Fire Bowman","Fire Dragon","Fire at Will","Fire Ambush","Firemaw Kavu","Fire Juggler"
]
}
I am using retrofit2 for android.
Here is some of my code.
This is my interface for the endpoints
public interface ScryfallEndPoints {
//https://api.scryfall.com/cards/named?fuzzy=
#GET("cards/named")
Call<Card> getCard(
#Query(value=("fuzzy")) String name);
//https://api.scryfall.com/cards/autocomplete?q=
#GET("cards/autocomplete")
Call<Card> getCardAutoComplete(
#Query(value=("q")) String name);
}
This is a method I use in my activity to perform the call.
private void loadCardList()
{
final ScryfallEndPoints apiService =
APIClient.getClient().create(ScryfallEndPoints.class);
Call<Map<String, String>> call = apiService.getCardAutoComplete(str);
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
call.enqueue(new Callback<Map<String, String>>()
{
#Override
public void onResponse(Call<Map<String, String>> call, Response<Map<String, String>> response)
{
Toast.makeText(SuggestionResults.this, "onResponse()", Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<Map<String, String>> call, Throwable t)
{
Toast.makeText(SuggestionResults.this, "onFailure()", Toast.LENGTH_SHORT).show();
}
});
//tv.setText(str);
}
Here is a method that is part of my model class.
#SerializedName("data")
private Map<String, String> cardList;
public Map<String, String> getCardList() {return cardList;}
So, I feel like there is definitely something maybe wrong in the way I am trying to access he data in my model class and maybe with the way I have it set up in my interface. When I make the call, it doesn't fail so I don't have error logs to show, i just know that it is going to the onFailure() method and I'm not sure why. I mostly need to figure this out then I can work on getting the list to populate. Also, if there is a way that I can see more of what is going on with the calls that I am making, that would be great too. Thanks!
#GET("cards/autocomplete")
Call<Card> getCardAutoComplete(
#Query(value=("q")) String name);
}
Then the calling part
//Call<Map<String, String>> call = apiService.getCardAutoComplete(str);
//It returns Call of Card type, hence it will be as follows
Call<Card> call = apiService.getCardAutoComplete(str);
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
call.enqueue(new Callback<Card>()
{
#Override
public void onResponse(Call<Card> call, Response<Card> response)
{
Toast.makeText(SuggestionResults.this, "onResponse()", Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<Card> call, Throwable t)
{
Toast.makeText(SuggestionResults.this, "onFailure()", Toast.LENGTH_SHORT).show();
}
});
Related
First of all, I am a great newbie in aAndroid development (and with a English not really perfect).
What is the simplest way to access my layout (simple TextView, here named "text") from onNewToken() method from FirebaseMessagingService class ?
I saw that we regularly talked about BroadcastReceivers but I also read that it was deprecated.
Of course I have errors with findViewById().
public class MyFirebaseMessagingService extends FirebaseMessagingService {
#Override
public void onNewToken(String device_token) {
#SuppressLint("HardwareIds") String device_id = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
DeviceService service = DeviceService.retrofit.create(DeviceService.class);
Call<String> call = service.insertToken(device_token,device_id);
call.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if (response.isSuccessful()) {
text.setText("Token registered");
} else {
text.setText("Token registering error");
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
text.setText("Token not transmitted");
}
});
}
}
BroadcastReceiver is not deprecated, but LocalBroadcastManager is deprecated.
you can register receiver without LocalBroadcastManager, using Context.registerReceiver().
And put your response result into SharedPreferences, and use the value of SharedPreferences as LiveData or Flow in your Activity, Fragment or ViewModel.
Or, use event bus with RxJava or Flow whatever
I'm dealing with the following api call https://pokeapi.co/api/v2/pokemon/, the problem is that it seems quite slow and each entry has itself several endpoints. Is it possible to update ListView asynchronously while making the api calls? I feel that it will probably be something like using the endpoint's next and previous keys and triggering listener events that update the Listview from the AsyncTask's doInBackground or onProgressUpdate methods. I'd appreciate any help, I feel that I have the beginning of an idea, but I need help finishing the thought.
You can definitely implement it through AsyncTask but I would rather suggest a solution using RxJava.
You can implement RxJava Chaining.
Sharing a code snippet how you can make a chaining call using RxJava.
private void fetchHackerNewsStoriesChaining() {
StoriesApiInterface storiesApiInterface = HackerNewsApiClient.getStoriesApiInterface();
storiesApiInterface.getTopStories()
.flatMapIterable(new Function<JsonArray, Iterable<?>>() {
#Override
public Iterable<?> apply(JsonArray jsonArray) throws Exception {
Log.d("Count", ""+jsonArray.size());
return jsonArray;
}
})
.flatMap(new Function<Object, ObservableSource<SingleStoryModelResponse>>() {
#Override
public ObservableSource<SingleStoryModelResponse> apply(Object newsId) throws Exception {
return HackerNewsApiClient.getStoryDetailsApiInterface().getNewsStoryDetail(((JsonElement) newsId).getAsLong())
.subscribeOn(Schedulers.io());
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<SingleStoryModelResponse>() {
#Override
public void onSubscribe(Disposable d) {
progressBar.setVisibility(View.VISIBLE);
}
#Override
public void onNext(SingleStoryModelResponse singleStoryModelResponse) {
adapterNewsList.addNewsItem(singleStoryModelResponse);
}
#Override
public void onError(Throwable e) {
Log.d("Hacker News", e.getMessage());
}
#Override
public void onComplete() {
progressBar.setVisibility(View.GONE);
}
});
}
I wanted to refactor code for simplicity and readability and that's why I want to move the code outside the class and return a result to class whenever the method is called.
Trying:
ArrayList<MovieReview> movieReview;
public ArrayList<MovieReview> getReviews(String id) {
if (NetworkUtil.isNetworkConnected(getActivity())) {
ApiInterface apiService =
ApiClient.getClient().create(ApiInterface.class);
Call<MovieReviewResponse> call = null;
call = apiService.getMovieReviews(id, BuildConfig.THE_MOVIE_DB_API_KEY);
call.enqueue(new Callback<MovieReviewResponse>() {
#Override
public void onResponse(Call<MovieReviewResponse> call, Response<MovieReviewResponse> response) {
movieReview= (ArrayList<MovieReview>) response.body().getMovieReviews();
}
#Override
public void onFailure(Call<MovieReviewResponse> call, Throwable t) {
// Log error here since request failed
Log.e(TAG, t.toString());
}
});
}
return movieReview;
}
Output: if I used array list outside the on response gives null value.
but if I called a method from on response and pass the result movieReview, as a parameter, it works fine.
Previously used:
public void getReviews(String id) {
if (NetworkUtil.isNetworkConnected(getActivity())) {
ApiInterface apiService =
ApiClient.getClient().create(ApiInterface.class);
Call<MovieReviewResponse> call = null;
call = apiService.getMovieReviews(id, BuildConfig.THE_MOVIE_DB_API_KEY);
call.enqueue(new Callback<MovieReviewResponse>() {
#Override
public void onResponse(Call<MovieReviewResponse> call, Response<MovieReviewResponse> response) {
movieReview = (ArrayList<MovieReview>) response.body().getMovieReviews();
setData(movieReview);
}
#Override
public void onFailure(Call<MovieReviewResponse> call, Throwable t) {
// Log error here since request failed
Log.e(TAG, t.toString());
}
});
}
}`
Output: if I used array list outside the on response gives null value
Because that is how asyncronus methods work. Your return happens before onResponse ever happens, so the list object is null.
Tip: In ideal situations, you want to always return an empty list, not null anyway.
Rename your method.
public ArrayList<MovieReview> getReviews(String id)
To Instead
public ArrayList<MovieReview> getReviews(String id, Callback<MovieReviewResponse> callback)
Replace this code
call.enqueue(new Callback<MovieReviewResponse>() {
...
});
With this
call.enqueue(callback);
Wherever you call that method
// In Activity
String id = "X";
api.getReviews(id);
Now do....
// In Activity
String id = "X";
api.getReviews(id, new Callback<MovieReviewResponse>() {
...
});
And now from within onResponse, you can update a ListView adapter, or whatever you need to do
Instead of using only Retrofit make use of RxAndroid. By using this you will get response of Observable<T> which consists of three Override methods onCompleted(), onError() and onNext().
In onNext() method call your specific activity, pass your data through putExtra and get through getExtra.
I have a Service class that I am using to make web service calls using Volley:
public class AccountService {
public static void forgotPassword(Context c, String emailAddress) {
String url = "myUrl";
JsonArrayRequest request = new JsonArrayRequest(url,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
// done
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
Volley.newRequestQueue(c).add(request);
}
}
And I'm calling it from an Activity like this:
public class ForgotPasswordActivity extends AppCompatActivity implements View.OnClickListener{
private void submit() {
accountService.forgotPassword();
}
}
When the Volley request is finished, I want to update the UI in my activity. How can I do this considering it is an asynchronous request? Am I able to call a method from the AccountService class in my activity? Thanks for the help
Pass a listener object to the AccountService and then send communication success or error to the activity.
Try as per below (you should not pass the layout to the AccountService which will create some unnecessary issues)
public class ForgotPasswordActivity extends AppCompatActivity
implements View.OnClickListener,
AccountServiceCallback{
private void submit() {
AccountService.forgotPassword("email#123.com", this);
}
#Override
public void onClick(View v) {
}
#Override
public void onResponse(JSONArray response) {
// UPDATE UI as per on response requirement
}
#Override
public void onErrorResponse(VollyError error) {
// UPDATE UI as per response flow
}
}
public class AccountService {
public static void forgotPassword(Context c, String email, final
AccountServiceCallback callback) {
String url = "myUrl";
JsonArrayRequest request = new JsonArrayRequest(url,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
// done
callback.onResponse(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
callback.onErrorResponse(error);
}
});
Volley.newRequestQueue(c).add(request);
}
interface AccountServiceCallback {
public void onResponse(JSONArray response);
public void onErrorResponse(VollyError error);
}
}
You will have to find a way to get the result of your background work back onto the main thread so it can make changes to the view hierarchy. There are a LOT of ways to do this, and most of them are not really good.
But before you even do that, there are problems with your code. First, if you are thinking you made an Android Service component, you didn't actually do that. You made a class with the name AccountService, which does nothing special in the Android world.
Second, your call of forgotPassword() passes no arguments, but your definition of forgotPassword() in AccountService has a completely different signature. This wouldn't compile.
You should probably start learning about Android-specific asynchronous programming patterns and develop a strategy for solving your problem before writing code, because you'll almost certainly do it wrong without understanding what you're doing first.
I'm totally new to RxJava and I've spent all day understanding it, I'm tying to think how to solve this problem:
I have one object, fetched by Retrofit, it contains two ArrayLists, I have to process every ArrayList differently. Currently it looks like:
apiService.getUser(token).enqueue(new Callback<User>() {
#Override
public void onResponse(Response<User> response) {
final User user = response.body();
for (Skill s : user.getSkills()) {
// process here first ArrayList
}
for (OrganizerAction o : user.getOrganizerActions()) {
// process here second ArrayList
}
}
#Override
public void onFailure(Throwable t) {
t.printStackTrace();
}
});
UPDATE:
public class User {
// fields
#SerializedName("organizer_actions")
#Expose
private List<OrganizerAction> mOrganizerActions;
#SerializedName("skills")
#Expose
private List<Skill> mSkills;
public List<OrganizerAction> getOrganizerActions() {
return mOrganizerActions;
}
public List<Skill> getSkills() {
return mSkills;
}
}
Thanks,
Anton
This answer is for Retrofit 2.0.0-beta, which is what you appear to be using. Also, you didn't give your POJO or service definitions, so going to use a general GitHub API example as a guide, modify to match your specify data.
First step is to convert your service definition to use Observable instead of Call.
public interface GitHubService {
#GET("/users/{user}")
Observable<User> getUser(#Path("user") String user);
}
Where User is
public class User {
public String login;
public int id;
}
Next, add a custom call adapter with to your retrofit builder with addCallAdapterFactory --
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
Get your service in the usual way --
GitHubService gitHubService = retrofit.create(GitHubService.class);
Next, get your observable and call cache on it to create an Observable that will replay the result. From that Observable, you can subscribe multiple times, in your case, you can subscribe twice. Once for each type of data you are interested in, and use the map function to transform from the User object to your specific fields. map allows you to apply function to the data in the observable. See the docs for more details. In this example, we will make two streams. One each for the id and login fields.
Observable<User> getUserResult = gitHubService.getUser("octocat").cache(1);
getUserResult.map(new Func1<User, Integer>() {
#Override
public Integer call(User user) {
return user.id;
}
}).subscribe(new Action1<Integer>() {
#Override
public void call(Integer id) {
Log.d("Stream 1", "id = " + id);
}
});
getUserResult.map(new Func1<User, String>() {
#Override
public String call(User user) {
return user.login;
}
}).subscribe(new Action1<String>() {
#Override
public void call(String login) {
Log.d("Stream 2", "login = " + login);
}
});
Finally, make sure your gradle file has the needed dependencies,
compile 'io.reactivex:rxjava:1.0.14'
compile 'com.squareup.retrofit:retrofit:2.0.0-beta1'
compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta1'
And, not directly related to your question, but if you are going to doing RxJava in Android, I recommend you checkout Retrolambda if you have not already. The above map and subscribe code, and Rx code in general, is more succinct with lambdas.
getUserResult.map(user -> user.id).subscribe(
id -> { Log.d("Stream 1", "id = " + id); }
);
getUserResult.map(user -> user.login).subscribe(
login -> { Log.d("Stream 2", "login = " + login); }
);