I've been trying to build some functionality into my app too allow user-generated data (EEG recordings) to be sent to a central BigQuery database.
I've never done any networking code in Java before, so I shied away from doing the POST or REST-based strategies recommended here. The BigQuery Java client library seemed to be exactly what I needed, though I was completely confused why it wouldn't officially support Android.
Still, I came across this example Android app (from Google no less) that promised to do exactly what I wanted with the BigQuery Client library. I incorporated it into my app as follows:
// .... in an AsyncTask
#Override
protected String doInBackground(String... params) {
String CSV_CONTENT = params[0];
try {
AssetManager am = MainApplication.getInstance().getAssets();
InputStream isCredentialsFile = am.open(CREDENTIALS_FILE);
BigQuery bigquery = BigQueryOptions.builder()
.authCredentials(AuthCredentials.createForJson(isCredentialsFile))
.projectId( PROJECT_ID )
.build().service();
TableId tableId = TableId.of(DATASET,TABLE);
Table table = bigquery.getTable(tableId);
int num = 0;
Log.d("Main", "Sending CSV: ");
WriteChannelConfiguration configuration = WriteChannelConfiguration.builder(tableId)
.formatOptions(FormatOptions.csv())
.build();
try (WriteChannel channel = bigquery.writer(configuration)) {
num = channel.write(ByteBuffer.wrap(CSV_CONTENT.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
Log.d("Main", e.toString());
}
Log.d("Main", "Loading " + Integer.toString(num) + " bytes into table " + tableId);
} catch (Exception e) {
Log.d("Main", "Exception: " + e.toString());
}
return "Done";
}
This runs without any errors and fires off an API call that is detected by Google Cloud Storage. However, it returns error 200 (job was cancelled) every time. I don't understand how this could be since I'm not doing anything in the code to cancel the request and I don't see how the async task I put the call in could be cancelled.
Was this just a bad example app I copied and a bad usage of the BigQuery Client? If so, what's the best way to send data to BigQuery from Android?
I've been trying to use the Parse library to access MongoDB in order to create users or objects for my Android application. The issue is that every time I attempt to SignUpInBackground or SignInInBackground I get this super unspecific error.
"java.lang.NullPointerException: value == null"
Just value == null.. I have no idea which value this refers to but the most frustrating part is that the code I'm using works perfectly in one app and despite copying it over, it always returns this error.
I usually wouldn't bother making a question for my issue but I've been trying to find a solution to this for about a week now and have been unable to find anything related to my issue online.
Hope someone out there has seen this before, as I'm about out of ideas for this one. Thanks in advance to anyone who gets back to me on this :)
The Sign Up Method:
private void signUp(){
String username = "Andy"; //Temp
ParseUser user = new ParseUser();
user.setUsername(username);
user.setEmail(txtUsername.getText().toString());
user.setPassword(txtPassword.getText().toString());
Log.i(TAG,"SignUp method. Parse user = " + user.getUsername() + ", " + user.getEmail());
user.signUpInBackground(new SignUpCallback() {
#Override
public void done(ParseException e) {
if(e==null){
Log.i(TAG, "Successfully created new user!");
signIn();
} else {
Log.i(TAG, "Failed to create new user. Error: " + e.getMessage());
Toast.makeText(getContext(),"Sign up failed! " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
}
Error:
I/LogIn: Validation success
I/LogIn: SignUp method. Parse user = Andy, test
I/LogIn: Failed to create new user. Error: java.lang.NullPointerException: value == null
Edit:
While stepping through the parse libraries, I ended up with this error message several times in the log.
I/art: Rejecting re-init on previously-failed class java.lang.Class
This is fairly simple question but as I'm not using parse.com Android built-in SDK, I got stuck.
I'm trying to pass the data through the following in Android to parse.com even though the object is created with no data, it doesn't work when I include data.
The text box in Android app covnerts the data through this to JSON:
protected JSONObject getBodyTextAsJSON() {
String bodyText = getBodyText();
if (bodyText != null && !TextUtils.isEmpty(bodyText)) {
try {
return new JSONObject(bodyText);
} catch (JSONException e) {
Log.e(LOG_TAG, "User's data is not a valid JSON object", e);
}
}
return null;
}
when I enter some data my Logcat shows the following which seems fine to me:
The code to show log output:
try {
Iterator keys = body.keys();
Log.d(LOG_TAG, "JSON data:");
while (keys.hasNext()) {
String key = (String) keys.next();
Log.d(LOG_TAG, " " + key + ": " + body.get(key));
params.put(key, body.get(key).toString());
}
} catch (JSONException e) {
Log.w(LOG_TAG, "Unable to retrieve a JSON value", e);
}
Say when I enter this data similar to this {"a"="test1", "b"= test2"} in the text body I get the folowing in the logcat:
12-31 09:09:22.631 1991-1991/com.loopj.android.http.sample D/JsonStreamSample: JSON data:
12-31 09:09:22.631 1991-1991/com.loopj.android.http.sample D/JsonStreamSample: a: asd
12-31 09:09:22.632 1991-1991/com.loopj.android.http.sample D/JsonStreamSample: b: ert"
which seems to be fine; however, parse.com does not record the data entered. Instead when the text box is blank it creates the object fine (but with no data which is not desired). It seems to me the JSON in Android and parse.com don't have the same format.
I'm attempting to get a list of URL's from a subreddit in order to load them into universal image loader for viewing pleasure. However, I cannot figure out where my query is going wrong. Plus, I'm not familiar with android studio's logcat as compared to eclipses logcat, so I'm not exactly sure where to look for my debugging responses.
Here's the query method:
public void queryReddit()
{
// Prepare your search string to be put in a URL
// It might have reserved characters or something
// String urlString = "";
// try {
// urlString = URLEncoder.encode(searchString, "UTF-8");
// } catch (UnsupportedEncodingException e) {
//
// // if this fails for some reason, let the user know why
// e.printStackTrace();
// Toast.makeText(this, "Error: " + e.getMessage(), Toast.LENGTH_LONG).show();
// }
// Create a client to perform networking
AsyncHttpClient client = new AsyncHttpClient();
// 11. start progress bar
setProgressBarIndeterminateVisibility(true);
// Have the client get a JSONArray of data
// and define how to respond
client.get("http://www.reddit.com/r/pics/.json",
new JsonHttpResponseHandler() {
#Override
public void onSuccess(JSONObject jsonObject) {
// 11. stop progress bar
setProgressBarIndeterminateVisibility(false);
// Display a "Toast" message
// to announce your success
Toast.makeText(getApplicationContext(), "Success!", Toast.LENGTH_LONG).show();
// // 8. For now, just log results
// Log.d("omg android", jsonObject.toString());
try
{
Log.d("go reddit yay", jsonObject.toString());
JSONObject testingData = (JSONObject) jsonObject.get("data");
JSONArray testingChildren = (JSONArray) testingData.get("children");
JSONObject testingLogData = (JSONObject) testingChildren.get(0);
JSONArray children = (JSONArray) jsonObject.get("children");
JSONObject logData = (JSONObject) children.get(0);
Log.d("go reddit yay", logData.getString("url"));
Log.d("go reddit yay", testingLogData.getString("url"));
for(int i = 0; i < 10; i++)
{
JSONObject data = (JSONObject) children.get(i);
if(data.getString("url") != null)
{
System.out.println(data.getString("url"));
}
//if the url field exists and it's a picture that univ image loader understands then add it
if(data.getString("url") != null && data.getString("url").substring(data.getString("url").length()-3).equals("png") ||
data.getString("url").substring(data.getString("url").length()-3).equals("jpg"))
{
imageUrls.add(data.getString("url"));
System.out.println(data.getString("url"));
}
//TODO I found this error: this requires android.permission.INTERACT_ACROSS_USERS_FULL
}
mPagerAdapter.notifyDataSetChanged();
} catch (JSONException e)
{
e.printStackTrace();
}
//TODO Might want to put all this data in a try catch block and do it right here.
// update the data in your custom method.
//updateData()
}
#Override
public void onFailure(int statusCode, Throwable throwable, JSONObject error)
{
// 11. stop progress bar
setProgressBarIndeterminateVisibility(false);
// Display a "Toast" message
// to announce the failure
Toast.makeText(getApplicationContext(), "Error: " + statusCode + " " + throwable.getMessage(), Toast.LENGTH_LONG).show();
// Log error message
// to help solve any problems
Log.e("omg android", statusCode + " " + throwable.getMessage());
}
});
}
Within the try catch block, the only logged information that ends up being sent to logcat is the first line, Log.d("go reddit yay", jsonObject.toString());
I can't find the responses from the other log calls which is very strange to me.
Here's the response from the first log call:
06-17 06:35:29.324 17133-17133/.wallpaper D/absfr﹕ {"data":{"after":"t3_2823ou","children":[{"data":{"media_embed":{},"author_flair_css_class":null,"score":503,"created_utc":1402931529,"clicked":false,"visited":false,"id":"28a94k","author":"JamesBDW","title":"Any Bronson fans? [1920x1080]","over_18":false,"created":1402960329,"name":"t3_28a94k","selftext_html":null,"domain":"i.imgur.com","author_flair_text":null,"secure_media":null,"num_reports":null,"edited":false,"stickied":false,"link_flair_text":null,"link_flair_css_class":null,"saved":false,"secure_media_embed":{},"subreddit_id":"t5_2qmjl","distinguished":null,"gilded":0,"url":"https:\/\/i.imgur.com\/Hq1fcSm.jpg","banned_by":null,"subreddit":"wallpaper","is_self":false,"num_comments":31,"approved_by":null,"thumbnail":"http:\/\/a.thumbs.redditmedia.com\/Dh2iU7Q0rpFogkWt.jpg","permalink":"\/r\/wallpaper\/comments\/28a94k\/any_bronson_fans_1920x1080\/","hidden":false,"likes":null,"downs":188,"ups":691,"selftext":"","media":null},"kind":"t3"},{"data":{"media_embed":{},"author_flair_css_class":null,"score":8,"created_utc":1402989714,"clicked":false,"visited":false,"id":"28cnyn","author":"ZadocPaet","title":"Active Dunes on Mars (OS) [1024x768]","over_18":false,"created":1403018514,"name":"t3_28cnyn","selftext_html":null,"domain":"nasa.gov","author_flair_text":null,"secure_media":null,"num_reports":null,"edited":false,"stickied":false,"link_flair_text":null,"link_flair_css_class":null,"saved":false,"secure_media_embed":{},"subreddit_id":"t5_2qmjl","distinguished":null,"gilded":0,"url":"http:\/\/www.nasa.gov\/sites\/default\/files\/styles\/1024x768_autoletterbox\/public\/pia18244.jpg","banned_by":null,"subreddit":"wallpaper","is_self":false,"num_comments":1,"approved_by":null,"thumbnail":"http:\/\/a.thumbs.redditmedia.com\/dFTBquSWiMSjK0aZ.jpg","permalink":"\/r\/wallpaper\/comments\/28cnyn\/active_dunes_on_mars_os_1024x768\/","hidden":false,"likes":null,"downs":3,"ups":11,"selftext":"","media":null},"kind":"t3"},{"data":{"media_embed":{"content":"<iframe class=\"embedly-embed\" src=\"\/\/cdn.embedly.com\/widgets\/media.html?src=http%3A%2F%2Fimgur.com%2Fa%2F0jeZf%2Fembed&url=http%3A%2F%2Fimgur.com%2Fa%2F0jeZf&image=http%3A%2F%2Fi.imgur.com%2F2PdUiuE.jpg&key=2aa3c4d5f3de4f5b9120b660ad850dc9&type=text%2Fhtml&schema=imgur\" width=\"550\" height=\"550\" scrolling=\"no\" frameborder=\"0\" allowfullscreen><\/iframe>","scrolling":false,"height":550,"width":550},"author_flair_css_class":null,"score":1,"created_utc":1403004127,"clicked":false,"visited":false,"id":"28czid","author":"smessies","title":"I found a wallpaper album full of iconic design chairs. I tought there were some important ones missing so i started adding some myself. work in progress! [1920x1200]","over_18":false,"created":1403032927,"name":"t3_28czid","selftext_html":null,"domain":"imgur.com","author_flair_text":null,"secure_media":null,"num_reports":null,"edited":false,"stickied":false,"link_flair_text":null,"link_flair_css_class":null,"saved":false,"secure_media_embed":{},"subreddit_id":"t5_2qmjl","distinguished":null,"gilded":0,"url":"http:\/\/imgur.com\/a\/0jeZf","banned_by":null,"subreddit":"wallpaper","is_self":false,"num_comments":0,"approved_by":null,"thumbnail":"http:\/\/b.thumbs.redditmedia.com\/GYaN5fyJfY8fI8xE.jpg","permalink":"\/r\/wallpaper\/comments\/28czid\/i_found_a_wallpaper_album_full_of_iconic_design\/","hidden":false,"likes":null,"downs":0,"ups":1,"selftext":"","media":{"type":"imgur.com","oembed":{"thumbnail_height":1200,"author_url":"http:\/\/imgur.com\/user\/smessies","width":550,"type":"rich","version":"1.0","thumbnail_url":"http:\/\/i.imgur.com\/2PdUiuE.jpg","thumbnail_width":1920,"title":"imgur: the simple image sharer","height":550,"description":"Imgur is home to the web's most popular image content, curated in real time by a dedicated community through commenting, voting and sharing.","author_name":"smessies","html":"<iframe class=\"embedly-embed\" src=\"\/\/cdn.embedly.com\/widgets\/media.html?src=http%3A%2F%2Fimgur.com%2Fa%2F0jeZf%2Fembed&url=http%3A%2F%2Fimgur.com%2F
no idea what to do.
One last debugging info is that the toast for the onSuccess method does appear, so it is clearly successful in it's query, I'm just doing something wrong in interpreting the data.
From your description it seems like a statement after the first Log.d("go reddit yay", ...); call throws an exception which you can't see.
Try replacing your exception handling code:
} catch (JSONException e)
{
e.printStackTrace();
}
with this:
} catch (Throwable t)
{
Log.e("omg android", "Exception in onSuccess()", t);
}
and check if any exceptions are logged.
I'm trying to indicate the authentication / sync status of an account using the AccountAuthenticator and SyncAdapter. I've been through the samples, and can get it working alright.
How can I set the indicator to red just like the GMail account?
I'd also like to add additional status indicators on the sync adapter page. See picture below:
Answering my own question for future team knowledge...
Getting the indicator to change color was fairly easy after some experimentation. Start by creating a project based on thecode supplied in the SDK sample projects, modify as follows:
1) Fake the initial login from the server during the AuthenticationActivity. Once past the initial check, the system will start it's periodic sync attempts.
/**
* Called when the authentication process completes (see attemptLogin()).
*/
public void onAuthenticationResult(boolean result) {
Log.i(TAG, "onAuthenticationResult(" + result + ")");
// Hide the progress dialog
hideProgress();
// Override the result, we don't care right now....
result = true;
if (result) {
if (!mConfirmCredentials) {
finishLogin();
} else {
finishConfirmCredentials(true);
}
} else {
Log.e(TAG, "onAuthenticationResult: failed to authenticate");
if (mRequestNewAccount) {
// "Please enter a valid username/password.
mMessage.setText(getText(R.string.login_activity_loginfail_text_both));
} else {
// "Please enter a valid password." (Used when the
// account is already in the database but the password
// doesn't work.)
mMessage.setText(getText(R.string.login_activity_loginfail_text_pwonly));
}
}
}
2) Modify the "onPerformSync()" method within the SyncAdapter. The key here are the "syncResult.stats" fields. While modifying them, I found that inserting multiple errors didn't get the effect I wanted. Also noting that the counts didn't seem to be recorded across sync attempts (i.e. the fails always come in as zero). The "lifetimeSyncs" is a static variable that keeps count across sync attempts. This modified code will continue to alternate between green and red...
#Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
List<User> users;
List<Status> statuses;
String authtoken = null;
try {
// use the account manager to request the credentials
authtoken = mAccountManager.blockingGetAuthToken(account, Constants.AUTHTOKEN_TYPE, true );
// fetch updates from the sample service over the cloud
//users = NetworkUtilities.fetchFriendUpdates(account, authtoken, mLastUpdated);
// update the last synced date.
mLastUpdated = new Date();
// update platform contacts.
Log.d(TAG, "Calling contactManager's sync contacts");
//ContactManager.syncContacts(mContext, account.name, users);
// fetch and update status messages for all the synced users.
//statuses = NetworkUtilities.fetchFriendStatuses(account, authtoken);
//ContactManager.insertStatuses(mContext, account.name, statuses);
if (SyncAdapter.lifetimeSyncs-- <= 0 ){
//mAccountManager.invalidateAuthToken(Constants.ACCOUNT_TYPE, authtoken);
syncResult.stats.numAuthExceptions++;
//syncResult.delayUntil = 60;
lifetimeSyncs = 5;
}
} catch (final AuthenticatorException e) {
syncResult.stats.numParseExceptions++;
Log.e(TAG, "AuthenticatorException", e);
} catch (final OperationCanceledException e) {
Log.e(TAG, "OperationCanceledExcetpion", e);
} catch (final IOException e) {
Log.e(TAG, "IOException", e);
Log.d(TAG, extras.toString());
syncResult.stats.numAuthExceptions++;
syncResult.delayUntil = 60;
//extras.putString(AccountManager.KEY_AUTH_FAILED_MESSAGE, "You're not registered");
} catch (final ParseException e) {
syncResult.stats.numParseExceptions++;
Log.e(TAG, "ParseException", e);
}
}
That's it, enjoy playing with the delays and other variables too...