In a service I'm populating an ArrayList, which I then return to the calling activity:
In service (here, resultArrayList contains items and is of class ArrayList<MyObjs>):
public class DataFetchService extends BaseService {
#Override
protected void onHandleIntent(final Intent intent) {
super.onHandleIntent(intent);
// Do some work here that populates resultArrayList...
final Bundle bundle = new Bundle();
bundle.putSerializable(BaseService.RESULT_OBJ, resultArrayList);
message.setData(bundle);
try {
final Messenger messenger = startIntent.getParcelableExtra(BaseService.PARAM_MESSENGER);
messenger.send(message);
} catch (RemoteException e) {
L.p("Help!");
}
From BaseService:
public class BaseService extends IntentService {
protected ArrayList<MyObjs> resultArrayList = new ArrayList<>();
// Yada yada...
In the activity's handleMessage():
#Override
public boolean handleMessage(final Message msg) {
final Bundle bundle = msg.getData();
#SuppressWarnings("unchecked")
final ArrayList<MyObjs> nodes = (ArrayList<MyObjs>) bundle.getSerializable(BaseService.RESULT_OBJ);
if (nodes == null) {
//App never enters this
return
}
if (nodes.size() == 0) {
// Always enters here!
// If I set a breakpoint here, the IDE tells me nodes size is 1
}
The weird thing is that if I set a breakpoint inside the if (nodes.size == 0) { code, the IDE shows that nodes does contain items (says size = 1 and I can expand it and see the variables), even though it enters that.
Any idea what could be the issue? Could this be a race condition between other services sending data back to handleMessage()?
I'm uncertain about the causes of this error, but creating a new ArrayList<> before sending from the service seems to fix it.
So the following makes it work:
final Bundle bundle = new Bundle();
// Created this new ArrayList
final ArrayList<MyObjs> newArrayList = new ArrayList<>();
// Add items to the new ArrayList
newArrayList.addAll(resultArrayList);
// Send the new ArrayList and NOT the other one.
bundle.putSerializable(BaseService.RESULT_OBJ, newArrayList);
message.setData(bundle);
try {
final Messenger messenger = startIntent.getParcelableExtra(BaseService.PARAM_MESSENGER);
messenger.send(message);
} catch (RemoteException e) {
L.p("Help!");
}
Related
This is how I log firebase event in my app
public static void trackGAEvent(String name, String screenName) {
FirebaseAnalytics firebaseAnalytics = Utils.getFirebaseAnalytics();
if (firebaseAnalytics == null) {
return;
}
Bundle bundle = null;
if (screenName != null) {
bundle = new Bundle();
bundle.putString(FirebaseAnalytics.Param.SCREEN_NAME, screenName);
}
firebaseAnalytics.logEvent(name, bundle);
}
trackEvent("review_ok", "screen_name_A");
trackEvent("review_ok", "screen_name_B");
By using custom definition, I expect I can break down the event review_ok to screen_name_A and screen_name_B.
However, when I look at the even of review_ok, the parameter name selection is empty.
Do you know what else I have missed out?
I'm new to android programming and I'm having a problem with my codes. Can anyone help me or point out the cause of my error because I'm not really sure why it's giving me a NullPointerException when its a text view or if that is possible.
LogCat:
java.lang.NullPointerException: Attempt to invoke virtual method 'double java.lang.Double.doubleValue()' on a null object reference
at com.example.app.rosbridge.MainActivity$2$1.run(MainActivity.java:133)
Here is the code for that line:
current.setText(String.format("%.4f%s", batteryStateData.msg.current * Math.pow(10, 6), "A"));
But when i run my app my voltage is setting null and here is the code for the voltage:
voltage.setText(String.format("%.4f%s", batteryStateData.msg.voltage, "v"));
Here is the full code:
public class MainActivity extends Activity {
private TextView voltage, current, percentage, status;
private SubscribedData<BatteryState> batteryStateData;
private RosbridgeListener rosbridge;
private boolean subscribed = false;
private boolean advertised = false;
/** Indicates that Lint should ignore the specified warnings for the annotated element. */
#SuppressLint("ClickableViewAccessibility")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_activity);
Button settings_btn = (Button) findViewById(R.id.connect_btn);
voltage = findViewById(R.id.voltage_txt);
current = findViewById(R.id.current_txt);
percentage = findViewById(R.id.percentage_txt);
status = findViewById(R.id.status_txt);
connectButton = findViewById(R.id.connect_btn);
batteryStateData = new SubscribedData<>();
final Type batteryStateType = new TypeToken<SubscribedData<BatteryState>>() {
}.getType();
// ROSBRIDGE protocol allows access to underlying ROS messages and services as serialized JavaScript Object Notation (JSON) objects
WebSocket protocol communicates to a server for the connection from a user's web browser
//A connection to the rosbridge thru the IP address of the robot from the socket
rosbridge = new RosbridgeListener("ws://10.24.204.231:9090");
rosbridge.setOnDataReceivedListener(new RosbridgeMessageListener() {
// a running thread that when the connection is made the data of the topic will serialize and deserialized java objects to (and from) JSON. #param msg
#Override
public void onDataReceived(final String msg) {
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
batteryStateData = new Gson().fromJson(msg, batteryStateType);
voltage.setText(String.format("%.4f%s", batteryStateData.msg.voltage, "v"));
current.setText(String.format("%.4f%s", batteryStateData.msg.current * Math.pow(10, 6), "A"));
percentage.setText(String.format("%.2f%s", batteryStateData.msg.percentage, "%"));
status.setText(String.format("%s", PowerSupplyStatus.values()[batteryStateData.msg.powerSupplyStatus]));
}
});
Log.d("B9T", String.format("Received data: %s", msg));
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
connectButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (!subscribed) {
rosbridge.Subscribe("/battery", "sensor_msgs/BatteryState");
subscribed = true;
connectButton.setText("Disconnect");
} else {
rosbridge.UnSubscribe("/battery");
subscribed = false;
connectButton.setText("Connect");
}
}
});
Everytime when you are setting the text to textview, you must need to check if it is not null.
You can check null before you set text on textview like the following.
#Override
public void run() {
batteryStateData = new Gson().fromJson(msg, batteryStateType);
// check null before set text or calculate something
if(batteryStateData.msg.current != null){
current.setText(String.format("%.4f%s", batteryStateData.msg.current * Math.pow(10, 6), "A"));
}
// you can check belows like above
voltage.setText(String.format("%.4f%s", batteryStateData.msg.voltage, "v"));
percentage.setText(String.format("%.2f%s", batteryStateData.msg.percentage, "%"));
status.setText(String.format("%s", PowerSupplyStatus.values()[batteryStateData.msg.powerSupplyStatus]));
}
Check weather your created class with GSON is not null
and then check class fields values is not null or 0.
batteryStateData = new Gson().fromJson(msg, batteryStateType);
if (batteryStateData != null) {
if (batteryStateData.msg.voltage!=0)
voltage.setText(String.format("%.4f%s", batteryStateData.msg.voltage, "v"));
if (batteryStateData.msg.current!=0)
current.setText(String.format("%.4f%s", batteryStateData.msg.current * Math.pow(10, 6), "A"));
if (batteryStateData.msg.percentage!=0)
percentage.setText(String.format("%.2f%s", batteryStateData.msg.percentage, "%"));
if (batteryStateData.msg.values !=null)
status.setText(String.format("%s", PowerSupplyStatus.values()[batteryStateData.msg.powerSupplyStatus]));
}
So Azure spit the following code for me to insert into an activity (Android Studio is what I'm using)
Add the following line to the top of the .java file containing your launcher activity:
import com.microsoft.windowsazure.mobileservices.*;
Inside your activity, add a private variable
private MobileServiceClient mClient;
Add the following code the onCreate method of the activity:
mClient = new MobileServiceClient("https://pbbingo.azurewebsites.net", this);
Add a sample item class to your project::
public class ToDoItem{ public String id; public String Text;}
In the same activity where you defined mClient, add the following code:
ToDoItem item = new ToDoItem();
item.Text = "Don't text and drive";
mClient.getTable(ToDoItem.class).insert(item, new TableOperationCallback<item>(){
public void onCompleted(ToDoItem entity, Exception exception, ServiceFilter response)
{
if(exception == null){
//Insert Succeeded
} else {
//Insert Failed
}
}});
My goal is to create a login page. I understand that the above was probably offered up more with a ToList in mind. I just want to get the syntax correct today. The problem I think, is my basic class structure. I have created an OnClick Listener within my on create that gets the ID from a button in my layout. I don't need it checking for anything in the database until the button has been actually clicked to either login or register.
public class LoginClass extends AppCompatActivity{
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.MyLoginLayout);
MobileServiceClient mClient = null;
try {
mClient = new MobileServiceClient ("myAzureWebsite", "AzureKey", this);
} catch (MalformedURLException e) {
e.printStackTrace();
}
Button Attempt = (Button) findViewById (R.id.mySubmitButton);
final MobileServiceClient finalMClient = mClient; // finalized so I can use it later.
Attempt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick (View v) {
final View thisView = v;
final MyToDoItemClass item = new MyToDoItemClass();
In MyToDoItemClass I have two variables (Both String) Just left over from
the example of a ToDoList (they are String ID and String Text)
item.Text = "Filler";
item.ID = "Fill";
finalMClient.getTable(MyToDoItemClass.class).insert(new Table OperationCallback<item>() { //<--- I'm getting an error that the variable, item
is from an unknown class...
public void onCompleted (Item entity, Exception exception, ServiceFilterResponse response){
if(exception == null) {
Intent i = new Intent (LoginClass.this, MainActivity.class);
startActivity(i);
}else{
Toast.makeText(thisView.getContext(), "Failed", Toast.LENGTH_LONG).show();
}}
});
}
});
}}
The problem is with that the TableOperationCallback is saying that the item from MyToDoItemClass class is from an unknown class.
There are many issues in your code, as below.
According to the javadoc for class MobileServiceClient, there is not a method insert(TableOperationCallback<E> callback), so the code finalMClient.getTable(MyToDoItemClass.class).insert(new Table OperationCallback<item>() {...} is invalid.
The generics E in Table OperationCallback<E> means that you need to write a POJO class name instead of E, not an object variable name like item, so the correct code should be new Table OperationCallback<MyToDoItemClass>, please see the Oracle tutorial for Generics to know more details.
The figure below shows all methods insert of class MobileServiceClient. The bold word Deprecated under the method name means that you should not use it for developing on new project, it‘s only compatible for old project on the new version of Java SDK.
Please follow the offical tutorial to develop your app. Any concern, please feel free to let me know.
I'm trying to retrieve some data from API, but i'm always getting null in async task. Here is my asynctask:
private class DownloadTask extends AsyncTask<Bundle, Void, List<Topic>> {
#Override
protected void onPreExecute() {
HomeActivity.mProgressBar.setVisibility(View.VISIBLE);
HomeActivity.mProgressBar.setIndeterminate(true);
}
#Override
protected List<Topic> doInBackground(Bundle... params) {
return downloadPhotos(params[0]);
}
#Override
protected void onPostExecute(List<Topic> topics) {
HomeActivity.mProgressBar.setVisibility(View.INVISIBLE);
HomeActivity.mProgressBar.setIndeterminate(false);
Log.d("List Size: ", ""+topics); // 0
adapter = new TopicListAdapter(activity, topics);
RecyclerView.LayoutManager manager = new MyCustomLayoutManager(activity);
recyclerView.setLayoutManager(manager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(adapter);
}
}
Method for retrieving data should merge two arrays into one array because i'm retrieving data from two places:
private List<Topic> downloadPhotos(Bundle params) {
String profileId = activity.getPreferencesManager().getProfileId();
List<Topic> topicsFromMe, topicsFromFriends;
topicsFromFriends = setValuesFromFriends(params);
topicsFromMe = setValuesFromMe(profileId, params);
topicsFromFriends.addAll(topicsFromMe);
sortTopics(topicsFromFriends);
int k = topicsFromFriends.size();
Log.d("List Size: ", "" + topicsFromFriends); // here also 0 for size
if (k > 10)
topicsFromFriends.subList(10, k).clear();
return topicsFromFriends;
}
And here is one method where i'm setting values to array list. It is strange that RecyclerView in this case is populated with this array, but i'm not getting results i want. For instance i should sort this list and show only 10 records from it.
private List<Topic> setValuesFromFriends(final Bundle params) {
final List<Topic> topics = new ArrayList<>();
activity.getSimpleFacebook().getFriends(new OnFriendsListener() {
#Override
public void onComplete(List<Profile> friends) {
for (final Profile profile : friends) {
activity.getSimpleFacebook().get(profile.getId(), "photos/uploaded", params,
new OnActionListener<List<Photo>>() {
#Override
public void onComplete(List<Photo> photos) {
for (final Photo photo : photos) {
// Initialize instance of Topic
final User user = photo.getFrom();
final Topic topic = new Topic();
topic.setCaption(photo.getName());
topic.setImageId(photo.getId());
topic.setCreatedTime(photo.getCreatedTime());
topic.setPostImage(photo.getSource());
topic.setUserId(user.getId());
topic.setName(user.getName());
final Bundle likeParams = new Bundle();
likeParams.putString("fields", "total_count");
likeParams.putString("limit", "100000");
activity.getSimpleFacebook().get(photo.getId(), "likes",
likeParams, new OnActionListener<List<Like>>() {
#Override
public void onComplete(List<Like> likes) {
topic.setNumOfLikes(likes.size());
topics.add(topic);
}
#Override
public void onThinking() {
super.onThinking();
}
});
}
}
});
}
}
});
return topics;
}
You are using AsyncTask incorrectly.
AsyncTask is launching another Thread (thread1) where where it is executing the method, doenloadPhotos. This method is calling setValuesFromFriends which is creating another thread (thread2) with the method getFriends. As thread2 has been launched, the rest of the code in setValuesFromFriends will get executed.
So here is how it is working:
private List<Topic> setValuesFromFriends(final Bundle params) {
final List<Topic> topics = new ArrayList<>();
//launched process on new thread
return topics; //this is 0 as topics = new ArrayList<>();
}
So now topicsFromFriends = 0. Hence you are getting the output = 0.
in effect thread1 is getting executed before thread2 is complete. As the output of thread1 is 0, nothing is displayed in UI after onPostExecute
There is no need of using AsyncTask.
You should put all the required code inside the onComplete of the new OnFriendsListener(). This way the info will be shown correctly. You can launch the progressbar before setValuesFromFriends and then remove it in the onComplete.
I have a single incident where a complete duplicate of a entry was made into the database (the same user comment appeared twice). They had different object IDs but were otherwise the exact same. It was slower than usual to finish the posting and only happened once out of dozens of comments, so I want to say it was a Parse issue during the saveInBackground call. Even so, I expect a service like Parse to be a little more robust. As my first time working with Android though, I also can't be sure nothing is wrong on my end. Any help? Also just any criticisms? This is the method called when the user hits a comment submission button:
private void submitComment() {
String text = commentText.getText().toString().trim();
Intent intent = getIntent();
String ID = intent.getStringExtra("imageID");
String parentID = intent.getStringExtra("parent");
// Set up a progress dialog
final ProgressDialog loadingDialog = new ProgressDialog(CommentSubmitActivity.this);
loadingDialog.setMessage(getString(R.string.publishing_comment));
loadingDialog.show();
Comment comment = new Comment();
comment.setText(text);
comment.setUser((ParseUser.getCurrentUser()));
if (ID.equals("#child")) {
comment.setParent(parentID);
comment.setImage("#child");
ParseQuery<ParseObject> query = ParseQuery.getQuery("Comment");
query.getInBackground(parentID, new GetCallback<ParseObject>() {
public void done(ParseObject parentComment, ParseException e) {
if (e == null) {
int numChild = parentComment.getInt("numChild");
parentComment.put("numChild", ++numChild);
parentComment.saveInBackground();
} else {
Log.d("numChild: ", "error");
}
}
});
} else {
comment.setImage(ID);
comment.put("numChild", 0);
ParseQuery<ParseObject> query = ParseQuery.getQuery("ImageUpload");
query.getInBackground(ID, new GetCallback<ParseObject>() {
public void done(ParseObject image, ParseException e) {
if (e == null) {
int numComments = image.getInt("numComments");
image.put("numComments", ++numComments);
image.saveInBackground();
} else {
Log.d("numComments: ", "error");
}
}
});
}
comment.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
loadingDialog.dismiss();
finish();
}
}
});
}
I encountered similar problem like yours.
I created an app where user can create account and add photo to it and list of objects (friends in my case).
Once when I was testing it user was created twice.
I went through my code and my my suspicions are connected with async calls.
I see that you use asynchronous parse api in you application so no fragment of code is waiting for response and blocking the rest of operations.
You cannot control when parse server will response.
What I did I just put all synchronous requests in my custom async code (AsyncTask in Android).
Hope that my answer somehow meeets your expectations.