I am creating a webview based Android Application using Phonegap. To help the application, I have created a service that basically gets user's location from time to time and processes it and saves it.
This is what happens:
I run the application - I have startService() call in onCreate() of the MainActivity. There is no other activity in the application (until now).
The service runs, application runs. I can see all this in LogCat.
Now, when I press back key on application's first screen, application exits and as a result after few seconds I see stack trace in LogCat and message that application has stopped. The error is NullPointerException
I get the exception in method below at indicated line:
public void GetAvailableLocation(){
vstore = new VariableStorage(); //Even when I assigned new object to vstore
if(vstore.load("mobileNumber").equals("0")) // Exception occures here
return;
// Get all available providers
List<String> providers = locationManager.getAllProviders();
for(String provider: providers) {
Location newLocation = locationManager.getLastKnownLocation(provider);
if(isBetter(newLocation, locationListener.location)
&& newLocation != null) {
locationListener.location = newLocation;
}
}
}
The above method is first method called in onCreate() of service.
Please help me out on this.
Edit: here is the load method in vstore-
public String load(String key){
Log.d(TAG, "Load key: "+key);
try{
if(!loaded){
this.loadFromFile();
}
String result = null;
if(key.equals("loggedIn"))
result = Boolean.toString(loggedIn);
else if(key.equals("mobileNumber"))
result = Long.toString(mobileNumber);
else if(key.equals("password"))
result = password;
else if(key.equals("gettingService"))
result = Boolean.toString(gettingService);
else if(key.equals("providingService"))
result = Boolean.toString(providingService);
else if(key.equals("gettingServiceID"))
result = Integer.toString(gettingServiceID);
else if(key.equals("providingServiceTo"))
result = Long.toString(providingServiceTo);
else if(key.equals("usersName"))
result = usersName;
else if(key.equals("currLatitude"))
result = Double.toString(currLatitude);
else if(key.equals("currLongitude"))
result = Double.toString(currLongitude);
else if(key.equals("prevLatitude"))
result = Double.toString(prevLatitude);
else if(key.equals("prevLongitude"))
result = Double.toString(prevLongitude);
else if(key.equals("lastLocationUpdateTime"))
result = Integer.toString(lastLocationUpdateTime);
else if(key.equals("publicKey"))
result = publicKey;
else if(key.equals("notification"))
result = Integer.toString(notification);
else if(key.equals("verifyMobileNumber"))
result = Long.toString(verifyMobileNumber);
return result;
}
catch(Exception e){
Log.d(TAG, "VSLoad Error: " + e.getMessage());
return null;
}
}
that is a better way to write that condition:
if("0".equals(vstore.load("mobileNumber")))
"0" is always given. so if load returns null you will call return;
That is called null saved :)
Be sure that vstore.load("mobileNumber") returns something
or write something like:
if(vstore.load("mobileNumber") == null || vstore.load("mobileNumber").equals("0"))
return;
Related
When doing defensive programming, I often have some early-return code. In addition, I need to write some log warning that the function returns because of what problem.
void f(A a) {
var b = a.b;
if (b == null) {
Log.warn('f skip running since b==null');
return;
}
var c = some_func(b);
if (c == null) {
Log.warn('f skip running since c==null');
return;
}
another_func(c);
}
I know I can use null-aware operators like ?. but it cannot print warnings when it is null.
I have also tried another approach:
void f(A a) {
var errorMessage = fCore(a);
if (errorMessage!=null) Log.warn(errorMessage);
}
String fCore(A a) {
var b = a.b;
if (b == null) {
return 'f skip running since b==null';
}
var c = some_func(b);
if (c == null) {
return 'f skip running since c==null';
}
another_func(c);
return null
}
But that is also kind of verbose.
Is there a better approach? Thanks!
EDIT
I call it like:
void outer_func() {
one_func();
f(); // even if f skip running, I want two_func still run, etc
two_func();
...
}
Thus, I cannot simply do something like throw LogWarnException('...') and catch at outermost function.
In my app I need to monitorize recently added or updated packages, but since Oreo this is a hard task.
To do it I have a service that runs every X time to detect the new installed/updated apps.
The main core of this service is to call the getChangedPackages function from the PackageManager, but this function always returns null, even if I install or update any app from or not from the Play Store in the interval between two consequtive calls to getChangedPackages.
https://developer.android.com/reference/android/content/pm/PackageManager.html#getChangedPackages(int)
I need to request any permission to call this function? Is the getChangedPackages buggy?
private void _doProcess()
{
try
{
PackageManager package_manager = getPackageManager();
int sequence_number = ApplicationPreferences.getInteger(this, GET_CHANGED_PACKAGES_SEQUENCE_NUMBER_KEY, 0);
ChangedPackages changed_packages = package_manager.getChangedPackages(sequence_number);
LogUtilities.show(this, String.format("Retrieve recently apps installs/updates using sequence number %d returns %s", sequence_number, changed_packages == null ? "null" : "a not null object"));
if (changed_packages == null) changed_packages = package_manager.getChangedPackages(0);
LogUtilities.show(this, String.format("Retrieve recently apps installs/updates using sequence number %d returns %s", sequence_number, changed_packages == null ? "null" : "a not null object"));
if (changed_packages != null)
{
List<String> packages_names = changed_packages.getPackageNames();
LogUtilities.show(this, String.format("%d recently installed/updated apps", packages_names == null ? 0 : packages_names.size()));
if (packages_names != null) for (String package_name : packages_names) PackagesUpdatedReceiver.doProcessPackageUpdate(this, new Intent(isNewInstall(package_manager, package_name) ? Intent.ACTION_PACKAGE_ADDED : Intent.ACTION_PACKAGE_REPLACED).setData(Uri.parse(String.format("package:%s", package_name))));
LogUtilities.show(this, String.format("Storing %s is the sequence number for next iteration", changed_packages.getSequenceNumber()));
ApplicationPreferences.putInteger(this, GET_CHANGED_PACKAGES_SEQUENCE_NUMBER_KEY, changed_packages.getSequenceNumber());
}
else
{
LogUtilities.show(this, String.format("Storing %s is the sequence number for next iteration", sequence_number + 1));
ApplicationPreferences.putInteger(this, GET_CHANGED_PACKAGES_SEQUENCE_NUMBER_KEY, sequence_number + 1);
}
}
catch (Exception e)
{
LogUtilities.show(this, e);
}
}
My experimental results so far have shown that this PackageManager API method getChangedPackages() is not reliable: quite often the returned ChangedPackages value contains many unchanged packages. So I’ve decided to implement a similar feature in a class called PackageUtils, as shown below. The idea is to poll for all the installed packages, as shown in method getInstalledPackageNames() below, and compare the string list with a previously saved one. This comparison boils down to comparing 2 string lists, as shown in method operate2StringLists() below. To get a set of removed packages, use GET_1_MINUS_2_OR_REMOVED as operation. To get a set of added packages, use GET_2_MINUS_1_OR_ADDED as operation.
public class PackageUtils {
public static final int GET_1_MINUS_2_OR_REMOVED = 0;
public static final int GET_2_MINUS_1_OR_ADDED = 1;
// Get all the installed package names
public static List<String> getInstalledPackageNames(Context context) {
List<String> installedPackageNames = new ArrayList<>();
try {
PackageManager packageManager = context.getPackageManager();
List<ApplicationInfo> appInfoList = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo appInfo : appInfoList) {
installedPackageNames.add(appInfo.packageName);
}
} catch (Exception e) {
e.printStackTrace();
}
return installedPackageNames;
}
// Compare 2 string lists and return differences.
public static Set<String> operate2StringLists(List<String> pkgList1, List<String> pkgList2, int operation) {
Set<String> result = null;
Set<String> pkgSet1 = new HashSet<String>(pkgList1);
Set<String> pkgSet2 = new HashSet<String>(pkgList2);
switch (operation) {
case GET_1_MINUS_2_OR_REMOVED:
pkgSet1.removeAll(pkgSet2);
result = pkgSet1;
break;
case GET_2_MINUS_1_OR_ADDED:
pkgSet2.removeAll(pkgSet1);
result = pkgSet2;
break;
default:
break;
}
return result;
}
}
The code has been tested on an Android Oreo device. It can reliably detect all added and removed packages between 2 time instances. However, it can’t detect updated packages in-between.
Finally got it. You have to create a variable called sequenceNumber, and update it every time you query changed packages.
private static int sequenceNumber = 0;
...
PackageManager pm = getContext().getPackageManager();
ChangedPackages changedPackages = pm.getChangedPackages(sequenceNumber);
if(changedPackages != null)
sequenceNumber = changedPackages.getSequenceNumber();
I have some pics to upload to the ftp server and I am using Asynctask for it.The images need to be sent to multiple host so I am using a for loop.The data to be passed is very well being fetched by the constructor but the doInBackground method is not running which was earlier running very well without the for loop and the additional data apart from the String filePathName that I am trying to pass in now in doInBackground.please help me
class uploadTask extends AsyncTask<String, Void, String> {
public uploadTask(String filePathName,String host_2,String user_2,String pass_2)
{
filePath=filePathName;
host_1=host_2;
user_1=user_2;
pass_1=pass_2;
Toast.makeText(getBaseContext(),"FTP DATA RECEIVING:"+"HOST:"+host_2+" USERNAME:"+user_2+" PASS:"+pass_2,Toast.LENGTH_LONG).show();
//hostName=host;
}
#Override
protected String doInBackground(String... params) {
try {
Toast.makeText(getBaseContext(),"Entered Do in Background Method to upload",Toast.LENGTH_SHORT).show();
ftp_host = "ftp.photoshelter.com";//This is not the correct way. Supposed to get from Backendless table
ftp_username = "brytest";//This is not the correct way. Supposed to get from Backendless table
ftp_password = "passtest";//This is not the correct way. Supposed to get from Backendless table
Toast.makeText(getBaseContext(),"HOST:"+ftp_host+" USERNAME:"+ftp_username+" PASS:"+ftp_password,Toast.LENGTH_LONG).show();
news_agency = "news agency";
easyFTP ftp = new easyFTP();
ftp.connect(ftp_host, ftp_username, ftp_password);
status = ftp.setWorkingDirectory("mem/images"); // if User say provided any Destination then Set it , otherwise
// Upload will be stored on Default /root level on server
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageTimeStamped = ftp_username + "_" + timeStamp + ".png";
FileInputStream is = new FileInputStream(imageFileLocation);
//addPhotoGrapherInfo();
ftp.uploadFile(is, imageTimeStamped);
System.out.println("Successfull ftp upload to " + ftp_host);
Toast.makeText(getBaseContext(), "Photo uploading by ftp to " + ftp_host, Toast.LENGTH_LONG).show();
//}
//reset booleans
//cameraPicTaken = false;
//galleryImageSelected = false;
//System.out.println("reset cameraPicTaken and galleryImageSelected");
// }
return new String("Upload Successful");
}catch (Exception e){
String t="Failure : " + e.getLocalizedMessage();
return t;
}
}
}
my onClickListener with for loop
if(cameraPicTaken || galleryImageSelected) {
Toast.makeText(SubmitActivity.this,"Image Location is:"+ imageFileLocation,Toast.LENGTH_LONG).show();
//addPhotoGrapherInfo();
for(int i=0;i<Common.selectedHostArray.size();i++) {
uploadFile(imageFileLocation,Common.selectedHostArray.get(i),Common.selectedUsernameArray.get(i),Common.selectedPasswordArray.get(i));
}
cameraPicTaken = false;
galleryImageSelected = false;
}
funnction called in onClick
public void uploadFile(String filePath,String host_1,String user_1,String pass_1)
{
if(cameraPicTaken == true) {
System.out.println("camera photo start upload");
//for(int i=0;i<Common.selectedHostArray.size();i++) {
//host_1=Common.selectedHostArray.get(i);
//user_1=Common.selectedUsernameArray.get(i);
//pass_1=Common.selectedPasswordArray.get(i);
//host_1="ftp.photoshelter.com";
//user_1="brytest";
//pass_1="passtest";
Toast.makeText(getBaseContext(),"FTP DATA PASSING:"+"HOST:"+host_1+" USERNAME:"+user_1+" PASS:"+pass_1,Toast.LENGTH_LONG).show();
new uploadTask(filePath,host_1,user_1,pass_1).execute();
// }
//cameraPicTaken = false;
//galleryImageSelected = false;
System.out.println("reset cameraPicTaken and galleryImageSelected");
//cameraPicTaken = false;
}
if(galleryImageSelected == true){
System.out.println("gallery image start upload");
Toast.makeText(getBaseContext(),"FTP DATA PASSING:"+"HOST:"+host_1+" USERNAME:"+user_1+" PASS:"+pass_1,Toast.LENGTH_LONG).show();
new uploadTask(filePath,host_1,user_1,pass_1).execute();
//new uploadTask(filePat)h.execute();
//galleryImageSelected = false;
}
Toast.makeText(getBaseContext(), "Photo uploading by ftp to photoshelter.com" /*+ news_agency*/, Toast.LENGTH_LONG).show();
}
You're trying to perform a UI command on a background thread (Toast). This is causing your background tasks to fail early. Since your background tasks catch their own errors, they fail silently.
#Override
protected String doInBackground(String... params) {
try {
// you can't Toast on a background thread, this should throw an exception
Toast.makeText(getBaseContext(),"Entered Do in Background Method to upload",Toast.LENGTH_SHORT).show();
...
}catch (Exception e){
// your Toast exception is getting caught silently here
String t="Failure : " + e.getLocalizedMessage();
return t;
}
}
By the way, the try/catch on everything is not a good practice. You end up with a ton of silent failures leaving you scratching your head and asking why things aren't working.
i use different savegames in my app. "coins" , "levels" , ...
It works fine but if a conflict detected then its wrong result.
/**
* Conflict resolution for when Snapshots are opened. Must be run in an AsyncTask or in a
* background thread,
*/
Snapshots.OpenSnapshotResult processSnapshotOpenResult(Snapshots.OpenSnapshotResult result, int retryCount) {
retryCount++;
int status = result.getStatus().getStatusCode();
Log.i(TAG, "Load Result for saveGame<" + savedGame.getName() + "> status: " + status);
if (status == GamesStatusCodes.STATUS_OK) {
return result;
} else if (status == GamesStatusCodes.STATUS_SNAPSHOT_CONTENTS_UNAVAILABLE) {
return result;
} else if (status == GamesStatusCodes.STATUS_SNAPSHOT_CONFLICT) {
saveResolveConflictGameData = true;
Log.i(TAG, "Konflikt aufgetreten");
Snapshots.OpenSnapshotResult resolveResult = null;
Snapshot snapshot = result.getSnapshot();
Snapshot conflictSnapshot = result.getConflictingSnapshot();
Snapshot mResolvedSnapshot = null;
mResolvedSnapshot = snapshot;
SnapshotMetadata s1Meta = snapshot.getMetadata();
SnapshotMetadata cMeta = conflictSnapshot.getMetadata();
// resolveConflict and get new merged Parser Object
//
Parser conflictParserTemp = savedGame.resolveConflict(snapshot, conflictSnapshot);
if ( conflictParserTemp == null) {
Log.e(TAG, "savedGame.resolveConflict(snapshot,conflictSnapshot) Error");
return result;
}
//
// wurde schon ein conflict behandelt ?
//
if ( conflictParser != null ) {
// merge previous Conflict with this conflict
conflictParser.merge(conflictParserTemp);
} else {
// set first conflict Parser
conflictParser = conflictParserTemp;
}
Log.i(TAG, String.format("Games.Snapshots.resolveConflict() Step %d", retryCount));
resolveResult =
Games.Snapshots.resolveConflict(
activity.mGoogleApiClient, result.getConflictId(), mResolvedSnapshot).await();
if (retryCount < MAX_SNAPSHOT_RESOLVE_RETRIES) {
// Recursively attempt again
return processSnapshotOpenResult(resolveResult, retryCount);
} else {
// Failed, log error and show Toast to the user
String message = "Could not resolve snapshot conflicts";
Log.e(TAG, message);
Toast.makeText(activity.getBaseContext(), message, Toast.LENGTH_LONG).show();
return resolveResult;
}
}
// Fail, return null.
return null;
}
The Error is that if I load savegame "coins" I become all conflicts from other savegames.
I see it here.
SnapshotMetadata s1Meta = snapshot.getMetadata();
SnapshotMetadata cMeta = conflictSnapshot.getMetadata();
The Snapshot for korrekt coins savegame show this:
SnapshotMetadataEntity{Game=GameEntity{ApplicationId=520840013521,
DisplayName=Crush me, PrimaryCategory=Simulation,
SecondaryCategory=null, Description=hallo, DeveloperName=steffen
höhmann, IconImageUri=null, IconImageUrl=null, HiResImageUri=null,
HiResImageUrl=null, FeaturedImageUri=null, FeaturedImageUrl=null,
PlayEnabledGame=true, InstanceInstalled=true,
InstancePackageName=cherry.de.wubbleburst, AchievementTotalCount=0,
LeaderboardCount=0, RealTimeMultiplayerEnabled=false,
TurnBasedMultiplayerEnabled=false, AreSnapshotsEnabled=true,
ThemeColor=00456B, HasGamepadSupport=false},
Owner=PlayerEntity{PlayerId=113260033482974102226,
DisplayName=shoehmi, HasDebugAccess=false, IconImageUri=null,
IconImageUrl=null, HiResImageUri=null, HiResImageUrl=null,
RetrievedTimestamp=1454003980807, Title=Anfänger,
LevelInfo=com.google.android.gms.games.PlayerLevelInfo#1e1b36},
SnapshotId=drive://113260033482974102226/520840013521/coins,
CoverImageUri=null, CoverImageUrl=null, CoverImageAspectRatio=0.0,
Description=null, LastModifiedTimestamp=1454004003382, PlayedTime=-1,
UniqueName=coins, ChangePending=true, ProgressValue=-1}
drive://113260033482974102226/520840013521/coins
and the snapshotData:
timestamp;coins#1453929273252;100#1453929280956;-70#230179;70
but he shows me savegame snaphot from "level" savegame as conflicted Snapshot:
levelId;points#1;3241#2;9634
and the Conflict Snapshot Metadata say it is a "coins" savegame:
SnapshotMetadataEntity{Game=GameEntity{ApplicationId=520840013521,
DisplayName=Crush me, PrimaryCategory=Simulation,
SecondaryCategory=null, Description=hallo, DeveloperName=steffen
höhmann, IconImageUri=null, IconImageUrl=null, HiResImageUri=null,
HiResImageUrl=null, FeaturedImageUri=null, FeaturedImageUrl=null,
PlayEnabledGame=true, InstanceInstalled=true,
InstancePackageName=cherry.de.wubbleburst, AchievementTotalCount=0,
LeaderboardCount=0, RealTimeMultiplayerEnabled=false,
TurnBasedMultiplayerEnabled=false, AreSnapshotsEnabled=true,
ThemeColor=00456B, HasGamepadSupport=false},
Owner=PlayerEntity{PlayerId=113260033482974102226,
DisplayName=shoehmi, HasDebugAccess=false, IconImageUri=null,
IconImageUrl=null, HiResImageUri=null, HiResImageUrl=null,
RetrievedTimestamp=1454003980807, Title=Anfänger,
LevelInfo=com.google.android.gms.games.PlayerLevelInfo#1e1b36},
SnapshotId=drive://113260033482974102226/520840013521/coins,
CoverImageUri=null, CoverImageUrl=null, CoverImageAspectRatio=0.0,
Description=null, LastModifiedTimestamp=1454004003382, PlayedTime=-1,
UniqueName=coins, ChangePending=true, ProgressValue=-1}
drive://113260033482974102226/520840013521/coins
Why only if conflict occured and without conflicts its running correct and
save / load correct??
Please Help me???
sorry for my english ;)
I'm currently studying ways to sync data between an Android device to Google Drive and stumbled upon the sample code at https://github.com/googledrive/android-quickeditor , but I'm encountering an issue when opening an existing file and then tapping on 'Save Changes'. Basically the problematic code is here:
#Override
protected com.google.android.gms.common.api.Status doInBackground(DriveId... params) {
R await;
DriveFile file = params[0].asDriveFile();
PendingResult<DriveContentsResult> openDriveContentsResult = file.open(mClient, DriveFile.MODE_WRITE_ONLY, null);
if (!openDriveContentsResult.await().getStatus().isSuccess()) {
return openDriveContentsResult.await().getStatus();
}
Changes changes = edit(openDriveContentsResult.await().getDriveContents());
PendingResult<MetadataResult> metadataResult = null;
PendingResult<com.google.android.gms.common.api.Status> closeContentsResult = null;
if (changes.getMetadataChangeSet() != null) {
metadataResult = file.updateMetadata(mClient, changes.getMetadataChangeSet());
if (!metadataResult.await().getStatus().isSuccess()) {
return metadataResult.await().getStatus();
}
}
if (changes.getDriveContents() != null) {
closeContentsResult = changes.getDriveContents().commit(mClient, null);
closeContentsResult.await();
}
return closeContentsResult.await().getStatus();
}
The exact line throwing the error is:
Changes changes = edit(openDriveContentsResult.await().getDriveContents());
The Error is java.lang.IllegalStateException: Result has already been consumed. I referred to the PendingResult class reference and it clearly states that "After the result has been retrieved using await() or delivered to the result callback, it is an error to attempt to retrieve the result again." Fair enough I guess.
The problem is, how am I supposed to fix this properly? I have commented out
if (!metadataResult.await().getStatus().isSuccess()) {...}
for now to avoid calling await() twice (for sake of debugging), BUT (as expected) then results into the same error at
return closeContentsResult.await().getStatus();
So...how can I fix this? Pretty please?
Solved. For anyone that's getting frustrated over this:
#Override
protected com.google.android.gms.common.api.Status doInBackground(DriveId... params)
{
R await;
DriveFile file;
Changes changes;
PendingResult<DriveContentsResult> openDriveContentsResult;
DriveContentsResult driveContentsResult;
PendingResult<MetadataResult> updateMetadataResult;
MetadataResult metaDataResult;
PendingResult<com.google.android.gms.common.api.Status> commitResults;
com.google.android.gms.common.api.Status closeContentResults = null;
file = params[0].asDriveFile();
openDriveContentsResult = file.open(mClient, DriveFile.MODE_WRITE_ONLY, null);
driveContentsResult = openDriveContentsResult.await();
if (!driveContentsResult.getStatus().isSuccess())
{
return driveContentsResult.getStatus();
}
changes = edit(driveContentsResult.getDriveContents());
updateMetadataResult = null;
commitResults = null;
if (changes.getMetadataChangeSet() != null)
{
updateMetadataResult = file.updateMetadata(mClient, changes.getMetadataChangeSet());
metaDataResult = updateMetadataResult.await();
if (!metaDataResult.getStatus().isSuccess())
{
return metaDataResult.getStatus();
}
}
if (changes.getDriveContents() != null)
{
commitResults = changes.getDriveContents().commit(mClient, null);
closeContentResults = commitResults.await();
}
if (null == commitResults)
{
return commitResults.await().getStatus();
}
else
{
return closeContentResults.getStatus();
}
}