Currently i'm developing a dialer-app for the Android OS.
It shows callogs, contacts in ListViews.
A SearchView is used to search contacts by: surname, name, nickname and all it's numbers. If the search string represents a substring of the four key-kinds, the list shall be shrinked to the matching entries.
i'm using CursorAdapter and CursorLoader to fill the list.
This is very important for the sake of performance.
Till that point everything is working and absolute clear.
Now the problem:
I need to join 2-3 tables from default contacts db (contacts2.db),
in one single sql query.
So my app should not use it's own, private db.
But existing ContentProviders do not offer acces to more than one table.
--> PROBLEM!
Implementing my own ContentProvider, it is possible to do joins utilising the SQLiteQueryBuilder.
But at a first glimpse that seems to work only for private databases.
When I try to directly access the contacts2.db, located in:
/data/data/com.android.providers.contacts/databases/contacts2.db
I get following error:
Failed to open database '/data/data/com.android.providers.contacts/databases/contacts2.db'.
android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (Sqlite code 14): Could not open database, (OS error - 13:Permission denied)
Which is somehow expected, because every app has it's own storage, using an appropriate uid in the underlying linux filesystem.
So, how can I get read-acces to this database?
Can I request that db-object somehow from the android system or utilizing the Context object? Or can I gain those acces-rights somehow?
Would it be possible to use the same AUTHOTITY_NAME as the contacts App does?? Like this:
com.android.providers.contacts
I'm not interested in joining columns using java code, that's UGLY, EXTREMELY SLOW, and I need a Cursor instead of a List or similar stuff anyway
I don't need help in SQL
I don't need help in how to implement android stuff
All you Android-Pros out there - is that possible at-all?
Or is the android api really that crappy?
Do i really have to "join" the needed info by using CursorJoiner and generate
somehow a Cursor object from the first one?
Thank you very much in advance, folks!
You don't. You can't even assume that's the name of the contact db on a given device. The only way to access the contacts db across all devices is to use the ContentResolver.
It sounds like you're looking for Match a Contact By Any Type of Data as shown by the Retrieving a List of Contacts official Android lesson.
You'll need to follow all 4 steps:
Request Permission to Read the Provider
Match a Contact by Name and List the Results
Match a Contact By a Specific Type of Data
Match a Contact By Any Type of Data
Because the 4 step is based on the code created by the previous steps.
Basically, it's using a built-in search feature in Android's Contacts framework: Contacts.CONTENT_FILTER_URI:
Use Contacts.CONTENT_FILTER_URI as the base URI, and append your
search string to it by calling Uri.withAppendedPath(). Using this URI
automatically triggers searching for any data type
Related
I just followed tutorial in developer.android.com to create sync adapter to provider feature "synchronization between local db with server db", and after bloody trial and error i managed to make it work (onPerformSync has called successfully).
And now for next step to create sync feature, from what i have read in several articles, I need to create a content provider. I already read https://developer.android.com/guide/topics/providers/content-provider-basics.html but I still dont get it how does it work.
from this link https://developer.android.com/guide/topics/providers/content-provider-basics.html, it raised several questions in my head:
what table they are talking about? are they talking about sqlite table or some "another" table?
content://user_dictionary/words what uri is this? is this uri to table file where sqlite stored? if it's, how do I know mine? I mean where did my sqlite store table that I created?
from what I read (if i got it right), ContentProvider just like a repository. do they have same functionality? I already created my repository using anko https://gist.github.com/mockiemockiz/a552a669d28a3c90c144bc1542b86a5e , can I use that code / convert that code to be ContentProvider that able to tell sync adapter the data has changed?
I just followed tutorial in developer.android.com to create sync adapter to provider feature "synchronization between local db with server db", and after bloody trial and error i managed to make it work (onPerformSync has called successfully).
FWIW, SyncAdapter is not especially popular.
what table they are talking about?
The word "table" shows up 40 times on that page. We have no way of knowing which of those 40 concerns you, and they use the term in multiple ways.
what uri is this?
That is a Uri pointing to a collection of data ("table") in the user_dictionary ContentProvider.
is this uri to table file where sqlite stored?
That is for the developer of the ContentProvider to decide. The ContentProvider API does not stipulate where the data is stored. It could be stored in SQLite, or a JSON file, or whatever. Convention says that a collection of data exposed by a ContentProvider maps to a SQLite table or view, but that is not required.
if it's, how do I know mine?
You know it is your ContentProvider if you used user_dictionary as your authority (see android:authorities in the <provider> element in the manifest).
I mean where did my sqlite store table that I created?
That is up to you. ContentProvider has nothing to do with SQLite, unless you write code that ties a ContentProvider implementation to SQLite.
ContentProvider just like a repository
Not really, at least in terms of how I use the term "repository". A ContentProvider is a wrapper around some data storage mechanism, to allow outside parties to have controlled access to that data.
can I use that code / convert that code to be ContentProvider that able to tell sync adapter the data has changed?
That would be rather difficult. This is one of the reasons why few developers use SyncAdapter.
I have a phone application that uses a database of words and tests a user to see which words they know. I have a SQLite database with the words that I populate using a console application and this is then deployed as a resource to phones etc.
When the user runs the application then it stores pass fail data in the same database but in different tables.
When I update the application a fresh copy of the words database is installed on the phone and all the user data is lost.
How is this typically handled? Do phone applications that use SQLite have multiple databases with one being used to store user data and the other holding data which can be brought in when the application is first installed or updated?
If multiple databases are used then is it possible to create a look up from one database to the other?
Thanks in advance for any help, advice or links that point me in the right direction.
I would use a file (JSON, or plain text) to ship the words with the app. Then, when the app runs, it reads that file and adds the new words to the database. This won't affect the other tables.
Instead of having to deal with that, we hard code the values into a static method in code. Then at runtime, we see if there is any data in the table and, if not, we grab the hard coded data and do an insert.
In your case, I would also just add a version number of some kind so then, if the version was lower or the table was empty, you do a delete all and then insert your new static data.
Assume that I want to update dirty flags of some contacts linked to a group.
To do this,
From 'data' table, records having the group ID should be queried.
Should update using contact ID in the fetched records.
But if I can use SQL, it can be done with one SQL statement.
Is it possible?
Thanks in advance.
No, I don't think so.
See, Android API doesn't let you access the SQLite database behind the contacts (although it is there) but rather abstracts data access by means of ContentProviders (And there's a good reason for that: giving developers access to the SQLite db would be way too insecure -- any app with proper permissions could e.g. drop the contact tables and thus cause major malfunctions of other apps)
It's not much more complex to run update statements on those though (well, apart from the fact that SQL statements are kind of broken down into methods and parameters), the ContentProvider class has .update() method for just that, the tricky part is the WHERE part of the call, you'll have to take a good look at the ContactsContract class.
Me and my Android team have a problem. We have an app that present the user's contact book, with extended information.
Current setup
Our app reads the Contacts Provider of the Android OS. Sends this information to our Server that calculates a couple of necessary fields for us. This information is later fetched by our app and we save this information in an SQLite database. What we end up with in our database is two tables. One with all Numbers and all the extra information that the server calculated for us. The other table is one with all Contacts (one contact can have multiple numbers). This Contacts table was created only for performance; we can have a Cursor selecting all rows in this Contacts table in our CursorAdapter when presenting the contact book for the user.
Hence, when presenting the contact book to the user, we only need to read from our own SQLite database and only one table (e.g. no JOINs).
The main problem
There is a lot of syncing going on. Since, data is duplicated, we need to check for adds/changes/removes and need to sync all the f-ing time. Moreover, when we now are about to change a particular thing in our presentation layer, we need to change our Contacts table to include this particular information.
Priority for us
1st: Performance when presenting the contact book to the user.
2nd: Code maintainability.
Hence, do not comment "Do not duplicate data--it's the root of all problems". It is more important to us that the user does not have performance issues than if we as developers have to spend some extra time writing good synchronization algorithms.
Solutions?
I don't know why, but I've always thought that a CursorAdapter (reading all rows from one table) is performing much better than an ArrayAdapter with a List of objects (held in memory). Anyone know if this is true? Because one solution which will help us at least half the way is to, on start up, join the Contacts Provider (native contact book) and our extended information, save this in a List in memory and present this with an ArrayAdapter.
Creating your own Content Providers? I know little about creating your own content provider. Anyone tried to create a content provider that extend the information of the native contact book and join these. Maybe with the implementation of this interface: ContactsContract.DataColumnsWithJoins? Anyone tried anything similar? How's the performance when presenting this information in a CursorAdapter?
Please ask for any more information I might have forgot and I will update the question!
Thanks a lot in advance for all helpful tips and solutions!
I have come to conclusion (working on my app JReader which relies on fast DB operations a lot) that SQLite in Android is pretty quick as in other platforms but there are some Android specific issues.
Some advises regarding database performance and the questions you have asked:
Content providers are mostly useless if you are not planning sharing your data through them. But they provide at least 2 advantages. First you get data change notifications and you cursor gets updated automatically, and the second and important one: CursorLoaders require a Content provider, and if the performance matters to you, I would strongly suggest using them for loading your cursor;
Accessing Collections is much faster that accessing database, but it is a double work, since you have to persist the data anyway, and DB access is quite fast even for fetching data for a super-fast scrolling list and especially from a single table, it shouldn't be a problem;
Design your DB properly (with JOINS, indexes, etc) :) BUT DO NOT USE JOIN QUERIES IN CURSOR QUERIES! I have had lots of performance issues with that on multiple Android platforms (including 4.0+), not always though. The way I access joint tables is by simply getting foreign key first and then querying child table.
In my experience, I've had the situations when the DB performance was very poor, but in the end I have always managed to tweak the code and as a result would gain 10-100 fold performance increase. So keep profiling you DB code, and you will definitely achieve the desired performance.
Dan's 2nd comment is true .. Android Uses Cursor Window below the screens to Cache data from the Cursor (approx 1M ) . see Android docs on Cursor Window, Which is how the Cursor Adapter is quicker .
If you do not prefer joining two tables seperately you could consider a CursorJoiner which is faster, This can go to your custom contentProvider where one of the provider is pointing to the Contacts and returns a Cursor , similarly the second one points to your own table and returns a cursor . The cursorjoiner can join both these cursors .
(Though it is a complicated process )
I am making a list activity that will contain 'achievements'. Each achievement is a record in a sqlite db in the app. In each record, I have a column with a query string stored. For each record in the db, I am using the query string against another user generated db to determine which achievements have been accomplished...
The query strings I have been using are working correctly in my sqlite manager program... however in my app, it appears that the query is being ignored and returns the entire user generated db. I'm sure there is the potential for other general errors (like null query string returned etc) but I couldn't find any, and right now I don't have my code here to post.
Are there any pitfalls I am falling into by executing a query from a string extracted from the achievement db? This was the most straightforward way I could envision doing the achievements without a whole lot of if-then clauses.
EDIT: In the end I found an error in the call, passing the wrong argument. Pitfall in the end was working too bleary-eyed.
So basically your data is denormalised. This makes it harder to change, if you ever need to change the format for example. It will also be harder to do a variety of things with your data, e.g. query the number of people with a given achievement.