convert protobuff class as model and store its value using realm - android

I am generating protobuf class using Squareup Wire protobuf libary
here is my proto file
syntax = "proto2";
package squareup.dinosaurs;
option java_package = "com.squareup.dinosaurs";
message Dinosaur {
// Common name of this dinosaur, like "Stegosaurus".
optional string name = 1;
// URLs with images of this dinosaur.
repeated string picture_urls = 2;
}
and here is my auto generated code
// Code generated by Wire protocol buffer compiler, do not edit.
// Source file: dinosaur/dinosaur.proto at 8:1
package com.squareup.dinosaurs;
import com.squareup.wire.FieldEncoding;
import com.squareup.wire.Message;
import com.squareup.wire.ProtoAdapter;
import com.squareup.wire.ProtoReader;
import com.squareup.wire.ProtoWriter;
import java.io.IOException;
import java.lang.Object;
import java.lang.Override;
import java.lang.String;
import java.lang.StringBuilder;
import java.util.List;
import okio.ByteString;
public final class Dinosaur extends Message<Dinosaur, Dinosaur.Builder> {
public static final ProtoAdapter<Dinosaur> ADAPTER = new ProtoAdapter<Dinosaur>(FieldEncoding.LENGTH_DELIMITED, Dinosaur.class) {
#Override
public int encodedSize(Dinosaur value) {
return (value.name != null ? ProtoAdapter.STRING.encodedSizeWithTag(1, value.name) : 0)
+ ProtoAdapter.STRING.asRepeated().encodedSizeWithTag(2, value.picture_urls)
+ value.unknownFields().size();
}
#Override
public void encode(ProtoWriter writer, Dinosaur value) throws IOException {
if (value.name != null) ProtoAdapter.STRING.encodeWithTag(writer, 1, value.name);
if (value.picture_urls != null) ProtoAdapter.STRING.asRepeated().encodeWithTag(writer, 2, value.picture_urls);
writer.writeBytes(value.unknownFields());
}
#Override
public Dinosaur decode(ProtoReader reader) throws IOException {
Builder builder = new Builder();
long token = reader.beginMessage();
for (int tag; (tag = reader.nextTag()) != -1;) {
switch (tag) {
case 1: builder.name(ProtoAdapter.STRING.decode(reader)); break;
case 2: builder.picture_urls.add(ProtoAdapter.STRING.decode(reader)); break;
default: {
FieldEncoding fieldEncoding = reader.peekFieldEncoding();
Object value = fieldEncoding.rawProtoAdapter().decode(reader);
builder.addUnknownField(tag, fieldEncoding, value);
}
}
}
reader.endMessage(token);
return builder.build();
}
#Override
public Dinosaur redact(Dinosaur value) {
Builder builder = value.newBuilder();
builder.clearUnknownFields();
return builder.build();
}
};
private static final long serialVersionUID = 0L;
public static final String DEFAULT_NAME = "";
/**
* Common name of this dinosaur, like "Stegosaurus".
*/
public final String name;
/**
* URLs with images of this dinosaur.
*/
public final List<String> picture_urls;
public Dinosaur(String name, List<String> picture_urls) {
this(name, picture_urls, ByteString.EMPTY);
}
public Dinosaur(String name, List<String> picture_urls, ByteString unknownFields) {
super(unknownFields);
this.name = name;
this.picture_urls = immutableCopyOf("picture_urls", picture_urls);
}
#Override
public Builder newBuilder() {
Builder builder = new Builder();
builder.name = name;
builder.picture_urls = copyOf("picture_urls", picture_urls);
builder.addUnknownFields(unknownFields());
return builder;
}
#Override
public boolean equals(Object other) {
if (other == this) return true;
if (!(other instanceof Dinosaur)) return false;
Dinosaur o = (Dinosaur) other;
return equals(unknownFields(), o.unknownFields())
&& equals(name, o.name)
&& equals(picture_urls, o.picture_urls);
}
#Override
public int hashCode() {
int result = super.hashCode;
if (result == 0) {
result = unknownFields().hashCode();
result = result * 37 + (name != null ? name.hashCode() : 0);
result = result * 37 + (picture_urls != null ? picture_urls.hashCode() : 1);
super.hashCode = result;
}
return result;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (name != null) builder.append(", name=").append(name);
if (picture_urls != null) builder.append(", picture_urls=").append(picture_urls);
return builder.replace(0, 2, "Dinosaur{").append('}').toString();
}
public static final class Builder extends com.squareup.wire.Message.Builder<Dinosaur, Builder> {
public String name;
public List<String> picture_urls;
public Builder() {
picture_urls = newMutableList();
}
/**
* Common name of this dinosaur, like "Stegosaurus".
*/
public Builder name(String name) {
this.name = name;
return this;
}
/**
* URLs with images of this dinosaur.
*/
public Builder picture_urls(List<String> picture_urls) {
checkElementsNotNull(picture_urls);
this.picture_urls = picture_urls;
return this;
}
#Override
public Dinosaur build() {
return new Dinosaur(name, picture_urls, buildUnknownFields());
}
}
}
now the issue is i want to directly store the value of Dinosaur into the database using Realm in android. i want Dinosaur class to act as a model.
but the problem is Dinosaur class is declared as final so i cant even derive it.
So is there any design pattern or way that exists to reuse or convert Dinosaur class into model?

You cannot use the Wire Dinosaur with Realm as Wire also require you to extend the Message class, while Realm require you to extend RealmObject.
If you want to combine the two you can create a RealmDinosaur class that accept the wire Dinosaur. Something like this:
public class RealmDinosaur extends RealmObject {
private String name;
private RealmList<RealmString> pictureUrls;
public RealmDinosaur(Dinosaur dino) {
// Fill Realm fields. Note that Realm doesn't support Lists
// with primitive strings yet.
// See https://realm.io/docs/java/latest/#primitive-lists
}
// getter and setters
}
realm.beginTransaction();
realm.copyToRealm(new RealmDinosaur(wireDinosaur));
realm.commitTransaction();

Short answer: no.
For me, this is one of several show-stoppers for wide adoption of Realm.
The developers of Realm don't seem to have considered real-world use-cases such as yours, where your data objects already inherit from something.
They also seem don't seem to get Android's threading requirements.
If you really want to use Realm, I think that you'll have to create another set of objects, likely in another package, that you only use with Realm. Then, you'd have to copy your data from your 'real' objects into the Realm objects.
Personally, for anything non-trivial, I'd either use the built-in SQLite, or find another database that better meets your needs.

Related

How to use Firebase with the Android universal music player?

Here is the Google sample app. It's set up to pull metadata from a URL with a JSON. I would like to know how to have Firebase be my source.
Here is my attempt in changing the RemoteJSONSource class:
package com.mm.android.uamp.model;
import android.support.v4.media.MediaMetadataCompat;
import android.util.Log;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;
import com.mm.android.uamp.utils.LogHelper;
import java.util.ArrayList;
import java.util.Iterator;
public class RemoteJSONSource implements MusicProviderSource {
private static final String TAG = LogHelper.makeLogTag(RemoteJSONSource.class);
DatabaseReference mRootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference mMusic = mRootRef.child("music");
ArrayList<MediaMetadataCompat> tracksFromFB = new ArrayList<>();
public void buildFromFirebase(){
mMusic.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot music : dataSnapshot.getChildren()){
String title = music.child("title").getValue(String.class);
String album = music.child("album").getValue(String.class);
String artist = music.child("artist").getValue(String.class);
String genre = music.child("genre").getValue(String.class);
String source = music.child("source").getValue(String.class);
String id = String.valueOf(source.hashCode());
String iconUrl = music.child("image").getValue(String.class);
int trackNumber = music.child("trackNumber").getValue(Integer.class);
int totalTrackCount = music.child("totalTrackCount").getValue(Integer.class);
int duration = music.child("duration").getValue(Integer.class);
MediaMetadataCompat theMetadataFB = new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, id)
.putString(MusicProviderSource.CUSTOM_METADATA_TRACK_SOURCE, source)
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, album)
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration)
.putString(MediaMetadataCompat.METADATA_KEY_GENRE, genre)
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, iconUrl)
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
.putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, trackNumber)
.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, totalTrackCount)
.build();
tracksFromFB.add(theMetadataFB);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
}
});
}
#Override
public Iterator<MediaMetadataCompat> iterator() {
buildFromFirebase();
ArrayList<MediaMetadataCompat> tracksFB = tracksFromFB;
return tracksFB.iterator();
}
}
The firebase onDataChange is asynchronous so I think it hasn't finished pulling the data yet before the iterator method returns tracksFB.iterator cause tracksFB array is null. Weird thing is when I run in debug mode with a line break on
ArrayList tracksFB = tracksFromFB;
It works. From my research I think I need a callback or some type of pausing task, but I just cant figure it out.
Possible relevant code connected to the iterator method
public interface MusicProviderSource {
String CUSTOM_METADATA_TRACK_SOURCE = "__SOURCE__";
Iterator<MediaMetadataCompat> iterator();
}
next
public class MusicProvider {
private static final String TAG = LogHelper.makeLogTag(MusicProvider.class);
private MusicProviderSource mSource;
private ConcurrentMap<String, List<MediaMetadataCompat>> mMusicListByGenre;
private final ConcurrentMap<String, MutableMediaMetadata> mMusicListById;
private final Set<String> mFavoriteTracks;
enum State {
NON_INITIALIZED, INITIALIZING, INITIALIZED
}
private volatile State mCurrentState = State.NON_INITIALIZED;
public interface Callback {
void onMusicCatalogReady(boolean success);
}
public MusicProvider() {
this(new RemoteJSONSource());
}
public MusicProvider(MusicProviderSource source) {
mSource = source;
mMusicListByGenre = new ConcurrentHashMap<>();
mMusicListById = new ConcurrentHashMap<>();
mFavoriteTracks = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
}
public Iterable<String> getGenres() {
if (mCurrentState != State.INITIALIZED) {
return Collections.emptyList();
}
return mMusicListByGenre.keySet();
}
/**
* Get an iterator over a shuffled collection of all songs
*/
public Iterable<MediaMetadataCompat> getShuffledMusic() {
if (mCurrentState != State.INITIALIZED) {
return Collections.emptyList();
}
List<MediaMetadataCompat> shuffled = new ArrayList<>(mMusicListById.size());
for (MutableMediaMetadata mutableMetadata: mMusicListById.values()) {
shuffled.add(mutableMetadata.metadata);
}
Collections.shuffle(shuffled);
return shuffled;
}
/**
* Get music tracks of the given genre
*
*/
public Iterable<MediaMetadataCompat> getMusicsByGenre(String genre) {
if (mCurrentState != State.INITIALIZED || !mMusicListByGenre.containsKey(genre)) {
return Collections.emptyList();
}
return mMusicListByGenre.get(genre);
}
}
Also the musicService.java in the link above might be relevant. PLEASE help!
There are two ways I can think to do this, but I'm not familiar enough with Firebase to provide working code.
The sample executes iterator() in an AsyncTask, expecting it to block until it can provide a response. So the first, and probably easiest, way to fix it would be to cause iterator() to wait on the data being loaded, or it failing to load. This could be a spinlock or something like wait/notify.
if (!dataloaded) {
try {
wait();
} catch (InterruptedException e) {}
}
ArrayList<MediaMetadataCompat> tracksFB = tracksFromFB;
return tracksFB.iterator();
I'd call buildFromFirebase(); in the constructor though, rather than waiting.
The second option would be to refactor UAMP to have it load the catalog asynchronously. This would be a lot more work, but it may result in a better design in the long run.

Android simple way to Cache and restore from Cache

i found this class on Github and i'm trying to use that, my problem is i can't retrieve or restore data from chach, for example:
MemCache Class:
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.HashMap;
public class MemCache<K, V>
{
private final HashMap<K, CacheValue<K, V>> mCache = new HashMap<K, CacheValue<K, V>>();
private final ReferenceQueue<V> mRefQueue = new ReferenceQueue<V>();
public V get(K key)
{
prune();
CacheValue<K, V> ref = mCache.get(key);
if (ref != null)
{
V value = ref.get();
if (value != null)
return value;
else
mCache.remove(key);
}
return null;
}
public void put(K key, V value)
{
prune();
mCache.put(key, new CacheValue<K, V>(key, value, mRefQueue));
}
public V remove(K key)
{
prune();
CacheValue<K, V> ref = mCache.remove(key);
if (ref != null)
{
V value = ref.get();
if (value != null)
return value;
}
return null;
}
public int size()
{
return mCache.size();
}
public void clear()
{
while (mRefQueue.poll() != null)
/* Do nothing... */;
mCache.clear();
}
#SuppressWarnings("unchecked")
private void prune()
{
CacheValue<K, V> ref;
while ((ref = (CacheValue<K, V>)mRefQueue.poll()) != null)
{
K key = ref.key.get();
if (key != null)
mCache.remove(key);
}
}
/**
* #deprecated Do not use.
*/
public ReferenceQueue<V> getReferenceQueue()
{
return mRefQueue;
}
private static class CacheValue<Key, Value> extends SoftReference<Value>
{
/**
* Reference to the key that installed this value so we can prune
* entries when the value expires.
* <p>
* Must be weakly referenced for the case where the entry is removed
* from the cache, but still strongly referenced because of the
* reference queue.
*/
private final WeakReference<Key> key;
public CacheValue(Key key, Value value, ReferenceQueue<? super Value> queue)
{
super(value, queue);
this.key = new WeakReference<Key>(key);
}
}
}
Put data to cache:
public static MemCache<String,List<SimCards>> cache = new MemCache<>();
private List<SimCards> sims = new ArrayList<>();
...
SimCards tmp = new SimCards();
tmp.setID("1");
tmp.setNumber("0000");
tmp.setPrice("123");
tmp.setSaleSuccess("1");
sims.add(tmp);
Application.cache.put("all_simcards", sims);
sims.clear();
Restore from cache and my problem:
if (Application.cache.get("all_simcards") != null) {
sims.addAll(Application.cache.get("all_simcards"));
}
Problem is Application.cache.get("all_simcards") return null, because sims.clear(); clear my cache !!!
It seems that the cache stores a reference to the object. It doesn't copy it. So, if you call sims.clear();, that .clear() affects to the cached value. Just don't call sims.clear();. Or make a new List, add the object you want to cache, and store that List and not the original one.

Do ORMLite persisters work in Android?

Do custom persisters work on Android? I was trying to write one for an entity, and was having no luck in having it run when the entity gets written by the DAO. So, I tried to use the "MyDatePersister" from the examples and I am not able to get that working either.
The persister is nearly identical to the example one -> https://github.com/j256/ormlite-jdbc/blob/master/src/test/java/com/j256/ormlite/examples/datapersister/MyDatePersister.java
In my entity, I have
#DatabaseTable
public class ClickCount implements Serializable {
// other declarations
#DatabaseField(columnName = DATE_FIELD_NAME, persisterClass = MyDatePersister.class)
private Date lastClickDate;
// more code
}
Here is a link to the whole project in Bitbucket -> https://bitbucket.org/adstro/android-sandbox. It's basically one of the ORMLite Android examples with the custom persister example added.
Any feedback would be greatly appreciated.
Thanks
First off, what is the error result you're getting?
I got my custom persister to work just fine, though I didn't try to extend the DateType. Below is a JSONArrayPersister I found the need for. The confusing part is in the naming of the methods, but once they're setup properly, it should be ok.
package com.example.acme.persister;
import com.j256.ormlite.field.FieldType;
import com.j256.ormlite.field.SqlType;
import com.j256.ormlite.field.types.BaseDataType;
import com.j256.ormlite.support.DatabaseResults;
import org.json.JSONArray;
import org.json.JSONException;
import java.sql.SQLException;
public class JSONArrayPersister extends BaseDataType {
public static int DEFAULT_WIDTH = 1024;
private static final JSONArrayPersister singleTon = new JSONArrayPersister();
public static JSONArrayPersister getSingleton() {
return singleTon;
}
private JSONArrayPersister() {
super(SqlType.STRING, new Class<?>[] { String.class });
}
protected JSONArrayPersister(SqlType sqlType, Class<?>[] classes) {
super(sqlType, classes);
}
#Override
public Object parseDefaultString(FieldType fieldType, String defaultStr) {
try {
return new JSONArray(defaultStr);
} catch (JSONException ex)
{
return new JSONArray();
}
}
#Override
public Object resultToSqlArg(FieldType fieldType, DatabaseResults results, int columnPos) throws SQLException {
try {
return new JSONArray( results.getString(columnPos) );
} catch (JSONException ex)
{
return new JSONArray();
}
}
#Override
public Object resultStringToJava(FieldType fieldType, String stringValue, int columnPos) throws SQLException {
return parseDefaultString(fieldType, stringValue);
}
#Override
public int getDefaultWidth() {
return DEFAULT_WIDTH;
}
}
Then in your entity:
#DatabaseField(persisterClass = JSONArrayPersister.class)
private JSONArray something;

How to track Omniture Custom Traffic Variable in Android?

In Site Catalyst we have defined Custom Traffic Variable "Item Bought". Now how do i send request to update this custom variable from Android library? i don't find much help here
Please find my code below,
public class TrackingHelper {
private static final String TRACKING_RSID = "MY_TRACKING_RSID";
private static final String TRACKING_SERVER = "MY_TRACKING_SERVER";
static ADMS_Measurement measurement = null;
public static void startActivity(Activity activity) {
measurement = ADMS_Measurement.sharedInstance(activity);
measurement.startActivity(activity);
}
public static void stopActivity() {
measurement = ADMS_Measurement.sharedInstance();
measurement.stopActivity();
}
public static void configureAppMeasurement(Activity activity) {
if (measurement == null) {
measurement = ADMS_Measurement.sharedInstance(activity);
measurement.configureMeasurement(TRACKING_RSID, TRACKING_SERVER);
measurement.setOfflineTrackingEnabled(true);
measurement.setDebugLogging(true);
}
}
public static void trackItemBought() {
if (measurement != null) {
measurement = ADMS_Measurement.sharedInstance();
Hashtable<String, Object> contextData = new Hashtable<String, Object>();
contextData.put("Item Bought", "Item Bought");
measurement.setEvents("Item Bought");
measurement.track(contextData);
}
}
}
I can't vouch for the android code, but here are some general thoughts:
When you send a context data variable, you send a name-value pair, so I suspect this line:
contextData.put("Item Bought", "Item Bought");
Should be something more like this (I'm going to say the item being bought is a basketball):
contextData.put("Item Bought", "Basketball");
Then someone with access to processing rules in SiteCatalyst will need to configure a rule to say that that variable ("Item Bought") belongs to its assigned custom traffic variable (prop14, for instance). http://www.jasonegan.net/2011/04/07/omniture-sitecatalyst-15-context-variables-processing-rules/ talks about how to do that a bit, but the article generally assumes you have a certain level of knowledge of SiteCatalyst.
Just make the following change in your code.
public class TrackingHelper {
private static final String TRACKING_RSID = "MY_TRACKING_RSID";
private static final String TRACKING_SERVER = "MY_TRACKING_SERVER";
static ADMS_Measurement measurement = null;
public static void startActivity(Activity activity) {
measurement = ADMS_Measurement.sharedInstance(activity);
measurement.startActivity(activity);
}
public static void stopActivity() {
measurement = ADMS_Measurement.sharedInstance();
measurement.stopActivity();
}
public static void configureAppMeasurement(Activity activity) {
if (measurement == null) {
measurement = ADMS_Measurement.sharedInstance(activity);
measurement.configureMeasurement(TRACKING_RSID, TRACKING_SERVER);
measurement.setOfflineTrackingEnabled(true);
measurement.setDebugLogging(true);
}
}
public static void trackItemBought() {
if (measurement != null) {
measurement = ADMS_Measurement.sharedInstance();
Hashtable<String, Object> contextData = new Hashtable<String, Object>();
contextData.put("Item Bought", "Item Bought");
measurement.setEvents("Item Bought");
measurement.contextData(contextData);
measurement.track();
}
}

Android consuming RestService with/without cache

I am currently in the process of creating a high performance mobile application. Now i am looking at various design patterns for consuming rest services. One such pattern that stands out is the Google IO discussion here. How i have am looking at the code to develop this design. I will be using Spring Rest for doing the actual HTTP Rest and serialization to POJO with the Serialization Library. I came across this implementation here, and will be using it as a blue print for my application. Now a major question is here.
public interface HttpMethods {
public Object getForObject(Object ... params);
public Object putForObject(Object ... params);
}
public class LocationsHttpMethods implements HttpMethods{
private final Context mContext;
public LocationsHttpMethods(Context context)
{
mContext=context;
}
#Override
public Location[] getForObject(Object... params) {
return null;
}
#Override
public Object putForObject(Object... params) {
return null;
}
}
My Location is just a pojo class. Now the question that troubles me is that the second link that i have given just uses Boolean to return data. I will be returning an array of something.
package com.confiz.rest.services;
import java.util.ArrayList;
import java.util.HashMap;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import com.confiz.rest.providers.IProvider;
import com.confiz.rest.providers.LocationsProvider;
public class ProcessorService extends Service
{
private Integer lastStartId;
private final Context mContext = this;
/**
* The keys to be used for the required actions to start this service.
*/
public static class Extras
{
/**
* The provider which the called method is on.
*/
public static final String PROVIDER_EXTRA = "PROVIDER_EXTRA";
/**
* The method to call.
*/
public static final String METHOD_EXTRA = "METHOD_EXTRA";
/**
* The action to used for the result intent.
*/
public static final String RESULT_ACTION_EXTRA = "RESULT_ACTION_EXTRA";
/**
* The extra used in the result intent to return the result.
*/
public static final String RESULT_EXTRA = "RESULT_EXTRA";
}
private final HashMap<String, AsyncServiceTask> mTasks = new HashMap<String, AsyncServiceTask>();
/**
* Identifier for each supported provider.
* Cannot use 0 as Bundle.getInt(key) returns 0 when the key does not exist.
*/
public static class Providers
{
public static final int LOATIONS_PROVIDER = 1;
}
private IProvider GetProvider(int providerId)
{
switch(providerId)
{
case Providers.LOATIONS_PROVIDER:
return new LocationsProvider(this);
}
return null;
}
/**
* Builds a string identifier for this method call.
* The identifier will contain data about:
* What processor was the method called on
* What method was called
* What parameters were passed
* This should be enough data to identify a task to detect if a similar task is already running.
*/
private String getTaskIdentifier(Bundle extras)
{
String[] keys = extras.keySet().toArray(new String[0]);
java.util.Arrays.sort(keys);
StringBuilder identifier = new StringBuilder();
for (int keyIndex = 0; keyIndex < keys.length; keyIndex++)
{
String key = keys[keyIndex];
// The result action may be different for each call.
if (key.equals(Extras.RESULT_ACTION_EXTRA))
{
continue;
}
identifier.append("{");
identifier.append(key);
identifier.append(":");
identifier.append(extras.get(key).toString());
identifier.append("}");
}
return identifier.toString();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
// This must be synchronised so that service is not stopped while a new task is being added.
synchronized (mTasks)
{
// stopSelf will be called later and if a new task is being added we do not want to stop the service.
lastStartId = startId;
Bundle extras = intent.getExtras();
String taskIdentifier = getTaskIdentifier(extras);
Log.i("ProcessorService", "starting " + taskIdentifier);
// If a similar task is already running then lets use that task.
AsyncServiceTask task = mTasks.get(taskIdentifier);
if (task == null)
{
task = new AsyncServiceTask(taskIdentifier, extras);
mTasks.put(taskIdentifier, task);
// AsyncTasks are by default only run in serial (depending on the android version)
// see android documentation for AsyncTask.execute()
task.execute((Void[]) null);
}
// Add this Result Action to the task so that the calling activity can be notified when the task is complete.
String resultAction = extras.getString(Extras.RESULT_ACTION_EXTRA);
if (resultAction != "")
{
task.addResultAction(extras.getString(Extras.RESULT_ACTION_EXTRA));
}
}
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
public class AsyncServiceTask extends AsyncTask<Void, Void, Object>
{
private final Bundle mExtras;
private final ArrayList<String> mResultActions = new ArrayList<String>();
private final String mTaskIdentifier;
/**
* Constructor for AsyncServiceTask
*
* #param taskIdentifier A string which describes the method being called.
* #param extras The Extras from the Intent which was used to start this method call.
*/
public AsyncServiceTask(String taskIdentifier, Bundle extras)
{
mTaskIdentifier = taskIdentifier;
mExtras = extras;
}
public void addResultAction(String resultAction)
{
if (!mResultActions.contains(resultAction))
{
mResultActions.add(resultAction);
}
}
#Override
protected Object doInBackground(Void... params)
{
Log.i("ProcessorService", "working " + mTaskIdentifier);
Object result = false;
final int providerId = mExtras.getInt(Extras.PROVIDER_EXTRA);
final int methodId = mExtras.getInt(Extras.METHOD_EXTRA);
if (providerId != 0 && methodId != 0)
{
final IProvider provider = GetProvider(providerId);
if (provider != null)
{
try
{
result = provider.RunTask(methodId, mExtras);
} catch (Exception e)
{
result = false;
}
}
}
return result;
}
#Override
protected void onPostExecute(Object result)
{
// This must be synchronised so that service is not stopped while a new task is being added.
synchronized (mTasks)
{
Log.i("ProcessorService", "finishing " + mTaskIdentifier);
// Notify the caller(s) that the method has finished executing
for (int i = 0; i < mResultActions.size(); i++)
{
Intent resultIntent = new Intent(mResultActions.get(i));
//What to do here
resultIntent.put(Extras.RESULT_EXTRA, true);
//What to do here ends.
resultIntent.putExtras(mExtras);
resultIntent.setPackage(mContext.getPackageName());
mContext.sendBroadcast(resultIntent);
}
// The task is complete so remove it from the running tasks list
mTasks.remove(mTaskIdentifier);
// If there are no other executing methods then stop the service
if (mTasks.size() < 1)
{
stopSelf(lastStartId);
}
}
}
}
}
Now if you browse to the code that contain the AsyncService, and puts the resultIntent.put(Extras.RESULT_EXTRA, true);
Now how should i pass the data back to the intent. I heard Serializable is bad, and Parceable is ugly code. What else can i use. Secondly, where do i add the SQL cache retrieve code. How can i add this code to the framework. Hope i make sense.

Categories

Resources