I'm trying to get PhotoUrl in this method.
private String getUserPhotoUrl(String vk_id){
final String[] url = new String[1];
VKRequest request = VKApi.users().get(VKParameters.from(VKApiConst.USER_ID, vk_id,
VKApiConst.FIELDS, "photo_100"));
request.executeWithListener(new VKRequest.VKRequestListener() {
#Override
public void onComplete(final VKResponse response) {
super.onComplete(response);
new Thread(){
#Override
public void run() {
VKList<VKApiUser> User = (VKList<VKApiUser>) response.parsedModel;
url[0] = User.get(0).photo_100;
Log.i("PhotoUrl", url[0]); //working perfect
}}.start();
}});
return url[0];
}
In the Log.i("PhotoUrl", url[0]); it gives not null. I mean normal url. But when I try to return it in return url[0]; part, it gives me null. Any ideas?
Because of concurrency. Your method immediately returns without waiting for any other thread.
The return statement is outside the listener, and thefore returns befofre listener end processing.
So it returns a null
Related
I am using the Facebook graph api to find out what pages a user is apart of. When the query comes back with a json object it has what I need but, for some reason it doesn't want to add to my array list. The correct value is printed in log.d it seems to skip my arraylist for some reason. Any ideas?
Find page function
private ArrayList<String> foundPages;
private JSONObject jsonObject;
public ArrayList<String> findPages()
{
accessToken = AccessToken.getCurrentAccessToken();
foundPages = new ArrayList<>();
GraphRequest request = GraphRequest.newGraphPathRequest(
accessToken,
"/me/accounts",
new GraphRequest.Callback() {
#Override
public void onCompleted(GraphResponse response) {
try {
jsonObject = response.getJSONObject();
for(int i=0; i < jsonObject.getJSONArray("data").length(); i++)
{
page = response.getJSONObject().getJSONArray("data").getJSONObject(i).getString("name");
Log.d("viewmodel",page);
foundPages.add(page);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
});
request.executeAsync();
return foundPages;
}
There is a common way to solve this problem, which is to define a callback method which will return these values to you, AFTER they have been populated by the call, which goes something like this (my java is rusty, bear with me...)
define an interface :
interface Callback{
void apiResponseCallback(ArrayList<Page> result);//whatever your model is, make the array of that type
}
then, in your normal findPages method, change it to this:
public void findPages(Callback callback) {
//
//
........
for(int i=0; i < jsonObject.getJSONArray("data").length(); i++)
{
page = response.getJSONObject().getJSONArray("data").getJSONObject(i).getString("name");
Log.d("viewmodel",page);
foundPages.add(page);
}
callback.apiResponseCallback(foundPages);//here we are returning the data when it is done
}
then, when you call findPages
findPages(new Callback() {
#Override
public void apiResponseCallback(ArrayList<Page> result) {
here, this result parameter that comes through is your api call result to use, so result will be your populated pages to use.
}
});
}
sake of completeness:
public void findPages(Callback callback)
{
accessToken = AccessToken.getCurrentAccessToken();
foundPages = new ArrayList<>();
GraphRequest request = GraphRequest.newGraphPathRequest(
accessToken,
"/me/accounts",
new GraphRequest.Callback() {
#Override
public void onCompleted(GraphResponse response) {
try {
jsonObject = response.getJSONObject();
for(int i=0; i < jsonObject.getJSONArray("data").length(); i++)
{
page = response.getJSONObject().getJSONArray("data").getJSONObject(i).getString("name");
Log.d("viewmodel",page);
foundPages.add(page);
}
callback.apiResponseCallback(foundPages);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
request.executeAsync();
}
Yep. This here:
request.executeAsync();
triggers an asynchronous request. But your "current" thread simply continues to do:
return foundPages;
and it returns an empty list.
That list gets later filled, but at the moment in time when that method returns, that list is still empty. Or just gets filled. Who knows, as it gets filled asynchronously, at some unknown point in the future.
A solution could be to have some other variable/field that tells you the data has arrived and pushed into the list.
Alternatively, that method could just make a synchronous request, simply block the caller from progressing until the data has arrived.
You see, you can't have it both ways: when you don't wait for your results to arrive, you shouldn't expect them to be available immediately.
public class MainActivity extends AppCompatActivity {
public static final int TRANSMIT_DATA = 1;
public static String string0;
public String temp;//定义全局变量,想要把string0的值传给它。
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadData();
System.out.println("Main output:ID="+Thread.currentThread().getId());
anotherThread();
}
public void anotherThread(){
new Thread(){
public void run() {
System.out.println("anotherThread :ID="+Thread.currentThread().getId());
System.out.println("anotherThread output: Content="+temp);
}
}.start(); //开启一个线程
}
private Handler dataHandler =new Handler(){
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TRANSMIT_DATA:
System.out.println("handleMessage output:ID="+Thread.currentThread().getId());
System.out.println("handleMessage output: Content="+msg.obj);
temp=msg.obj.toString();
break;
default:
break;
}
}
};
public void loadData() {
OkHttpClient okHttpClient = new OkHttpClient();
//构造Request,
//builder.get()代表的是get请求,url方法里面放的参数是一个网络地址
Request.Builder builder = new Request.Builder();
final Map params = new LinkedHashMap();// 请求参数
Request request = builder.get()
.url("https://api.avatardata.cn/Jztk/Query?key=15f9ceafeeb94a2492fd84b8c68a554c&subject=4&model=c1&testType=rand")
.build();
//3将Request封装成call
Call call = okHttpClient.newCall(request);
//4,执行call,这个方法是异步请求数据
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
//失败调用
Log.e("MainActivity", "onFailure: " );
}
#Override
public void onResponse(Call call, final Response response) throws IOException {
//成功调用
Log.e("MainActivity", "onResponse: " );
//获取网络访问返回的字符串
string0 = response.body().string();
System.out.println("Asynchronous Request Output:ID="+Thread.currentThread().getId());
Message message = new Message();
message.obj = string0;
message.what =TRANSMIT_DATA;
dataHandler.sendMessage(message);
}
});
}
}
The picture is about System.out.println
Just like the picture show above: the "anotherThread output: Content=null", I want to Pass information from the main thread to the child thread (in the run method), how can I do it? Try to avoid changing the code of other methods as soon as possible.
Given that you want minimal code changes you could use ThreadLocal , value set in Parent threads ThreadLocal is available for all child threads
I think your "otherthread" starts and ends before the data is available in the temp variable, hence it prints null.
You can do something like:
a. Either you start/run your "otherthread" after you fill the temp variable in the handleMessage function.
b. Or if you insist on starting the "otherthread" before you have the data, have the thread in a synchronized way, check the temp variable for being non null after some interval. Also have some sortof boolean to let the thread know, to exit incase you didnt receive any data.
my 2 cents
I am trying to write to a local variable inside a JSONObjectRequest. Here is my code:
JsonObjectRequest get_id_request = new JsonObjectRequest(Request.Method.GET, URL_ID, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
boolean load_full_data = false;
try {
JSONObject jsonNotificationID = response.getJSONObject("n");
int notificationID = jsonNotificationID.getInt("id");
// Change flag to get full preferences below
if(notificationID > currentNotificationID) {
load_full_data = true;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onErrorResponse(VolleyError error) {
}
});
I want to be able to check the server, if there are new IDs (different to the ones already stored in shared Preferences), then download the new ones. So to do this, I want to set my variable load_full_data = true, then further down (oustide this request):
// Get the IDs, see if they are different.
volleyQueue.add(get_id_request);
if(load_full_data) {
Log.d(TAG, "run: Load Full Data");
volleyQueue.add(get_full_request);
}
Only thing is, I cant reference a local variable inside my JSONObjectRequest. It says it needs to final. How can I pass data in and out of this?
You could create a new class and let it implement the interface whose definition you would like to enrich, in your case Response.Listener<JSONObject>
I am not familiar with this API but an example code would be like:
class MyResponseListener implements Response.Listener<JSONObject> {
boolean isGoodParam;
MyResponseListener(boolean isGoodParam) {
this.isGoodParam = isGoodParam;
}
public isGoodParam() {
return this.isGoodParam;
}
#Override
public void onResponse(JSONObject response) {
//use your param
if(this.isGoodParam) {
doStuff();
}
}
}
then your client code would be:
boolean initialIsGood = true;
MyResponseListener listener = new MyResponseListener(initialIsGood);
JsonObjectRequest getIdRequest = new JsonObjectRequest(Request.Method.GET, URL_ID, null, listener,
Response.ErrorListener { error ->
// TODO: Handle error
});
//outside of the listener, assuming that the status of the boolean changed and you want to find out the new value
boolean newValue = listener.isGoodParam();
Cosmetic note: Please stick to the code convention standards, makes the code more readable. (for example camelCases and no_snakes :)
I have been working with the YouTube API v3 with Java and have encountered a problem.
When trying to set a variable in an array I am getting a NullPointerException. It seems completely random and impossible. I am getting this on this line:
full[0] = snippet.getDisplayMessage().toLowerCase();
in this method:
private static void listChatMessages(
final String liveChatId,
final String nextPageToken,
long delayMs) {
System.out.println(
String.format("Getting chat messages in %1$.3f seconds...", delayMs * 0.001));
Timer pollTimer = new Timer();
pollTimer.schedule(
new TimerTask() {
#Override
public void run() {
try {
// Get chat messages from YouTube
LiveChatMessageListResponse response = youtube
.liveChatMessages()
.list(liveChatId, "snippet, authorDetails")
.setPageToken(nextPageToken)
.setFields(LIVE_CHAT_FIELDS)
.execute();
// Display messages and super chat details
List<LiveChatMessage> messages = response.getItems();
for (int i = 0; i < messages.size(); i++) {
LiveChatMessage message = messages.get(i);
LiveChatMessageSnippet snippet = message.getSnippet();
final String[] full = new String[2];
full[0] = snippet.getDisplayMessage().toLowerCase();
full[1] = message.getId();
ExecutorService es = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
es.submit(new Runnable() {
public void run() {
PressKey.main(full);
}
});
}
// Request the next page of messages
listChatMessages(
liveChatId,
response.getNextPageToken(),
response.getPollingIntervalMillis());
} catch (Throwable t) {
System.err.println("Throwable: " + t.getMessage());
t.printStackTrace();
}
}
}, delayMs);
}
}
I am not entirely sure how the API is implemented, but this NPE happens because the value of
snippet.getDisplayMessage()
Is null, and calling .toLowerCase() on null results in the NPE.
Like I said, I do not know for sure how the API and the container classes are implemented, but it may seem like getDisplayMessage() is not always guaranteed to return any content.
You could fix your NPE by checking for null values and supplying a default value instead:
full[0] = snippet.getDisplayMessage() == null ? "" : snippet.getDisplayMessage().toLowerCase();
Here we assign an empty string to full[0] if getDisplayMessage() is null.
i've done a rest web service that gives me some contact information like numbers, age ... i get all this information in this function
public static void getRest(String search) {
if(search.equals("")){
json="http://localhost:8080/com.vogella.jersey.first/rest/jsonServices/print/";
} else {
json="http://localhost:8080/com.vogella.jersey.first/rest/jsonServices/print/"+search;
}
ConnectionRequest req = new ConnectionRequest() {
#Override
protected void postResponse() {
}
#Override
protected void readResponse(InputStream input) throws IOException {
JSONParser p = new JSONParser();
Map<String, Object> h = p.parseJSON(new InputStreamReader(input));
ArrayList object=new ArrayList();
for (Entry<String, Object> entry : h.entrySet()) {
object = (ArrayList) entry.getValue();
int i=object.size();
}
for(int i=0; i<object.size();i++){
LinkedHashMap s= (LinkedHashMap) object.get(i);
Risultati.add(s);
}
}
};
req.setUrl(json);
req.setPost(false);
req.addRequestHeader("Accept", "application/json");
InfiniteProgress prog = new InfiniteProgress();
Dialog dlg = prog.showInifiniteBlocking();
req.setDisposeOnCompletion(dlg);
NetworkManager.getInstance().addToQueue(req);
Risultati is an attribute of the class: ArrayList<LinkedHashMap> Risultati;
the problem is that when i call the function getRest("") in this way:
getRest("");
Label contatto=null;
for(int j=0;j<Risultati.size();j++){
LinkedHashMap s=Risultati.get(j);
String nome=(String) s.get("firstName");
String cognome=(String) s.get("lastName");
String numero =(String) s.get("numero");
contatto=new Label(nome+" "+cognome+" "+numero);
}
hi.addComponent(contatto);
it turns that Risultati is null, if i comment the for cycle i notice that the inner function readResponse is executed after...i don't know what i'm doing wrong
I think the point is that you're calling NetworkManager.getInstance().addToQueue(req). According to it's documentation, it will add a connection request (the one you've just created) to a queue. After the connection request is added to the queue, it returns, meaning the request may or may not have been executed by that time.
You have to options to deal with this. In my opinion, the best way would be to update the user interface after the request has completed, as described in the "File System, Storage, Network & Parsing" chapter of the CodeOne manual:
req.addResponseListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
NetworkEvent e = (NetworkEvent)ev;
// ... process the response
}
});
Alternatively, you could replace the call to addToQueue(req) with addToQueueAndWait(req). The latter method waits until the request is processed in the queue. The downside of the latter approach is that your user interface may freeze while the request is being processed, because the UI thread is blocked on the network I/O.