I have spent days trying to work out the cause of this crash in the Google Play Services Saved Game code from a tester's device and have no idea what else to try as it works on all three of my devices. The crash that occurs is:
java.lang.NullPointerException: Must provide a previously opened Snapshot
when I try and call
snapshot.readFully();
even though the snapshot has been opened and return code checked, and checked to see if it is null. Here is the code path leading up to the crash, with non-executed sections removed for brevity:
public void LoadSnapshot()
{
AsyncTask<Void, Void, SNAPSHOT_ASYNC_RESULT> task = new AsyncTask<Void, Void, SNAPSHOT_ASYNC_RESULT>()
{
#Override
protected SNAPSHOT_ASYNC_RESULT doInBackground(Void... params)
{
if (isSignedIn())
{
Snapshots.OpenSnapshotResult result = Games.Snapshots.open(getApiClient(), "MySnapshot", true).await();
int status = result.getStatus().getStatusCode();
DebugLog("Snapshot Load Open result code: " + status);
if (status == GamesStatusCodes.STATUS_SNAPSHOT_CONFLICT)
{
Snapshot snapshot = result.getSnapshot();
Snapshot conflictSnapshot = result.getConflictingSnapshot();
//write both conflicted files so we can merge them
if (snapshot != null && conflictSnapshot != null)
{
byte[] ssdata = snapshot.readFully(); //CRASH HERE!
...
}
...
}
}
}
}
task.execute();
}
I get similar crashes from the same device when simply saving sometimes with .open() followed by .writebytes().
It is making the entire game unstable and I need to get this fixed somehow. Any help or ideas would be much appreciated.
All I can think is that because it's running on a background thread in an AsyncTask something bad has happened in between opening the snapshot and trying to read/write it on this device. According to the tester is crashes 'most' of the time.
I solved this eventually by only allowing one Async operation at a time, as I was previously allowing a concurrent save and load of the same snapshot.
I also wrapped the readFully() inside a try/catch for safety.
I'm working on this as well. I'm looking at the documentation now, and it has this to say about the function: readFully()
Read the contents of a snapshot.
If this snapshot was not opened via open(GoogleApiClient, SnapshotMetadata), or if the contents have already been committed via commitAndClose(GoogleApiClient, Snapshot, SnapshotMetadataChange) this method will throw an exception.
Returns
The bytes of the snapshot contents.
Throws
IOException if reading the snapshot failed.
Are you sure that the snapshot has been opened and committed correctly?
Related
I'm working on an app that uses Android's MediaMuxer for recording the screen. Using Crashlytics, a significant number of users have the "Failed to stop the muxer" crash, but I can't reproduce it locally on any of my devices. According to another question, the MPEG4Writer logs generated while MediaMuxer is running should indicate what the source of the problem is, but since I'm unable to reproduce it locally, I need to collect those logs remotely and pass them over to Crashlytics.
So here's my problem: MediaMuxer and MPEG4Writer are system classes, so obviously I can't edit them to add Crashlytics.log() lines. I've thought of having the app read the Logcat output and storing all entries containing MPEG4Writer, which are then sent to Crashlytics if the muxer crashes, using this implementation as a base. Here's my code:
public class LogRetriever extends Thread {
private static final String TAG = LogRetriever.class.getCanonicalName();
public static ArrayList<String> logStorage = new ArrayList<>();
private AtomicBoolean mLoggingActive = new AtomicBoolean(true);
#Override
public void run() {
try {
String[] command = new String[] { "logcat" };
Process process = Runtime.getRuntime().exec(command);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while (mLoggingActive.get() && ((line = bufferedReader.readLine()) != null)){
if(line.contains("MPEG4Writer")) {
logStorage.add(line);
}
}
}
catch (IOException ex) {
Log.e(TAG, "start failed", ex);
}
}
public void stopLogging() {
mLoggingActive.set(false);
}
}
Using the above method, I only seem to get the first four log lines generated by MPEG4Writer. The rest are visible through Android Studio's logcat, but aren't collected by my code. I've also tried this library which seems to do the same thing, but again, same problem, only the first 4 lines are collected. I suspect that MediaMuxer is creating its own process after those 4 lines, at which point I can no longer read its logcat output because my LogRetriever class is now in a different process. So how am I supposed to collect those logs? Am I taking the wrong approach here?
So how am I supposed to collect those logs?
Generally, unless you are working for a device manufacturer, you don't collect those logs.
First, accessing LogCat at runtime has never been officially supported; hence, the clunky "fork logcat" approach that you have to take.
Beyond that, you need the READ_LOGS permission to get more than what you are. That permission has signature|privileged|development for the protectionLevel, meaning that ordinary apps cannot hold that permission.
This is for privacy reasons. READ_LOGS gives you access to all of LogCat, and lots of apps (and some system processes) log information that may be sensitive.
I am getting the above error : System.AggregateException: One or more errors occurred.
With this line of code here:
List<tblDeviceIds> installIDs = KumulosHelper.Functions.Device.GetDeviceIDsOfUser(toUser);
The Method "GetDeviceIdsOfUser" looks like this:
public static List<tblDeviceIds> GetDeviceIDsOfUser(string username)
{
IDictionary<string, string> myDict = new Dictionary<string, string>();
myDict.Add("username", username);
return KumulosGeneral.getTblDeviceIds("getDeviceIDsOfUser", myDict);
}
So, there is really nothing fancy going on.
Sometimes, but only on CERTAIN users above error. So even when the user would be "null", which by the way he never is, the list would just return nothing. BUT instead it crashes. This itself is something I didnt quite understand, so what I did was:
List<tblDeviceIds> installIDs = null;
try
{
installIDs = KumulosHelper.Functions.Device.GetDeviceIDsOfUser(toUser);
}
catch
{
installIDs = null;
}
This would be a bullet prove workaround, but yet: It goes into try, it crashes, it never goes into catch, it is dead.
Would someone care to explain?
Thanks!
O, maybe this has something todo with doing this on another thread? This is the function that calls all that:
await Task.Run(() =>
{
Intermediate.SendMessageToUser(toUsername, temp);
});
As you can see, it is inside an async task... but that should not be a problem, right?
The reason you receive an AggregateException is because the exception is originating from within a Task (that is likely running on a separate thread). To determine the cause, walk the line of InnerException(s).
Regarding the catch not catching, my suggestions would be: Ensure the latest code is being used. Add Tracing instead of relying on breakpoints. And see if the inner exception is thrown from yet another thread (is GetDeviceIDsOfUser also using async?)
See also: Why is .NET exception not caught by try/catch block?
I have android app which create file and read file on google drive (synchronization sql lite db between devices). Create file is running ok. Read file is ok too but only for second attempt. First time my query returns always 0. It looks like first time query check only local storage?
Example: I create export from mobile. It is ok. I can see that file was created and i see it for example via web on google drive. I can see it also via android drive app. So I can do import from this file from my tablet from my app. First attempt is every time failed. Query could not find the file. Second attempt: File was find and imported. Why is this behaviour?
Query is creating like this:
Query query = new Query.Builder().addFilter(Filters.and(
Filters.eq(SearchableField.MIME_TYPE, "text/xml"),
Filters.eq(SearchableField.TITLE,
getResources().getString(R.string.app_file_name)),
Filters.eq(SearchableField.TRASHED, false))).build();
Drive.DriveApi.query(mGoogleApiClient, query)
.setResultCallback(metadataCallback);
and read file is like this in callback result:
MetadataBuffer mdbf = null;
mdbf = result.getMetadataBuffer();
int iCount = mdbf.getCount();
tvout("file count: "+String.valueOf(iCount));
if (iCount == 1){
myFileId = mdbf.get(0).getDriveId();
Log.i(LOG_TAG, "file was found");
readFile();
}
else
{
displayAlertBox(getApplicationContext().getResources()
.getString(R.string.import_alert_res_not_ok)+" (ER-NOFILE)");
}
I do it like it is implemented in google drive api example - query file.
I did really a lot of tests also with sync function.
But every time my iCount is 0 for the first time. Second time it is 1. File was found.
It's because of, The local sync takes a little bit to process. If you make a request before its done, you may get incomplete results. You can use requestSync to wait for a sync to have completed.
Drive.DriveApi.requestSync(mGoogleApiClient).setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(#NonNull Status status) {
if (status.getStatus().isSuccess()) {
// Do you work here
}
}
});
}
Note that, Don't try sync operation too often.
In order to avoid excessive load on the device and the server, sync
requests are rate limited. In this case, the operation will fail with
DRIVE_RATE_LIMIT_EXCEEDED status, which indicates that a sync
already happened quite recently so there is no need for another sync.
The operation will succeed when reattempted after a sufficient backoff
duration.
I would like some help regarding Java - Android MultiThreading
While learning to develop my app in a multi-threading way in order to take advantage of the ever-growing multi-core devices market share (most devices are quad core now, some even octo-core), I ran in a situation where my threads are either being calling twice or running twice.
I just don't why and how.
[EDIT 3]
Alright, I narrowed down the issue : I called the AsyncTask from the onResume() method. Although my app did not lost focus (which would mean a call to onPause() then back to onResume() upon return of focus in which case my threads would be run twice) during the tests, I solved the issue by moving away the call to FetchFriendsList to another place.
So far so good, but since in my tests the app did not loose focus or perhaps it did but I could not witness it (!), I think there is another reason behind so I'd say my problem is not entirely solved ... at least for the moment. It does work though. Perhaps I did solve the issue but I do not know how :(
[end of EDIT 3]
I am implementing last Facebook SDK and I am using it to fetch the end-user friends list, which seems to do the work.
Since I am running this operation in an AsyncTask, I am not using request.executeAsync().
Instead I am using request.executeAndWait(). Facebook JavaDoc does state that this method must only be used if I am not in a the Main UI Thread which is my case otherwise I would get a NetworkOnMainThreadException.
Anyway, this is where the weird behavior is happening.
private final ArrayList<GraphUser> userFriendsList = new ArrayList<GraphUser>();
public final void fetchFriendsList() {
if (this.session != null && this.session.isOpened()) {
final Request requestUserFriendsList = Request.newMyFriendsRequest(
this.session, new Request.GraphUserListCallback()
public final void onCompleted(final List<GraphUser> users, final Response response) {
if (users != null && users.size() > 0) {
Log.v("Retrieved Friends List -> ", String.valueOf(users.size()));
userFriendsList.addAll(users);
}
}
}
);
if (this.asyncFlag)
requestUserFriendsList.executeAsync();
else
requestUserFriendsList.executeAndWait();
}
}
In my case, asyncFlag is set to false because I need to do stuff synchronously in that specific order :
Fetch User Friends List (not on the Main (UI) Thread)
Save friends list on device (separate new thread)
Save friends list on a server (separate new thread)
Following this pattern, the line userFriendsList.addAll(users); is called twice.
In the logcat, the Log.vis showed twice as well, and finally looking with the debugger, the content of the user friends list is made of duplicates.
But that's not all ... step 2 and 3 are indeed two separate threads which are both created and spawned within the same method : public final void asyncSaveFacebookFriendsList().
And guess what, this method is even called twice !
just why ?
At the beginning I was calling the method for step 2 and 3 like this :
[...]
userFriendsList.addAll(users);
asyncSaveFacebookFriendsList(); // it was private before
[...]
This is where the issue started as both line were running twice.
So I thought, alright, I'll call it later like this :
[...]
fetchFriendsList();
asyncSaveFacebookFriendsList(); // it is now public
[...]
But the issue remains still.
If I don't call public final void asyncSaveFacebookFriendsList(), then nothing is run twice.
Why does this issue happen ? Is there something I did not get in Java Threads ?
I do not think this is somehow related to the Facebook SDK because following the same pattern (and doing it also at the same time), I have the same issues when fetching and storing the end-user Twitter friends list.
So I do believe I am doing something wrong. Does someone have any idea in what possible case a thread is called twice ?
Note : all threads are started this way : thread.start(). I am not using any ThreadPool nor the ExecutorService.
In case you need more background context :
Content of AsyncTask : (no need to wonder why Void and Long, I remove the irrelevant code related to it)
private final class FetchFriendsLists extends AsyncTask<Long, Integer, Void> {
protected final Void doInBackground(final Long... params) {
if (params[0] != Long.valueOf(-1)) {
[...]
twitterAPI.fetchUserFriendsList();
publishProgress(1, -1);
}
if (params[1] == Long.valueOf(0)) {
[...]
facebookAPI.fetchFriendsList();
publishProgress(-1, 0);
}
return null;
}
protected final void onProgressUpdate(Integer... flags) {
super.onProgressUpdate(flags);
if (flags[0] != -1)
twitterAPI.asyncSaveFacebookFriendsList();
if (flags[1] == 0)
facebookAPI.asyncSaveFacebookFriendsList();
}
}
As you can see, I start step 2 and 3 in onPublishProgress() which runs on the Main UI Thread. Brefore it was in the doInBackground() method : the issue happens in both cases!
[EDIT]
After further test, it would seem any kind of code is in fact running twice.
I created a simple method called test in which in print a counter. The counter incremente twice as well !
Why you use onProgressUpdate?¿?
onProgressUpdate(Progress...), [...]. This method is used to display any form of progress in the
user interface while the background computation is still executing.
For instance, it can be used to animate a progress bar or show logs in
a text field.
This is used not at the finish of the petition, but when progress increased.
Read this:
http://developer.android.com/reference/android/os/AsyncTask.html
You need to use:
protected void onPostExecute(Long result) {
I am trying to integrate kiip in my android app . I have downloaded the latest sdk and sample example from https://kiip.me/ developers site . Also ,I have created a new app in kiip.me site.
Everything is fine but the problem is , I am getting KPResource null so showing No Promo .
Here is the listner that I am using in onStart() method:
public void onStart() {
super.onStart();
// The Activity context has been created by now, so start a new session.
KPManager.getInstance().startSession(mStartSessionListener);
}
private KPRequestListener<KPResource> mStartSessionListener = new KPRequestListener<KPResource>() {
public void onFinished(KPManager manager, KPResource response) {
if (response != null) {
toast("Start Session Finished w/ Promo");
} else {
toast("Start Session Finished No Promo");
}
manager.showResource(response);
// Start retrieving user's location
new LocationHelper(ExampleApplication.this).requestLocationUpdates(mLocationListener);
}
Here the response is always null , so getting the message : Start Session Finished No Promo . If anyone has got similar problem then please share your views.
Thanks in advance.
Have you enabled promos for test devices in the dashboard on https://kiip.me?
It sounds like it could be a settings issue on the server side. Try logging in and switching promo frequency and adding your test device.
Finally, the issue has been resolved. I think the problem was with my device , when I tested it next day with some other device, It worked for me.