I have a REST server that spits out data from a remote ADC, and I need to graph the data just for presentation. I used AsyncTask to move the data aquisition off of the main thread to prevent NetworkOnMainThreadException. I wrote this:
package inostiot.inostiot;
// imports
public class MonitorActivity extends AppCompatActivity {
private GraphWorker worker;
#Override
protected void onSaveInstanceState(Bundle outState) {
worker.cancel(true);
outState.putBundle("state", worker.prepareResume());
outState.putBoolean("resuming", true);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_monitor);
Intent intent = getIntent();
Bundle extras = intent.getExtras();
String ip = extras.getString("ip");
LineChart chart = (LineChart) findViewById(R.id.chart);
XAxis x = chart.getXAxis();
YAxis y = chart.getAxisLeft();
// More chart setup here, ignore
if (savedInstanceState != null) {
boolean resuming = savedInstanceState.getBoolean("resuming", false);
if (resuming) {
worker = new GraphWorker(this, ip, chart, true);
worker.resume(savedInstanceState.getBundle("state"));
worker.execute();
}
} else {
worker = new GraphWorker(this, ip, chart, false);
worker.execute();
}
}
}
class GraphWorker extends AsyncTask<Void, Object, Void> {
private LineChart chart;
private boolean resuming;
private boolean running = true;
private String ip;
private Activity parent;
private ArrayList<ADCPort> ports;
private ArrayList<WalkingDataset> walkingDatasets;
private ArrayList<LineDataSet> lineDataSets;
GraphWorker(Activity parent, String ip, LineChart chart, boolean resuming) {
this.parent = parent;
if (!resuming) {
ports = new ArrayList<>();
walkingDatasets = new ArrayList<>();
this.ip = ip;
}
this.chart = chart;
this.resuming = resuming;
lineDataSets = new ArrayList<>();
}
#Override
public Void doInBackground(Void...params) {
ADC adc = new ADC(ip);
if (!resuming) {
if (!adc.auth()) throw new RuntimeException("Server invalid!");
ports.add(new ColoredADCPort(0, "#FF0000"));
walkingDatasets.add(new WalkingDataset(10));
// More ports are initialized here, just copy-paste an
// color change
}
while (running) {
try {
ports = adc.readPorts(ports);
publishProgress((Object)ports);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (ADCException e) {
e.printStackTrace();
break;
}
}
return null;
}
void resume(Bundle data) {
this.ports = (ArrayList<ADCPort>) data.getSerializable("ports");
this.walkingDatasets = (ArrayList<WalkingDataset>) data.getSerializable("walkingDatasets");
this.ip = data.getString("ip");
this.chart.invalidate();
}
Bundle prepareResume() {
Bundle data = new Bundle();
data.putSerializable("ports", ports);
data.putSerializable("walkingDatasets", walkingDatasets);
data.putString("ip", ip);
return data;
}
#Override
protected void onProgressUpdate(Object... values) {
ArrayList<ADCPort> ports = (ArrayList<ADCPort>) values[0];
lineDataSets.clear();
for (int i = 0; i < ports.size(); i++) {
ColoredADCPort port = (ColoredADCPort) ports.get(i);
WalkingDataset dataset = walkingDatasets.get(i);
dataset.add(port.getValue());
LineDataSet lineDataSet = new LineDataSet(dataset, String.format(Locale.ENGLISH, "Sensor %d", i));
lineDataSet.setCircleColor(Color.parseColor(port.getColor()));
lineDataSet.setColor(Color.parseColor(port.getColor()));
lineDataSet.setDrawValues(false);
lineDataSets.add(lineDataSet);
}
final LineData data = new LineData();
for (LineDataSet set : lineDataSets) {
data.addDataSet(set);
}
chart.setData(data);
chart.postInvalidate();
super.onProgressUpdate();
}
public void stopRunner() {
this.running = false;
}
}
There is a LineChart from MPAndroidCharts on the Activity, and the AsyncTask is supposed to update the UI with the new chart data that it gets from the server. However, after a device rotation and after the resume() method of GraphWorker is called to restore the object state, and after calling execute(), doInBackground() is never called or being run. Why?
I need to stop the previous worker before starting another.
In sequential processing all Async tasks run in a single thread and thus have to wait before the previous task ends. If you need to execute code immediately, you need tasks to be processed in parallel in separate threads.
So, adding running=false in prepareResume to stop the previous worker solves this issue.
Related
I have made an app Earthquake Report app. in that I am fetching earthquake data through an API and showing it in recycler view.
This process runs on the background thread by using the Executor service method and runnable.
But when I run my app and when I rotated my phone the background process is re-executed and reloads data .
How to prevent it? I am using Java for making app.
RecyclerView recyclerView;
LinearLayout nointernetLinearLayout;
ArrayList<EarthquakeModel> earthquake;
private ImageView mEmptyView;
private Button mNoInternetButton;
boolean isConnected;
private static final String url = "https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2021-09-10&endtime=2021-09-11";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recycler_view);
nointernetLinearLayout = findViewById(R.id.no_internet);
mEmptyView = findViewById(R.id.empty_view);
earthquake = new ArrayList<>();
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
#Override
public void run() {
QueryUtils queryUtils = new QueryUtils();
String json = queryUtils.call(url);
try {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
if (isConnected){
Log.i("This is background task","it is restarted if showing again");
JSONObject jsonObject = new JSONObject(json);
JSONArray jsonArray = jsonObject.getJSONArray("features");
for (int i=0; i<jsonArray.length(); i++){
JSONObject c = jsonArray.getJSONObject(i);
JSONObject properties = c.getJSONObject("properties");
double magnitude = properties.getDouble("mag");
String location = properties.getString("place");
long time = properties.getLong("time");
String url = properties.getString("url");
EarthquakeModel earthquakeModel = new EarthquakeModel(location, magnitude,time,url);
earthquake.add(earthquakeModel);
}
}
}catch (JSONException e){
Log.e("Error","is"+e.getMessage());
}
runOnUiThread(new Runnable() {
#Override
public void run() {
View loadingIndicator = findViewById(R.id.loading_indicator);
loadingIndicator.setVisibility(View.GONE);
if (isConnected&&!earthquake.isEmpty()){
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext());
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
EarthquakeAdapter earthquakeAdapter = new EarthquakeAdapter(earthquake,MainActivity.this);
recyclerView.setAdapter(earthquakeAdapter);
}if(isConnected&&earthquake.isEmpty()){
recyclerView.setVisibility(View.GONE);
mEmptyView.setVisibility(View.VISIBLE);
}
if (!isConnected){
recyclerView.setVisibility(View.GONE);
nointernetLinearLayout.setVisibility(View.VISIBLE);
}
});
}
});
}
Run the service in your viewModel, since the viewmodel survives the configuration change.
The update the UI with LiveData.
public ExampleViewModel extends ViewModel {
private MutableLiveData<ArrayList<EarthquakeModel>> earthquake;
public ExampleViewModel() {
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
//get data
earthquake.post(someData)
}
}
public LiveData<ArrayList<EarthquakeModel>> getEarthQuake() {
return earthquake;
}
}
Then in your Activity observe the LiveData
public class ExampleActivity extends Activity {
private ExampleViewModel exampleViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
exampleViewModel = new ViewModelProvider(this).get(ExampleViewModel.class);
exampleViewModel.getEarthQuake().observe(getViewLifecycleOwner(), array -> {
//do somthing with array
}
}
}
I redesigned my project by simplifying the code programming. i placed all the images that are static through the xml layout.
i get only 2 results when i run the program.
1) running with no problems
2) running with problems
i get the following error:
Connected to process 10651 on device 4.7_WXGA_API_22 [emulator-5554]
I/art: Not late-enabling -Xcheck:jni (already on)
W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
I believe the problem lies somewhere in this inner class, i think?!
private class MatchCardGame{
private Game mMatchGame;
private List<Drawable> revealImagesOfCards;
private List<Integer> revealCards;
private Drawable hiddenCard;
private List<Integer> cardPoints;
private List< Boolean> isHidden;
public MatchCardGame(int numOfCards){
mMatchGame = new Game(numOfCards);
revealImagesOfCards = new ArrayList<>();
revealCards = new ArrayList<>();
cardPoints = new ArrayList<>();
isHidden = new ArrayList<>();
setCoverCard();
for(int i = 1; i <= numOfCards; i++)
setMatchImageCard(i);
}
public void setMatchImageCard( int cardLoc){
int drawableLoc = mMatchGame.findImageOfCard(cardLoc);
Drawable drawable = ResourcesCompat.getDrawable(getResources(), drawableLoc, null);
Integer revealCard = mMatchGame.findContentsOfCard(cardLoc);
revealImagesOfCards.add(drawable);
revealCards.add(revealCard);
cardPoints.add(Integer.valueOf(20));
Boolean hideCard = true;
isHidden.add(hideCard);
}
private void setCoverCard(){
hiddenCard = ResourcesCompat.getDrawable(getResources(), R.drawable.black_card, null);
}
public Drawable getImage(int loc, boolean statReveal){
loc--;
if(!statReveal){
Boolean hideCard = isHidden.get(loc);
hideCard = true;
return hiddenCard;
}
else {
Boolean hideCard = isHidden.get(loc);
hideCard = false;
return revealImagesOfCards.get(loc);
}
}
public boolean getHiddenStat(int loc){
loc--;
Boolean hideCard = isHidden.get(loc);
return hideCard;
}
public boolean compareCards(int loc1, int loc2){
loc1--;
loc2--;
Integer card1 = revealCards.get(loc1);
Integer card2 = revealCards.get(loc2);
Integer cardPts1 = cardPoints.get(loc1);
Integer cardPts2 = cardPoints.get(loc2);
if(card1 == card2){
int num = Integer.valueOf( scoreText.getText().toString());
Log.i("TAGG","Score Points: " + (cardPts1 + cardPts2));
new AdjustScore().execute(Integer.valueOf(cardPts1 + cardPts2));
return true;
}
else{
cardPts1 -= 5;
cardPts2 -= 5;
if(cardPts1 < 0)
cardPts1 = 0;
if(cardPts2 < 0)
cardPts2 = 0;
cardPoints.set(loc1, cardPts1);
cardPoints.set(loc2,cardPts2);
return false;
}
}
private class AdjustScore extends AsyncTask<Integer,Integer,Void>{
private TextView scoreText;
private int currentScore;
#Override
protected void onPreExecute() {
super.onPreExecute();
scoreText = (TextView) findViewById(R.id.score_txt);
currentScore = Integer.valueOf( scoreText.getText().toString());
}
#Override
protected Void doInBackground(Integer... integers) {
final int num = integers[0];
Runnable runnable = new Runnable() {
#Override
public void run() {
for (int x = 1; x <= num; x++){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
publishProgress(Integer.valueOf(currentScore + x));
}
}
};
new Thread(runnable).start();
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
Message msg = scoreHandler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString("myPoints",String.valueOf(values[0]));
msg.setData(bundle);
scoreHandler.sendMessage(msg);
}
}
}
I call this in onCreate of activity
private MatchCardGame myGame;
private List<Integer> selectCards;
private TextView scoreText;
private Handler scoreHandler = new Handler(){
#Override
public void handleMessage(Message msg) {
Bundle bundle = msg.getData();
String stat1 = bundle.getString("myPoints");
int num = Integer.valueOf(stat1);
scoreText.setText(String.valueOf(num));
}
};
#Override
protected void onCreate( Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_card_game);
myGame = new MatchCardGame(12);
}
I have another inner class derived from AsynTask.
it is mainly used to setup the card views and set clicklisteners on imageviews. but i don't think it is the problem.
First off, those aren't errors. They're the garbage collector. That's perfectly normal, and shouldn't give you any concern unless you have a performance issue at the same time. Doing something in code rather than in xml to avoid those is unnecessary, and will work or not based on pure luck of when the garbage collector is needed.
Secondly, you're loading all your images in an AsyncTask. Sometimes thats good (it stops you from pausing the main thread to load images), but if you don't have a default image in place, then until that task is done it won't actually be able to display any images. So you have a race condition between drawing and the task finishing.
Solution: do it in xml, or put up a loading screen. I suggest the first, because your task isn't actually doing anything useful if the images are static images from the app- those were loaded when the app launched.
I have a String variable, and I set it's value inside a thread, since it's using a netwok operation.
How can I access the values stored in the Strings?
public class HomeActivity extends AppCompatActivity {
// Initialize AWS DynamoDB Client
public static AmazonDynamoDBClient ddbClient;
public static DynamoDBMapper mapper;
public static Aqua aqua;
// App details
public static String a = "A";
public static String b;
public static Boolean c;
public static String d;
public static String e;
public static String f;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
// Initialize the Amazon Cognito credentials provider
CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
getApplicationContext(),
"******", // Identity Pool ID
Regions.**** // Region
);
// Initialize AWS DynamoDB
ddbClient = new AmazonDynamoDBClient(credentialsProvider);
mapper = new DynamoDBMapper(ddbClient);
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
// Get app details
aqua = mapper.load(Aqua.class, a);
b = aqua.getB();
c = aqua.getC();
d = aqua.getD();
e = aqua.getE();
f = aqua.getF();
} catch (Exception e) {
Log.e("error", e.getMessage());
}
}
});
thread.start();
}
}
Use ExecutorService and submit Callable (below assumes you want the data that is stored inside b,c,d,e,f):
ExecutorService exec = Executors.newSingleThreadExecutor();
Future<String[]> future = exec.submit(new Callable<String[]>() {
#Override
public String[] call() {
try {
// Get app details
aqua = mapper.load(Aqua.class, a);
b = aqua.getB();
c = aqua.getC();
d = aqua.getD();
e = aqua.getE();
f = aqua.getF();
} catch (Exception e) {
Log.e("error", e.getMessage());
}
return new String[] {b, c, d, e, f};
}
});
// ... b will be at value[0], c at value[1]
String[] value = future.get();
Declare the string globally in your Activity/Fragment. This way you can acces it from everywhere.
You could also use handler.sendMessage(message); with your String as message to send it whenever your Thread has finished or whenever you want to. You can then retrieve your String int
protected Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
String status = (String) msg.obj;
Log.i("Got a new message", "MESSAGE: "+status);
}
};
Hope it helps :)
Im trying to understand how threading works in Android.
I've created this AsyncTask class, but I still get this warning in my console:
Skipped 295 frames! The application may be doing too much work on its main thread.
LoadAnswersTask class
public class LoadAnswersTask extends AsyncTask<String, Void, ArrayList<MessageItemModel>> {
public interface LoadAnswersEventHandler {
void onLoadFinished(ArrayList<MessageItemModel> answers);
}
protected LoadAnswersEventHandler event;
public LoadAnswersTask(LoadAnswersEventHandler event) {
this.event = event;
}
#Override
protected ArrayList<MessageItemModel> doInBackground(String... params) {
try {
QuestionModel q = QuestionModel.getById(Integer.parseInt(params[0]));
ArrayList<MessageItemModel> items = new ArrayList<>();
for (AnswerModel answer : q.getAnswers()) {
MessageItemModel messageItem = new MessageItemModel();
messageItem.message = answer.getComment();
messageItem.id = answer.getId();
messageItem.parentId = answer.getParentId();
messageItem.gender = answer.getGender();
messageItem.name = answer.getName();
messageItem.reply = (answer.getParentId() > 0);
messageItem.email = answer.getEmail();
messageItem.answer = true;
items.add(messageItem);
}
return items;
} catch (Exception e) {
Log.d(getClass().getName(), "Failed to load question", e);
}
return null;
}
#Override
protected void onPostExecute(ArrayList<MessageItemModel> messageItemModels) {
this.event.onLoadFinished(messageItemModels);
}
}
I also tried this approach, which seems to work - well sort of as I have my items in a Fragment inside a viewpager - and it sometimes didn't load the answers, im suspecting it's because of the WeakReference combined with the viewpager causing event.get() to be null, but i'm really not sure...
private static class LoadAnswersHandler extends Handler {
private WeakReference<LoadAnswersEventHandler> event;
public LoadAnswersHandler(LoadAnswersEventHandler event) {
this.event = new WeakReference<>(event);
}
#Override
public void handleMessage(Message msg) {
if(event.get() != null) {
event.get().onLoadFinished((ArrayList<MessageItemModel>) msg.obj);
}
}
}
private LoadAnswersHandler loadAnswersHandler;
// ...
protected void loadAnswers(final LoadAnswersEventHandler event) {
loadAnswersHandler = new LoadAnswersHandler(event);
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
QuestionModel q = QuestionModel.getById(question.getId());
ArrayList<MessageItemModel> items = new ArrayList<>();
for (AnswerModel answer : q.getAnswers()) {
MessageItemModel messageItem = new MessageItemModel();
messageItem.message = answer.getComment();
messageItem.id = answer.getId();
messageItem.parentId = answer.getParentId();
messageItem.gender = answer.getGender();
messageItem.name = answer.getName();
messageItem.reply = (answer.getParentId() > 0);
messageItem.email = answer.getEmail();
messageItem.answer = true;
items.add(messageItem);
}
loadAnswersHandler.sendMessage(Message.obtain(loadAnswersHandler, UPDATE_UI, items));
} catch (Exception e) {
Log.d(getClass().getName(), "Failed to load question", e);
}
}
});
thread.start();
}
Thanks!
- Simon
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;
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.
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.
If your don't pulibc your service , the static memory and the callback function can do.
If not , you can send broadcast.