I try to return array of objects with this function:
public static JSONEvent[] invokeFunction(String funName, String requestContent) {
final String functionName = funName;
final String requestPayload = requestContent;
new AsyncTask<Void, Void, InvokeResult>() {
#Override
protected InvokeResult doInBackground(Void... params) {
try {
final ByteBuffer payload =
ENCODER.encode(CharBuffer.wrap(requestPayload));
final InvokeRequest invokeRequest =
new InvokeRequest()
.withFunctionName(functionName)
.withInvocationType(InvocationType.RequestResponse)
.withPayload(payload);
final InvokeResult invokeResult =
AWSMobileClient
.defaultMobileClient()
.getCloudFunctionClient()
.invoke(invokeRequest);
return invokeResult;
} catch (final Exception e) {
Log.e("LAMBDA", "AWS Lambda invocation failed : " + e.getMessage(), e);
final InvokeResult result = new InvokeResult();
result.setStatusCode(500);
result.setFunctionError(e.getMessage());
return result;
}
}
#Override
protected void onPostExecute(final InvokeResult invokeResult) {
try {
final int statusCode = invokeResult.getStatusCode();
final String functionError = invokeResult.getFunctionError();
final String logResult = invokeResult.getLogResult();
if (statusCode != 200) {
//showError(invokeResult.getFunctionError());
} else {
final ByteBuffer resultPayloadBuffer = invokeResult.getPayload();
//resultPayloadBuffer.rewind();
// while (resultPayloadBuffer.hasRemaining())
// Log.e("BUFFER",resultPayloadBuffer.position() + " -> " + resultPayloadBuffer.get());
// User a = new User(23, 24);
//
// User b = new User(58, 59);
// User[] ab = new User[] {a, b};
// User [] events = new User[3];
ObjectMapper mapper = new ObjectMapper();
final String resultPayload = DECODER.decode(resultPayloadBuffer).toString();
Log.e("LAMBDA-SUCCESS", resultPayload);
try {
// String s2 = getJson2(ab);
// Log.e("S2", s2);
//User[] user2 = mapper.readValue(resultPayload, User[].class);
events = mapper.readValue(resultPayload, JSONEvent[].class);
// for (JSONEvent u : events)
// Log.e("USER",u.getLocationLat()+"");
Log.e("ARRAY",Arrays.toString(events));
} catch (Exception e) {
e.printStackTrace();
}
//return resultPayload;
// mResultField.setText(resultPayload);
}
if (functionError != null) {
Log.e("LAMBDA", "AWS Lambda Function Error: " + functionError);
}
if (logResult != null) {
Log.d("LAMBDA", "AWS Lambda Log Result: " + logResult);
}
}
catch (final Exception e) {
Log.e("LAMBDA", "Unable to decode results. " + e.getMessage(), e);
//showError(e.getMessage());
}
}
}.execute();
return events;
}
The problem is that I call invokeFunction in diffrent activity and it returns null but in onPostExecute the array is not null. It seems that it returns array before calling OnPostExecute. How to solve that?
The problem is that the method invokeFunction is finishing before onPostExecute (asynchronous)
You could use an interface to communicate AsyncTask and activity.
Interface (pseudocode):
public interface AsyncCom {
public void sendUsers(User [] events);
}
Your asynFunction (pseucode):
public void invokeFunction(String funName, String requestContent, AsyncCom listener) {
...
And call the function of the listener in postExecute (pseudocode):
protected void onPostExecute(final InvokeResult invokeResult) {
...
listener.sendUsers(events);
}
Declare the interface in your activity and call your method with the listener (pseudocode):
public class MyActivity implements AsyncCom {
...
invokeFunction(funName, requestContent, this);
...
Finally, in your activity, implements the returned method (pseudocode):
public void sendUsers(User [] events){
// do wathever you want with users
}
But remeber that the response will bw asynchonous
private void invokeFunction(String funName, String requestContent{
YourTask task = new YourTask();
task.execute(new String[]{funName, requestContent});
}
static class YourTask extends AsyncTask<String, Void, InvokeResult> {
#Override
protected InvokeResult doInBackground(String... params) {
String funName = params[0];
String requestContent = params[1];
// ...
}
#Override
protected void onPostExecute(final InvokeResult invokeResult) {
/// . ..
doWhatYouNeedWithTheResult(result);
}
};
}
Related
textView = findViewById(R.id.textVieww);
String url = "https://zenquotes.io/api/random";
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
final String myResponse = response.body().string();
try {
JSONArray jsonarray = new JSONArray(myResponse);
for(int i=0; i<jsonarray.length(); i++) {
JSONObject obj = jsonarray.getJSONObject(i);
}
} catch (JSONException e) {
e.printStackTrace();
}
Quote.this.runOnUiThread(() ->
textView.setText(myResponse));
}
}
});
}
This is the part im stuck on i think im on the right track but not sure where to go from here im trying to get the "q" information from the returned url and the "a" information but it just outputs everything any suggestions?
What was your problem
Even when you parsed JSON string, you were still using the myResponse string in your textView.setText() method.
Continuing your code snippet
your code snippet is quite short, but i do think i can quite understand what you mean.
So let's say that we have Activity, which is called MainActivity and in that activity we have two views, one TextView called that has an id of tv_author_and_quote and one Button which has a xml id btn_request_quote.
The button has an OnClickListener which calls method requestForQuote().
Our onCreate + the variables of Button and TextView looks like this:
TextView tvAuthorAndQuote;
Button btnRequestQuote;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvAuthorAndQuote = findViewById(R.id.tv_author_and_quote);
btnRequestQuote = findViewById(R.id.btn_request_quote);
btnRequestQuote.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
requestForQuote();
}
});
}
And then we have a code itself for method requestForQuote():
public void requestForQuote() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(URL)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
if (response.isSuccessful()) {
final String myResponse = Objects.requireNonNull(response.body()).string();
String myFormattedQuote = "";
try {
JSONArray jsonarray = new JSONArray(myResponse);
for(int i=0; i<jsonarray.length(); i++) {
JSONObject obj = jsonarray.getJSONObject(i);
String quote = obj.getString("q");
String author = obj.getString("a");
Log.d(TAG, "onResponse: quote:" + quote);
Log.d(TAG, "onResponse: author:" + author);
myFormattedQuote = author + ": " + quote;
}
} catch (JSONException e) {
e.printStackTrace();
}
final String myFinalQuote = myFormattedQuote;
MainActivity.this.runOnUiThread(() -> {
if (!myFinalQuote.equals("")) {
tvAuthorAndQuote.setText(myFinalQuote);
} else {
tvAuthorAndQuote.setText(myResponse);
}
});
}
}
});
}
The code above basically uses your existing solution, but instead of setting the text of textView with myResponse string, it parses the json array and gets a quote and an author from it. Then it just logs it (just for testing purposes), then it constructs the string which gets displayed to the if there is any, otherwise it prints the response. That it is.
Using Gson library
import it into your gradle dependecies
implementation 'com.google.code.gson:gson:2.8.7'
Write short "holder" class called Quote
public class Quote {
public Quote() {
}
String q;
String a;
String h;
public String getQ() {
return q;
}
public void setQ(String q) {
this.q = q;
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getH() {
return h;
}
public void setH(String h) {
this.h = h;
}
#NonNull
#NotNull
#Override
public String toString() {
return a + ": " + q;
}
}
Then the requestForQuote() method could look something like this:
public void requestForQuoteWithGson() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(URL)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
if (response.isSuccessful()) {
final String myResponse = Objects.requireNonNull(response.body()).string();
Type listType = new TypeToken<ArrayList<Quote>>(){}.getType();
List<Quote> yourClassList = new Gson().fromJson(myResponse, listType);
if (yourClassList != null && yourClassList.size() > 0) {
final Quote quote = yourClassList.get(0);
if (quote != null) {
myQuotes.add(quote);
MainActivity.this.runOnUiThread(() ->
tvAuthorAndQuote.setText(quote.toString())
);
}
}
}
}
});
}
So I have a URL within Method1 like so
public void Method1 (String x) {
String Url = "http://MYURL.com/?country=" + x + "&api_key=APIKEY";
new AsyncTaskParseJson().execute();
}
I need to pass the Url into my AsyncTask which is as follows
public class AsyncTaskParseJson extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {}
#Override
protected String doInBackground(String... arg0) {
try {
// create new instance of the httpConnect class
httpConnect jParser = new httpConnect();
// get json string from service url
String json = jParser.getJSONFromUrl(ServiceUrl);
// save returned json to your test string
jsonTest = json.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String strFromDoInBg) {
textLastLocation = (TextView) findViewById(R.id.lastlocation);
textLastLocation.setText(jsonTest);
}
}
I need it so the ServiceUrl = the Url from the method. I can't figure out how to do this even from looking at other peoples questions and answers
The first parameter on AsyncTask<First, Second, Third> will define the parameter to be passed on execute(), so you define it as String and pass the url. Then:
public void Method1 (String x) {
String Url = "http://MYURL.com/?country=" + x + "&api_key=APIKEY";
new AsyncTaskParseJson().execute(url);
}
On your AsyncTask, you can get it on the arg0 (array), i(ndex based on the order on how you passed it on execute())
public class AsyncTaskParseJson extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {}
#Override
protected String doInBackground(String... arg0) {
String url = arg0[0]; // this is your passed url
try {
// create new instance of the httpConnect class
httpConnect jParser = new httpConnect();
// get json string from service url
String json = jParser.getJSONFromUrl(ServiceUrl);
// save returned json to your test string
jsonTest = json.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String strFromDoInBg) {
textLastLocation = (TextView) findViewById(R.id.lastlocation);
textLastLocation.setText(jsonTest);
}
}
I know there are some identical questions but I just couldn't figure out what I'm doing wrong.
public class MainActivity extends AppCompatActivity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
...
new JsonHandler().execute(this, collection, gridArray, customGridAdapter);
...
}
}
So in my main activity I need to query an API which gives back JSON and I have to process that to build my database.
Then in doInBackground() I call getAllCards() which gets the first JSON. Because the JSON includes URLs for more JSON requests, I have a few methods each querying a more detailed JSON.
public final class JsonHandler extends AsyncTask {
private final String urlCards = "https://api.gwentapi.com/v0/cards/";
private final String urlSpecificCard = "https://api.gwentapi.com/v0/cards/:id";
private Context context;
private Collection collection;
private ArrayList<Card> gridArray;
private CustomGridViewAdapter customGridAdapter;
public JsonHandler(Context context, Collection collection, ArrayList<Card> gridArray, CustomGridViewAdapter customGridAdapter){
this.context = context;
this.collection = collection;
this.gridArray = gridArray;
this.customGridAdapter = customGridAdapter;
}
public JsonHandler(){
this.context = null;
this.collection = null;
this.gridArray = null;
this.customGridAdapter = null;
}
private void getAllCards() throws RuntimeException {
JsonObjectRequest arrayRequest = new JsonObjectRequest(Request.Method.GET, urlCards, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
generateCollection(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError e) {
throw new RuntimeException(e.getMessage());
}
});
Volley.newRequestQueue(context).add(arrayRequest);
}
private void getSpecificCard(final String cardURL) throws RuntimeException {
JsonObjectRequest arrayRequest = new JsonObjectRequest(Request.Method.GET, cardURL, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
processCard(response, collection);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError e) {
throw new RuntimeException(e.getMessage());
}
});
Volley.newRequestQueue(context).add(arrayRequest);
}
private void generateCollection(JSONObject response) throws RuntimeException {
try {
JSONArray array = response.getJSONArray("results");
for(int i = 0; i < array.length();i++){
JSONObject object = array.getJSONObject(i);
String cardURL = object.getString("href");
getSpecificCard(cardURL);
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
private void processCard(JSONObject response, Collection collection){
try {
String id = response.getString("id");
EnumFaction faction = EnumFaction.valueOf(response.getJSONObject("faction").getString("name").toUpperCase());
EnumType type = null;
EnumRarity rarity = null;
EnumLane lane = null;
EnumLoyalty loyalty = null;
String name = response.getString("name");
String text = response.getString("text");
String imagePath = "https://api.gwentapi.com/media/\" + id + \"_small.png";
URL url = new URL(imagePath);
InputStream inputStream = url.openConnection().getInputStream();
Bitmap image = BitmapFactory.decodeStream(inputStream);
Card card = new Card(id, faction, type, rarity, lane, loyalty, name, text, null, imagePath, 0);
collection.addCard(card);
gridArray.add(card);
customGridAdapter.notifyDataSetChanged();
} catch (Exception e){
throw new RuntimeException(e.getMessage());
}
}
#Override
protected Object doInBackground(Object[] params) {
context = (Context) params[0];
collection = (Collection) params[1];
gridArray = (ArrayList<Card>) params[2];
customGridAdapter = (CustomGridViewAdapter) params[3];
getAllCards();
return null;
}
}
So now on to the problem:
When the programm reaches processCard() when I've gathered enough information, I get a NetworkOnMainThreadException when I create the InputStream.
I've tried so many different methods to get a Bitmap from my URL and different methods to do an asynchronous task - all leading to the same result.
If you could show me how to resolve this issue, I'd be sooo happy.
Edit: Since it got marked as duplicate: I AM USING ASYNCTASK! I have looked at many questions and tried what they did there, it doesn't work!
Not really familiar with how volley works but onResponse but be on the main thread so you need to start a new thread to make that call too
I have this function called facebookCheckEmail() (below) inside a container class, and I want to change a variable value (fbresponse) of the container class inside public void onResponse(FbCheckObject fbCheckObject) {} of facebookCheckEmail(), how do I do that ?
private void facebookCheckEmail( String email ) {
fbCheckEmail_params = new HashMap<String, String>();
fbCheckEmail_params.put("Email", email);
final GsonRequest<FbCheckObject> gsonRequest = ApiRequest.gsonFacebookCheckEmailRequest
(
new Response.Listener<FbCheckObject>() {
#Override
public void onResponse(FbCheckObject fbCheckObject) {
try {
System.out.println("Status = " + fbCheckObject.getStatus());
if (new String(fbCheckObject.getStatus()).equals("ok") == true) {
fbresponse = "ok";
} else if(new String(fbCheckObject.getStatus()).equals("no") == true){
fbresponse = "no";
}
} catch (Exception e) {
Log.d("Web Service Error", e.getMessage());
e.printStackTrace();
}
}
}
,
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// Deal with the error here
}
},
fbCheckEmail_params
);
AppController.getInstance().addToRequestQueue(gsonRequest, TAG);
}
my container class is :
public class MainLoginActivity extends Activity {
public String fbresponse = "";
#Override
protected void onCreate(Bundle savedInstanceState) {
.......
}
..........
private void facebookCheckEmail( String email ) {
......
}
}
NOTE: Inside the "#Override public void onResponse(FbCheckObject fbCheckObject) {}", fbresponse value is successfully assigned to "ok" or "no" if unsuccessful, but anywhere outside that scope if I System.out.printIn fbresponse, I will get "null"
Doing what you want is not a good design. You should rather change your method so that it returns the status:
private String facebookCheckEmail( String email ) {
...
}
And set the return value to the response:
fbresponse = facebookCheckEmail(email);
The following code creates a background task and executes it.
String dateString = null;
if (dateSelected)
dateString = Utils.parseDateToMsTimestamp(selectedDate);
final String ori = originCode;
final String dest = destinationCode;
RequestScheduleTask requestScheduleTask = new RequestScheduleTask();
requestScheduleTask
.execute(ori, dest, dateString);
originCode and destinationCode are instance variables.
The following is what the background task does.
private class RequestScheduleTask extends
AsyncTask<String, Void, List<CUSchedule>> {
#Override
protected List<CUSchedule> doInBackground(String... args) {
List<CUSchedule> cuSchedules = null;
try {
cuSchedules = CURestCommunicator
.requestSUScheduleByOriginAndDestination(args[0],
args[1], args[2]);
} catch (NetworkException e) {
}
return cuSchedules;
}
#Override
protected void onPostExecute(List<CUSchedule> result) {
if (result == null) {
raiseError("Server Error");
}
InnoBusApplication innoBusApplication = (InnoBusApplication) getApplication();
innoBusApplication.setCuSchedules(result);
super.onPostExecute(result);
}
}
The following is part of what the http call does.
public static List<CUSchedule> requestSUScheduleByOriginAndDestination (
String origin, String destination, String date) throws NetworkException {
Log.d("upload", "up");
origin = Utils.shortNameForCity(origin);
destination = Utils.shortNameForCity(destination);
HttpClient client = null;
String url = "http://" + SVR + "/innobussvr/BusSchedulesSearchByOrgDestStartTimeEndTime/"
+ origin + "/" + destination;
Log.d("url", url);
...
}
The following is the URL that results.
http://192.168.0.150/innobussvr/BusSchedulesSearchByOrgDestStartTimeEndTime/null/null
I understand that this is a thread visibility problem. How can I solve it?
Simple Solution just create a Constractor for RequestScheduleTask that takes 2 String and pass them then create inside the Task class 2 local string var.