Consider the following scenario. A company releases many apps. And they want some data to be shared across all these apps. Any of these app can create or read these data, just like a common database. So company decided to create an android library which does this purpose. I searched for a few days and my analysis are given below.
SharedPreferences- not recommended and is deprecated. It does not serve the purpose too. All other apps need to know the package name of the app that created the data to create PackageContext. Here this is impractical as any app can create/update/read data and it is not possible to say who is who.
ContentProviders - This does not work for me. The reason being ContentProviders has to be present in each app. There can not be 2 content providers with same name in a device. In addition to that, ContentProviders are basically meant for one app creates data and other apps subscribe to it using Content_Uri.
Network connection - We do not want to do store data in any server.
External storage - This is the only option remaining. Should I go for this?
And interestingly the data has to be secured as well which is nowhere supported in any of the storage options.
Note: For iOS, we use keychain to implement the same functionality
Understanding the problem on Android
Ironically, due to the intense sandboxing on iOS, there's a straightforward way to make this happen there (App Groups) provided the apps that need to share data are all by the same developer. Likely because Android is more flexible on security, this actually ends up being a more difficult problem there. The Android team have so far not seen fit to provide a convenient and secure way to specifically share this kind of data because there's a low security workaround.
That said, there are plenty of ways to share data between applications without involving the cloud.
SharedPreferences
The original question states that SharedPreferences are deprecated. This isn't true, as far as I can tell, however the MODE_WORLD_READABLE and MODE_WORLD_WRITABLE contexts are deprecated which makes this approach subject to not working in the future. The mode has been deprecated for quite some time, though - since Android 4.2 (2012). There's no threat in the current Android docs to suggest they're actually phasing it out (sometimes deprecation just means "this isn't a great idea" not "this is going to be removed"). I suspect the lack of a more secure OS-level direct alternative for application data sharing at the settings level is probably the reason for preserving it in a state of deprecation for the last 5 years.
File Access
The simplest and most common way I'm aware of to implement data sharing between applications on Android is to simply request file access on the device and create a shared location on external storage for this data. (Don't be confused by the "external storage" designation - this is just how Android refers to shared data. It doesn't necessarily refer to an SD card.) You give the file a unique name, and you store it somewhere that your apps know where to look for it. Best way to get that path is something like:
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
The obvious problem with this is security. While not deprecated by the OS, it introduces the same problem that the Android docs list as the reason for deprecating MODE_WORLD_* - it's inherently insecure and opens up potential exploits in your application.
You're placing your information where everything has ready access to
it.
You're asking for permissions that your app may not otherwise need.
You're reading files that you can't verify the origin of.
If your application isn't handling any sensitive data, maybe this doesn't matter to you (it might to your users). If you're planning to read data from those files, you should ensure you're providing maximum validation for that data before parsing. Check the size of the file, validate the formatting, etc.
Creating your own service
You could always create a Service or an IntentService. (There are subtle differences between the two, but IntentService is a subclass of Service that runs in a Worker thread while Service interrupts the main thread. IntentService also implements Intent support which provides the most straightforward interapplication communication on Android).
This service has its own private storage, for which it has full read/write access, but nothing else does. This service then provides an interface to receive Intents from other apps, and to return results (as Intents) to those apps. This is an extremely friendly way to implement interapplication data while maximizing data privacy and security of that data. If outlying apps mostly need to request very basic information from a central application, this is your entry-level option.
Implementing a BroadcastReceiver
Along the same lines is the BroadcastReceiver class. Depending on what sort of data you're intending to share between applications, and how familiar those applications may be with your specific approach, this another possibility. Again, you'll be managing the shared data under one application's private storage. Communication is done by Intents, so it's similar to an IntentService - except that applications may communicate with a BroadcastReceiver by issuing systemwide events (that is, they don't need to be explicitly communicating with your app or service - they're shouting out the world for a piece of info, and expecting an answer.)
Creating a ContentProvider
The Original Post seems to misunderstand what a ContentProvider is and how it works. You have to think of this type of item like you would a cloud solution - even though it's local to your device. Every app doesn't need a ContentProvider - they all need to communicate with a ContentProvider, and that ContentProvider maintains, updates and returns data.
This is probably the most "Android-y" solution for this particular usecase and offers the greatest expandability. You implement an independent process that handles data storage and responds to other applications. It is, however, a more evolved solution - and as such may be more a more challenging endeavor. If you need a real database service, rather than a fairly simple request/response type service, ContentProvider seems to be the best choice.
Seems like what you need is shared user id.
It allows application sandbox to be shared across multiple android applications if they are all signed by the same signature.
But, watch out for gotchas!
Yes. Probably using same path in external storage for all applications is the best way. A common portion of code could be used to know whether database there exists or not and therefore open or create new one. For security I recommend always to use 'user' and 'password' when connecting to DB, but if you think it is not sufficient I advise you to see this: http://www.hwaci.com/sw/sqlite/see.html
Related
Background: we are porting an enterprise system to have android clients. The architecture for windows and html is based around a core library that does the hard business logic but no user interaction at all, and we use programs or single page web apps to provide the user interface and simply call the core API library to actually do stuff.
The "core" is implemented as a shared library on windows and built into each app. If we mirror this and use a java library, we need to share files using external storage, which is a not permitted as data needs to be reasonably secure. (Nb data is binary data, not Sql database, in case that is relevant)
So we thought about using a bound service, and using intents, content provider etc, but it seems (from googling) we must then distribute the background service separately the user interface app, but this seems terrible experience for new users. However, a bound service seems ideal from all other angles.
We also cannot guarantee which apps a user might download, we will have at least 10 individual apps all doing logically different things, but referencing similar data.
In brief:
lots of individual apps all wanting access to same data
no control over which apps are downloaded
using external data is not permitted as data should be semi secure
using sqllite might not work as data is long binary chunks ( eg 3Mb plus ). (Ref: How to share data across a group of applications in Android )
some data files are big and do not want every app to download a private copy
some data changes dynamically, say every 15min
core business logic is big and complex, cannot be distributed in source form, lib/jar ok though.
the windows solutions all use network IO to an application server, but we want to avoid as much network traffic as possible by storing data locally.
How can we bundle a bound service in each and every user interface app we distribute? Or is there a different way to approach this whole design?
I think that there is a few number of options that you can explore:
1) I never have done this before though this seems possible as Android is package based.
First you need to use the same main package across all your apps though each app must be in a separated sub package, e.g. : main -> au.com.myapp.main and the app actually have it's first screen on app1 -> au.com.myapp.main.app1 .
Create on your main app a method(s) that will look for those extra packages (within your project), as it find something you create a trigger that will display a item on the menu. Each app should have the same main packages and main activity, as it will be responsible for enable the user have access to the others and all of them can share the same preferences, files folders and Database.
When installing the same packages should be overrides though those different ones should keep intact. You should have all the 'main' classes for each app, not the real main one declared on your manifest (that will be quite big depending on the amount of activities in all your apps) with those packages.
2) You can using Spongy Castle, create a shared zone (folder) where you create the DB and write your settings or files, encrypting everything with a key (strong one or using RSA) that might be made by the user or provided once for your company at the very first run. You must decide how to handle this.
3) You also can use the share id in all your apps and each app before run perform look up for all packages (it's possible to do) to know and if and what packages exist to check if there is a DB with data in that package.
4) Not really nice though create a background service that keep updated all tables in all apps (sharing id or using content provider), you can user AlarmManager to trigger it in intervals rather keep it on at all times and you have all apps.
My last project had a similar requirement though as the user had to login to do anything, I opted for the option 3 and each data pertinent exclusively to each app went the app DB.
I hope this helps.
Is there anyway I can do something like this
all data I/O functions are written in a library package
data can only be shared between apps with this library (optional)
every apps with this library can initiate the "DB" at first time, and later-installed apps can access the same "DB"
I thought ContentProvider is a perfect solution for me, but it seems that condition 3 is impossible.
any suggestion plz?
all data I/O functions are written in a library package
OK.
data can only be shared between apps with this library (optional)
Perfectly fine with the proper permissions for your provider (signature).
every apps with this library can initiate the "DB" at first time, and later-installed apps can access the same "DB"
I thought ContentProvider is a perfect solution for me, but it seems that condition 3 is impossible.
It's up to you to code the underlying structure of your data. Since you already assumed that the provider will belong in a dedicated library package, a possible solution is:
Implement your provider in package com.mysuite.library.
Publish this app in the Play Store.
Make client apps A, B and C.
Publish them in the Play Store.
Require your users to download this library package whenever apps A, B or C can't find com.mysuite.library installed.
However, if you don't want to provide a central package, I believe you will need to serve a provider in each of your own apps, with different authorities (to avoid CONFLICTING_PROVIDER error). Upon initializing each client, you first check if there is another provider in your namespace (com.mysuite.provider*), assuming you either know all possible authorities you are going to create and/or iterate among them when searching (com.mysuite.provider1, 2 etc.).
However, this proposition may create problems with custom backups (say, if only one of the clients is backed up), which will force the re-creation of data. It certainly has caveats and is definitely more complex (ugly, IMHO), but it can be made to work.
Personally, I'd stick with option 1 (library package). I don't see users complaining when downloading required library packages for apps.
It's just an architectural decision, really.
There are only four ways to maintain shared data:
You have a single APK that stores the data and makes it available to other apps. If this app is uninstalled the data is gone.
Every app maintains its own copy of the data but synchronizes changes to the others.
The data is stored on the SD card. This is generally a poor solution though several apps do it.
The data is stored on a server and is only accessible when the device has a network connection.
Once you've chosen where you want your data to reside, you can transfer it around by any number of mechanisms.
ContentProvider is a good choice if you are not storing the data on SD card or on a server, but you want to be able to transfer the data to apps that don't know it exists using content: URI's.
If you have no need to ever share any of this data with anyone outside your particular suite of apps, and your data is not structured as a database, you may prefer a simpler transport.
So I have an application that logs medical data entered by user. I need to add functionality for backing up and restoring that data in case user changes their Android phone OR switches to and iPhone (there is an iPhone version of this app, which I have limited visibility into).
The data is currently stored inside a SQLite DB on the device.
I need help figuring out cleanest/easiest way to do this. So far I am thinking about these options:
add UI to email DB file to + UI to accept a DB file and copy it over current DB. Seems hacky.
create a web service which will store user data and sync on the background. I would need to build sync process and introduce some kind of account system. This seems like quite a bit of work, though likely the most flexible solution in the long run.
switch to Google Calendar as my data storage (data is essentially a set of event entries anyway). This would probably be most seamless, but iPhone option is out of the window.
Are there other pros/cons to these options that I am missing? Or perhaps there are some standard solutions to this?
Option #1 is filled with a lot of problems. Sideloading databases from files coming from external sources is no easy task. I wouldn't recommend it.
Option #3 should be a possibility - but it require the user to set up a Google account which (imho) shouldn't be required. Furthermore the addition of a new calendar and/or a lot of events wouldn't be many users favorite feature. Personally I would hate it.
The second option seems like to most promising. You can build it as simple as you like. You could even make it require only a simple identification code for backing up - or only provide "live" synchronization. It is true that you need some sort of server and accounting system - but these things come as standards and servers are cheap. This option would be the one of my choice.
So I come mostly from a web background and I'm trying to learn the architecture of android apps. Trying to grasp a proper understanding of content providers.
What I think I'm understanding is that content providers are pretty much your middle man between the activity/services and your data (DAO of sort). I also think that content provider are also to provide other applications access to your data (almost like a web service?)
What I'm not really getting is, what if you did not need this data exposed to other applications? Do I need to bother with content providers and all the uri defining etc? If not, is there a name for this ... pattern? (or lack of) Or am I better off just using ContentProviders and just accept the added benefit of exposing this data to other apps?
I know eventually I will want to sync data between the app and a external database. I saw a google IO presentation supporting the pattern of using a content provider for RESTful communication. But for the time being, I'm just trying to get comfortable with basic static data. Then hopefully swap it out to data from a REST service down the line once I get it.
Hope I'm not completely off here. Thanks.
Although it says that content providers are nice for sharing content they also give you a couple of other things free!
The best thing about content providers is that the system handles the threading issues for you :) when I ported my app over to Honeycomb i got lots of database errors (mind you ones that couldn't previously exist in gingerbread) I wasn't handling the access on different threads properly.
I quickly put a content provider on top of my database and well.. never looked back in that regards, it seems much easier once you've setup your content provider the code throughout the rest of the app is much nicer and you don't have to worry about concurrency
As written in the specs, and as you mentioned :
Content providers store and retrieve data and make it accessible to all applications. They're the only way to share data across applications; there's no common storage area that all Android packages can access.
So if the data you are storing should not be accessible by other applications there is no need to bother with ContentProviders. As you can see on Android Data Storage there are several ways in which you could store your data.
My opinion is that in your case, for usage of REST Services, and data that is not to be shared with other application you should use SQLiteDatabase. You can find a good example SQLiteDatabase example here.
I understand that Content Providers are made to allow publicly sharing data between applications. However, I'm wondering if anyone has thoughts about making a Content Provider to use just within your own app. Would there be any advantages to doing this? Any disadvantages?
In the past I've just implemented the SQliteOpenHelper to access data from my database, but I'm considering creating a Content Provider. I feel like the URI approach to requesting data is clear and concise. On the other hand, will using a Content Provider just for my application be redundant ( since within it I will have a SQliteOpenHelper class ) and more work than I need?
I would argue it is definitely a good idea to use a ContentProvider even if you don't intend to make it public.
It's good practice to provide the extra level of abstraction over your data to make it easier to change internally. What if you decide to change the underlying database structure at a later time? If you use a ContentProvider you can contain all the structural changes within it, where as if you don't use one, you are forced to change all areas of the code that are affected by the structural changes. Besides, it's nice to be able to re-use the same standard API for accessing data rather than littering your code with low-level access to the database.
Also, there is always the chance that you might want to expose your data in the future. If you don't use a ContentProvider up front, it will be much harder to retrofit it in at a later date.
Then, there's the other parts of the Android where ContentProvider's are required/recommended such as when using SyncAdapters and if you want an App Widget that involves data access for instance.
In summary, there is very little overhead involved in writing a ContentProvider up front (once you have learned the API which is a good idea anyway) so it makes sense to do so, even for private data.
If you are not planning to share data, don't think about Content Providers. They are powerful but hard to write and it will be just silly to implement them if you are going to use them internally.
However, I'm wondering if anyone has thoughts about making a Content Provider to use just within your own app.
Of course... for instance, for an old TODO list app I wrote, I had to write a content provider to allow other apps retrieve and access the tasks states. It was part of the requirements, but more than that it made sense and made the app nicer.
Take a look at the MOTODEV Studio for Eclipse. It is a development environment that extends Eclipse. They have a tool where you can automatically generate a content provider for a database. If a content provider makes it easier to access your data and it doesn't have a significant impact on performance go ahead and use it. In most scenarios this will be the case.
In short,Content Providers helps in managing your data effectively. I would suggest to use them for the following reasons.
It acts as an abstraction layer between your UI and database. You can implement data validation in ContentProviders to validate the data entered by the user. It also lets you to modify the structure of the database without touching the UI and other parts.
They play along nicely with other android framework classes like SyncAdapter. For eg., you can automatically refresh a list, when a value in a database changes using ContentProviders along with CursorLoader. Without ContentProviders you have to implement a lot of functionalities like these on your own.
We can safely expose our private data to other apps. Using ContentProviders will allow us to share our data easily and safely with other apps.
So even if you don't need any of these functionalities now, you might need them in future and its good to go the extra mile and implement them right now.
I agree ContentProviders are a little difficult to grasp but they are definitely helpful, even if you want to use them internally for you own app. The best thing about it is that you can customize the contentproviders for suitable URIs.
Here's a scenario where you may have 5 tables in your database, but you need to join a few of them in certain orders before using them. And make a content URI for each of these joins. You could then each use these URIs as a table :)
I suggest you go ahead with Content Provider, you'll be amazed to see how powerful it is.
In my view point, the content-provider comes with plenty of advantages leave alone just sharing data with other apps. If you need to synchronize with the server using a Sync-Adapter, use google cloud messaging, auto update the UI when the underlying data in the DB changes using Loaders, implement search, use widgets... then the content provider is for you.
I prefer you follow the guideline on because one day you may need to implement some of the above features attached to the content-provider
By the way, you can quickly build you database and CP in less than 5 minutes using content provider generator
As said in documentation:
Creating a Content provider
You don't need a provider to use an SQLite database if the use is
entirely within your own application.
So why bother developing this overhead? You want easier and faster development, right? So one layer of abstraction (SQLiteOpenHelper descendent) is enough.
See Occam's Razor
Do not make an entities without very good reason.
Using a Content Provider can help in an additional level of abstraction - Putting it within your own application make add a significant development time to your project. However if you are using it to share data, application settings or configurations across multiple applications then the Content Provider is your choice.
Watch your security levels and I would recommend using SQLcipher to encrypt data-at-reset (DAR) if your Content Provider is writing to SQLite. (I've used a content provider in a few solutions and provided the ability to take a live "snap shot" of the operational values for debugging and testing.)
Do not use content provider if do not wish to share data with other apps. Use simple sqlitedatabase to perform database operations. Be careful while using content providers for storing confidential data because your confidential information may be accessed by other apps