How to pass binary data between two apps using Content Provider? - android

I need to pass some binary data between two android apps using Content Provider (sharedUserId is not an option).
I would prefer not to pass the data (a savegame stored as a file, small in size < 20k) as a file (ie. overriding openFile()) since this would necessitate some complicated temp-file scheme to cope with concurrency with several content provider accesses and a running game.
I would like to read the file into memory under a mutex lock and then pass the binary array in the simplest way possible.
How do I do this?
It seems creating a file in memory is not a possibility due to the return type of openFile().
query() needs to return a Cursor. Using MatrixCursor is not possible since it applies toString() to all stored objects when reading it.
What do I need to do? Implement a custom Cursor? This class has 30 abstract methods.
Do I read the file, put it in a SQLite db and return the cursor?
The complexity of this seemingly simple task is mindboggling.

Why not giving the data as Base64 string?

Have you tried using streams? That will work
From the documentation:
"If the table entry is a content: URI, you should never try to open and read the file directly (for one thing, permissions problems can make this fail). Instead, you should call ContentResolver.openInputStream() to get an InputStream object that you can use to read the data."
Link: http://developer.android.com/guide/topics/providers/content-providers.html
Maybe something like this, if you are using Intents:
protected void onActivityResult(int requestCode, int resultCode, Intent intent){
InputStream is = getContentResolver().openInputStream(intent.getData())
...

Related

My Content Uri's keep launching the same application

I'm using content URI's to link content (more like launch a specific activity) in an app and other apps that I've developed but the problem is anytime one application is selected from the android launcher the rest of the URI's keep opening using that singular app
I'm building the uri links using the Linkify class. Below shows the URI's
Pattern inlinkMatcher = Pattern.compile("\\(click[^()]*\\)|\\(you can[^()]*\\)|\\(check here [^()]*\\)");
String inLinkURL = "content://com.n4labs.sexed.providers/hgcontent/";
Pattern inlinkMatcher2 = Pattern.compile("\\(click here to find [^()]*\\)");
Pattern inlinkMatcher3 = Pattern.compile("\\(learn more [^()]*\\)|\\(talk to [^()]*\\)");
boolean yfsinstalled = appInstalledOrNot("com.n4labs.yfs");
String inLinkURL2 = "http://market.android.com/details/?id=com.n4labs.yfs";
if(yfsinstalled)
inLinkURL2 = "content://com.n4labs.yfs.providers/centersearch/";
boolean divainstalled = appInstalledOrNot("com.n4labs.diva");
String inLinkURL3 = "http://market.android.com/details/?id=com.n4labs.diva";
if(divainstalled)
inLinkURL3 = "content://com.n4labs.diva.providers/learn/";
And the calls to Linkify
if(yfsinstalled){
Linkify.addLinks(itemController3.paragraphtext, inlinkMatcher2, inLinkURL2);
}
else
{
Linkify.addLinks(itemController3.paragraphtext, inlinkMatcher2, inLinkURL2, null, mentionFilter);
}
if(divainstalled){
Linkify.addLinks(itemController3.paragraphtext, inlinkMatcher3, inLinkURL3);
}
else
{
Linkify.addLinks(itemController3.paragraphtext, inlinkMatcher3, inLinkURL3, null, mentionFilter);
}
Linkify.addLinks(itemController3.paragraphtext, inlinkMatcher, inLinkURL);
The provider in each app has the appropriate authority and is exported as such
<provider
android:name="com.n4labs.diva.providers.HealthGuideContentProvider"
android:authorities="com.n4labs.diva.providers"
android:exported="true">
</provider>
How can I ensure that each URI opens in the appropriate application automatically, or at least the option is displayed to the user every-time.
I hope I was clear enough.
Anyone?
Thanks.
Each of your ContentProviders is reporting that the MIME type associated with those Uri values is text/plain. This is a very common MIME type, one normally associated with standard text files.
When the user clicks on a link for any of those Uri values, Android will construct an ACTION_VIEW Intent, for a MIME type of text/plain, and attempt to start an activity for that, such as a text editor.
The key now is: what do these ContentProviders actually deliver, as content, for those Uri values? In other words, if I were to call openInputStream() on a ContentResolver, passing in one of those Uri values, what data do I get back in the stream?
There are five main possibilities that I see:
They legitimately return plain text, and you really do want an ordinary text editor to be an option for the user to work with that text. In that case, your setup is fine. Bear in mind that the user might elect to click the "always" option for handling these Uri values and therefore may not necessarily want to be presented with a choice of activities each time. After all, in this scenario, all three of your activities can work with all three of your providers, and regular text editors can also work with these providers.
They legitimately return plain text, but you really do not want anything other than your activities handling those Uri values. In that case, get rid of the ContentProviders, get rid of Linkify, and add your own ClickableSpans to the text to directly start activities of your choosing.
They do not return plain text, but instead return data in some other format, and you are willing for third-party apps to be able to work with that content. In that case, change the MIME type (in the provider and in the associated <intent-filter>) to the correct value, instead of text/plain. This may involve you creating your own custom "vendor" MIME type, if your data does not match any standard data format.
The openInputStream() call would crash, because you have a buggy ContentProvider that is not actually serving data for these Uri values. In that case, fix the ContentProvider, then run through this list of possibilities again. Since you are exporting this provider, you need to actually implement it properly.
The openInputStream() call would crash, or the providers would return something other than plain text, but you are not intending on anybody actually using this content other than yourself. In that case, get rid of the ContentProvider, get rid of Linkify, and add your own ClickableSpans to the text to directly start activities of your choosing.
My guess is that your case is #2 or #5.

Is it more efficient to pass large parcelable or pass id and query db?

Just like it says on the tin. I have a decent sized object, consisting of about 20 variables, some of which are themselves android objects (Calendar, Uri). An arbitrary number of these objects are stored in a database.
I need to pass an individual object by intent to a broadcast receiver. When I create the intent I already have the entire object as a local variable and would not be querying the database. Is it more efficient to pass the entire object as a parcel, or to pass the database id and pull the object from the db in my onReceive method?
I suspect the former, but until yesterday I was doing the latter because I was too lazy to implement parcelable. I did, but now I'm second guessing myself.
P.S. I'm no CS major. In this case I define most efficient to mean least impact on user experience.
If the object in question is small (under 1MB), then going the Parcelable route should be more efficient. Basically, you avoid the disk I/O (and the accompanying complexity of trying to do that on a background thread when triggered by a broadcast).

Is passing with serializable or parceable an efficient solution?

Im building an android app and in the startup activity i parse a pretty big json file (3.3 mb) into custom objects, or when there was no update i retrieve it from a serialized bytearray. Im dealing with one object with a list of about 500 objects with subobjects, lists etc.
Now i need this data, or some of it in my other activities. What is the best solution for this?
It seemed a lot of data processing to serialize and deserialize using intent.putExtra or using parceable everytime you start a new activity. Is this processing less than i think or is there a way to use your parsing class and don't destroy it so you can use something like
Myclass.get(nrIneed).Mysubclass.getsomestring
?
This is how i did it when using data for logging or something in my parsing activity.
You can use Application class to store this data and you can use it across all the Activity
public class BusinessClass extends Application
{
public ParsedData parsedData = new ParsedData();
}
Then call it in any activity using following code
BusinessClass appState = ((BusinessClass)getApplicationContext());
appState.parsedData.getData();
For more info
http://developer.android.com/reference/android/app/Application.html
You SHOULD NOT use Parcelable for objects which may consume memory more than about 1MB. Otherwise the parsing will fail (at least as per API level 8).
However, in your case, I would recommend you to save/organize the parsed data into SQLite and query it from other activities. This will help your app to eat less memory :)
You may also create a static reference to your object, but since its huge in size, I wouldn't recommend you, because then your app will become an appealing target for android VM to kill - when running under low memory circumstances.
I think you shouldn't use your data as a big Json file. At the first launch you should save your data in a database then only use this db when you need to Create/Retrieve/Update/Delete.
If you really want to use your JSON file, then you should make it static (in your application singleton for example).
Hope this will help you

Reading contents of DataOutputStream

Can anyone tell me if there is a way to read/output the contents of a DataOutputStream? I am obtaining one using
new DataOutputStream( httpUrlConnection.getOutputStream() );
and am writing some post data to it. I would like to be able to see what data i am posting after writing to this output stream but cannot find a simple way.
Thanks for any help!
Sure you can 'see' the 'contents' of a DataOutputStream - I imagine it wouldn't be what you expected though! I imagine what you want to be able to do is examine the data getting passed through the stream, quite impossible with the regular class - indeed the very definition of a stream is that it doesn't contain all the data being managed by it, at any one time.
If you really need to be able to audit the data that you've supplied to any output stream then you could do something like this:
import java.io.DataOutputStream;
public class WatcherOutputStream extends DataOutputStream {
private byte[] data = null;
public WatcherOutputStream(OutputStream delegateStream) {
super(delegateStream);
}
public void write(byte[] b) {
// Store the bytes internally
// Pass off to delegate
super.write(b);
}
}
The data saving code and remaining write methods are left to you as an exercise. Of course, this is a horrible way to track the data you are writing out to the stream - it's an extra layer of overhead in both memory and speed. Another options would be to use AOP to examine the data as you write it. This method has the advantages of being less intrusive to your code, and easily maintainable in that you can easily add and remove point cuts without modifying your main program. I suspect that AOP may be more complicated a solution than you are looking for right now, but I am including this link to more reading, just in case it will be helpful.

Why use ContentProvider.getType() to get MIME type?

Looking at content providers, I'm not quite clean on the typical usage of the getType() method. The API doc says about implementing this method that
This allows [applications] to retrieve the MIME
type for a URI when dispatching
intents.
Could anyone describe a typical case where using it would be particularly useful?
For example, you're writing content provider for picture gallery. You should mention in your getType() method that you provide pictures - jpg or png. So, when one will launch image gallery, it will be able to show built-in pictures and pictures provided by your content provider.
In pseudocode the user of contentProvider do something like:
List contentProviders = getProviders();
List resultProviders;
final Type type = Type.JPG;
for (ContentProvider provider : contentProviders) {
if (type == provider.getType()) {
resultProviders.add(provider);
}
}
This is pseudocode, but I hope you will got the idea.
As I understand it, a use case could be the following:
App A contains the content provider. App B uses that content provider to retrieve all the data items from App A. The user then picks one of these (in App B) and after that an activity in App A to show/edit/delete the selected data item should be started. So App B then creates an intent, and to make sure that an activity in App A handles it, you need to set the (mime-)type of the intent to the mime-type of the uri (the show/edit/delete activities in App A has added this mime type to their intent filters).

Categories

Resources