2

i'm a newbie in android. In my app i create a many-to-many chat, and need to update from server a list of Messages. In order to do so, i created a service that updates every second from the server.

My problem is that i don't know how to pass data back to the application. I know that I should do it using intent and broadcast receiver, but in that I stuck with Bundle object that i have to serialize in order to pass it to the app, and it does not make sense to me, since this operation is not that efficient.

For now i'm using the ref to my application (i think it's not that good but don't know why), and after every update from server in the service i activate the application function, and updates it's fields directly. Moreover i think maybe my code will do some good for beginners as well :)

public class UpdateChatService extends Service {

private static final long DELAY_FOR_CHAT_TASK = 0;
private static final long PERIOD_FOR_CHAT_TASK = 1;
private static final TimeUnit TIME_UNIT_CHAT_TASK = TimeUnit.SECONDS;

//private Task retryTask; TODO: check this out
private ScheduledExecutorService scheduler;

private boolean timerRunning = false;
private long RETRY_TIME = 200000;
private long START_TIME = 5000;

@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onCreate() {
    super.onCreate();

    scheduleChatUpdate();
}

private void scheduleChatUpdate() {
    BiggerGameApp app = (BiggerGameApp) getApplication();
    this.scheduler = Executors.newScheduledThreadPool(3);
    this.scheduler.scheduleAtFixedRate(new UpdateChatTask(app), 
            DELAY_FOR_CHAT_TASK, PERIOD_FOR_CHAT_TASK,
            TIME_UNIT_CHAT_TASK);
    timerRunning = true;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if (!timerRunning) {
        scheduleChatUpdate();
    }

    return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy() {
    super.onDestroy();

    if (scheduler != null) {
        scheduler.shutdown();
    }

    timerRunning = false;
}

}

Here is the code of the asynchronous task the runs in the service. Please tell me what i'm doing wrong, and how should pass data from the service to the application.

public void run() {
    try {
        if (this.app.getLastMsgFromServer() == null) {
            this.app.setLastMsgFromServer(new Message(new Player(DEFAULT_EMAIL), "", -1));
            this.app.getLastMsgFromServer().setMessageId(-1);
        }

        Gson gson = new GsonBuilder()
        .registerTypeAdapter(DateTime.class, new DateTimeTypeConverter())
        .create();
        ServerHandler serverHandler = new ServerHandler();

        String jsonString = gson.toJson(this.app.getLastMsgFromServer());

        // Sending player to servlet in server
        String resultString = serverHandler.getResultFromServlet(jsonString, "GetListOfMessages");      

        if (resultString.contains("Error")) {
            return;
        }

        // Parsing answer
        JSONObject json = new JSONObject(resultString);
        Status status = null;
        String statusString = json.getString("status");    

        if (statusString == null || statusString.length() == 0)
            return;

        status = Status.valueOf(statusString);

        if (Status.SUCCESS.equals(status)) {       
            ArrayList<Message> tempChat = null;

            JSONArray jsonList = json.getJSONArray("data");
            MyJsonParser jsonParser = new MyJsonParser();
            tempChat = jsonParser.getListOfMessagesFromJson(jsonList.toString());    

            if (tempChat != null && tempChat.size() != 0) {
                // After getting the chat from the server, it saves the last msg
                // For next syncing with the server
                this.app.setLastMsgFromServer(tempChat.get(LAST_MSG_INDEX));

                tempChat.addAll(this.app.getChat());

                if (tempChat.size() > SIZE_OF_USER_CHAT) {
                    tempChat = (ArrayList<Message>) tempChat.subList(0, SIZE_OF_USER_CHAT - 1);
                }

                this.app.setChat(tempChat);
                this.app.updateViews(null);
            }
        }

        return;
Heydar k
  • 21
  • 4

3 Answers3

2

Is the Service local only (I'm going to assume "yes")?

Communication with a local-only service can be done by passing an instance of android.os.Binder back, as shown below:

public class UpdateChatService extends Service {
    public static final class UpdateChat extends Binder {
        UpdateChatService mInstance;
        UpdateChat(UpdateChatService instance) {
            mInstance = instance;
        }

        public static UpdateChat asUpdateChat(IBinder binder) {
            if (binder instanceof UpdateChat) {
                return (UpdateChat) binder;
            }
            return null;
        }

        public String pollMessage() {
            // Takes a message from the list or returns null
            // if the list is empty.
            return mInstance.mMessages.poll();
        }

        public void registerDataSetObserver(DataSetObserver observer) {
            mInstance.mObservable.registerObserver(observer);
        }

        public void unregisterDataSetObserver(DataSetObserver observer) {
            mInstance.mObservable.unregisterObserver(observer);
        }
    }

    private ScheduledExecutorService mScheduler;
    private LinkedList<String> mMessages;
    private DataSetObservable mObservable;

    @Override
    public IBinder onBind(Intent intent) {
        return new UpdateChat(this);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mObservable = new DataSetObservable();
        mMessages = new LinkedList<String>();
        mScheduler = Executors.newScheduledThreadPool(3);
        mScheduler.scheduleAtFixedRate(new UpdateChatTask(), 0, 1, TimeUnit.SECONDS);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mScheduler.shutdownNow();
        mObservable.notifyInvalidated();
    }

    class UpdateChatTask implements Runnable {
        int mN = 0;
        public void run() {
            // This example uses a list to keep all received messages, your requirements may vary.
            mMessages.add("Message #" + (++mN));
            mObservable.notifyChanged();
        }
    }
}

This example could be used to feed an Activity (in this case a ListActivity) like this:

public class ChattrActivity extends ListActivity implements ServiceConnection {
    LinkedList<String> mMessages;
    ArrayAdapter<String> mAdapter;
    UpdateChat mUpdateChat;
    DataSetObserver mObserver;
    Runnable mNotify;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mMessages = new LinkedList<String>();
        mNotify = new Runnable() {
            public void run() {
                mAdapter.notifyDataSetChanged();
            }
        };
        mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mMessages);
        getListView().setAdapter(mAdapter);
        // Bind to the Service if you do not need it to persist when this Activity
        // dies - otherwise you must call #startService(..) before!
        bindService(new Intent(this, UpdateChatService.class), this, BIND_AUTO_CREATE);
    }

    /**
     * @see android.app.ListActivity#onDestroy()
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mUpdateChat != null) {
            mUpdateChat.unregisterDataSetObserver(mObserver);
            unbindService(this);
        }
    }

    public void onServiceConnected(ComponentName name, IBinder service) {
        mUpdateChat = UpdateChat.asUpdateChat(service);
        mObserver = new DataSetObserver() {
            @Override
            public void onChanged() {
                String message;
                while ((message = mUpdateChat.pollMessage()) != null) {
                    mMessages.add(message);
                }
                runOnUiThread(mNotify);
            }
            @Override
            public void onInvalidated() {
                // Service was killed - restart or handle this error somehow.
            }
        };
        // We use a DataSetObserver to notify us when a message has been "received".
        mUpdateChat.registerDataSetObserver(mObserver);
    }

    public void onServiceDisconnected(ComponentName name) {
        mUpdateChat = null;
    }
}

If you need to communicate across processes you should look into implementing an AIDL interface - but for "local" versions this pattern works just fine & doesn't involve abusing the global Application instance.

Jens
  • 16,853
  • 4
  • 55
  • 52
0

You can use a static memory shared between your service and rest of application (activities). If you do not plan to expose this service to external apps, then sharing static memory is better than serializing/deserializing data via bundles.

Bundles based approach is encouraged for components that are to be exposed to outside world. A typical app usually has just the primary activity exposed in app manifest file.

0

If your don't pulibc your service , the static memory and the callback function can do. If not , you can send broadcast.

regrecall
  • 521
  • 1
  • 6
  • 20