-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optimization for very slow processing after downloading folder content #5150
base: master
Are you sure you want to change the base?
Conversation
Signed-off-by: Joris Bodin <[email protected]>
Signed-off-by: Joris Bodin <[email protected]>
Signed-off-by: Joris Bodin <[email protected]>
avg 15s to 0.2s for 900 new files ! Remove unnecessary double check and very old method not used (since 2015) Signed-off-by: Joris Bodin <[email protected]>
Signed-off-by: Joris Bodin <[email protected]>
Before avg 6s (for avg new 1000 files) now 0.2s with applyBatch Signed-off-by: Joris Bodin <[email protected]>
Signed-off-by: Joris Bodin <[email protected]>
And readd localFile = mStorageManager.getFileByPath(updatedFile.getRemotePath()); And optimize insert virtualFile Signed-off-by: Joris Bodin <[email protected]>
Rename file to ocFile for OCFile object to avoid ambiguity with the objects of type File Remove unUsed variable != null (i check method caller) Add != null is necessary Rename to camelCase Consistency in the code Signed-off-by: Joris Bodin <[email protected]>
Signed-off-by: Joris Bodin <[email protected]>
Signed-off-by: Joris Bodin <[email protected]>
Signed-off-by: Joris Bodin <[email protected]>
Signed-off-by: Joris Bodin <[email protected]>
Signed-off-by: Joris Bodin <[email protected]>
Signed-off-by: Joris Bodin <[email protected]>
Signed-off-by: Joris Bodin <[email protected]>
Signed-off-by: Joris Bodin <[email protected]>
Signed-off-by: Joris Bodin <[email protected]>
Signed-off-by: Joris Bodin <[email protected]>
Codecov Report
@@ Coverage Diff @@
## master #5150 +/- ##
=========================================
Coverage ? 18.71%
Complexity ? 3
=========================================
Files ? 392
Lines ? 34273
Branches ? 5011
=========================================
Hits ? 6414
Misses ? 26863
Partials ? 996 |
Issues
======
+ Solved 15
- Added 28
Complexity decreasing per file
==============================
+ src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java -3
See the complete overview on Codacy |
ProviderTableMeta.FILE_CONTENT_TYPE + " != 'DIR' AND " + | ||
ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + | ||
ProviderTableMeta.FILE_PATH + " LIKE ?"; | ||
selectionArgs = new String[]{account.name, parentPath + "%"}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issue found: Avoid instantiating new objects inside loops
|
||
List<OCFile> files = getFolderContent(folder, false); | ||
ArrayList<String> selectionArgsList = new ArrayList<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issue found: Avoid instantiating new objects inside loops
ArrayList<String> selectionArgsList = new ArrayList<>(); | ||
selectionArgsList.add(account.name); | ||
selectionArgsList.addAll(listPathString); | ||
String[] selectionArgs = selectionArgsList.toArray(new String[0]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issue found: Avoid instantiating new objects inside loops
if (cursor != null) { | ||
if (cursor.moveToFirst()) { | ||
do { | ||
OCFile ocFile = new OCFile(cursor.getString(cursor.getColumnIndex(ProviderTableMeta.FILE_PATH))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issue found: Avoid instantiating new objects inside loops
} else { | ||
file = null; | ||
ocFile = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
saveFile(newFile); | ||
} else { | ||
throw new IllegalArgumentException("Saving a new file in an unexisting folder"); | ||
private boolean isFileExists(ArrayList<OCFile> filesExists, OCFile ocFile) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return contentProviderResults; | ||
} | ||
|
||
private ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | ||
where = ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + | ||
ProviderTableMeta.FILE_PATH + " = ?"; | ||
selectionArgs = new String[]{account.name, parentPath}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issue found: Avoid instantiating new objects inside loops
* | ||
* @param folder | ||
* @param updatedFiles | ||
* @param filesToRemove | ||
*/ | ||
public void saveFolder(OCFile folder, Collection<OCFile> updatedFiles, Collection<OCFile> filesToRemove) { | ||
public void saveFolder(OCFile folder, ArrayList<OCFile> updatedFiles, Collection<OCFile> filesToRemove) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && operation.isInsert()) { | ||
ContentValues contentValues = operation.resolveValueBackReferences(results, i); | ||
Uri newUri = insert(database, operation.getUri(), contentValues); | ||
results[i] = new ContentProviderResult(newUri); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issue found: Avoid instantiating new objects inside loops
|
||
ArrayList<String> listPathString = new ArrayList<>(); | ||
|
||
int totalSize, processSize, loopSize; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (new File(path).delete() && MimeTypeUtil.isMedia(file.getMimeType())) { | ||
if (ocFile.isDown()) { | ||
String path = ocFile.getStoragePath(); | ||
if (new File(path).delete() && MimeTypeUtil.isMedia(ocFile.getMimeType())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issue found: Avoid instantiating new objects inside loops
loopSize = Math.min((totalSize - processSize), 998); | ||
|
||
listPathString.clear(); | ||
StringBuilder inList = new StringBuilder(folderContent.size() * 2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issue found: Avoid instantiating new objects inside loops
return deleted; | ||
} | ||
|
||
private int deleteFiles(Uri uri, String where, String[] whereArgs) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (i > 0) { | ||
inList.append(","); | ||
} | ||
inList.append("?"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ContentValues contentValues; | ||
// prepare operations to insert or update files to save in the given folder | ||
for (OCShare share : shares) { | ||
contentValues = new ContentValues(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issue found: Avoid instantiating new objects inside loops
return executeQuery(uri, null, selection, selectionArgs, null, errorMessage); | ||
} | ||
|
||
private ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations, String errorMessage) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
APK file: https://www.kaminsky.me/nc-dev/android-artifacts/12302.apk |
Codacy340Lint
SpotBugs (new)
SpotBugs (master)
SpotBugs increased! |
private static final String AND = "=? AND "; | ||
private static final String FAILED_TO_INSERT_MSG = "Fail to insert insert file to database "; | ||
private static final String AND = " = ? AND "; | ||
private static final String FAILED_TO_UPDATE_MSG = "Fail to update file to database "; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that it same String is used for insert and update.
Changing this to "update" is as wrong as using only "insert".
So either, we use both and change it accordingly, or we generify the error message and us "failed to insert/update file to…"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With my refactor, I've removed a lot of code duplication. So FAILED_TO_INSERT_MSG was used more than once.
However, for update, it is used in 2 places. So I changed that message to this.
We can use "insert/update", but it will be less exact.
"Fail to insert" is used in the insertFile method, if you want to merge this :D
Signed-off-by: tobiasKaminsky <[email protected]>
Signed-off-by: tobiasKaminsky <[email protected]>
Signed-off-by: tobiasKaminsky <[email protected]>
Signed-off-by: tobiasKaminsky <[email protected]>
Signed-off-by: tobiasKaminsky <[email protected]>
Signed-off-by: tobiasKaminsky <[email protected]>
remove non needed exceptions Split off of #5150 Update translations Update Nextcloud Android library
Hello,
Fixes #5086
My tests were performed with a folder of 1044 files and Galaxy S8
Benchmark
On master:
Insert (first download):
50 seconds.
Update (refresh):
20 seconds
With this pull request:
Insert:
20 seconds
Update:
6 seconds
There are still 2 things to improve

First, the doubleCheck in FileContentProvider:
This check is here because saveFolder (in FileDataStorageManager) is sometimes called twice for an insert.
I've found two cases where that happens, one of which is simple to fix.
The first time the application is started, the WRITE_EXTERNAL_STORAGE permission is requested. When the user accepts, syncAndUpdateFolder is called in onResume and in onRequestPermissionsResult.
Delete syncAndUpdateFolder in onRequestPermissionsResult is solution.
Also the first time the application is started, if refreshing the folder, syncAndUpdateFolder is called twice
The solution would be to block the simultaneous call of saveFolder.
Benchmark
If doubleCheck is removed:
Insert (first download):
13 seconds.
Update (refresh):
6 seconds
The other thing to improve is this check #4647
(It is discussed in #5086)
I haven't found an optimization solution for this case. 😞
Benchmark
But if we remove it
Insert (first download):
1 seconds.
Update (refresh):
5 seconds
I also took the time to refactor the FileDataStorageManager class, I have removed the inconsistencies and duplications that have accumulated since the beginning. 2298 lines to 1887 lines
It allowed me to better understand the code and better optimize it.
Next week, we're thinking of coming to hackweek in Berlin. Will you be there?