I would like to know when into an Activity Android execute a function itself and when is necessary to call a function. For example in the following script I downloaded, the first 4 methods are executed without calling it, but the last one sendMessage(), needs to be called:
public class BroadcastChat extends Activity {
// Debugging
private static final String TAG = "BcastChat";
private static final boolean D = true;
// Message types sent from the BluetoothChatService Handler
public static final int MESSAGE_READ = 1;
public static final int MESSAGE_WRITE = 2;
public static final int MESSAGE_TOAST = 3;
// Key names received from the BroadcastChatService Handler
public static final String TOAST = "toast";
// Layout Views
private ListView mConversationView;
private EditText mOutEditText;
private Button mSendButton;
// Array adapter for the conversation thread
private ArrayAdapter<String> mConversationArrayAdapter;
// String buffer for outgoing messages
private StringBuffer mOutStringBuffer;
// Member object for the chat services
private BroadcastChatService mChatService = null;
// The Handler that gets information back from the BluetoothChatService
private final Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
if(D) Log.e(TAG, "[handleMessage !!!!!!!!!!!! ]");
switch (msg.what) {
case MESSAGE_WRITE:
byte[] writeBuf = (byte[]) msg.obj;
// construct a string from the buffer
String writeMessage = new String(writeBuf);
mConversationArrayAdapter.add("Me: " + writeMessage);
break;
case MESSAGE_READ:
String readBuf = (String) msg.obj;
mConversationArrayAdapter.add("You: " + readBuf);
break;
case MESSAGE_TOAST:
Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST),
Toast.LENGTH_SHORT).show();
break;
}
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(D) Log.e(TAG, "+++ ON CREATE +++");
// Set up the window layout
setContentView(R.layout.main);
}
public void onStart() {
super.onStart();
if(D) Log.e(TAG, "++ ON START ++");
setupChat();
}
#Override
public synchronized void onResume() {
super.onResume();
if(D) Log.e(TAG, "+ ON RESUME +");
mChatService.start();
}
private void sendMessage(String message) {
if(D) Log.e(TAG, "[sendMessage]");
// Check that there's actually something to send
if (message.length() > 0 ) {
// Get the message bytes and tell the BluetoothChatService to write
byte[] send = message.getBytes();
mChatService.write(send);
// Reset out string buffer to zero and clear the edit text field
mOutStringBuffer.setLength(0);
mOutEditText.setText(mOutStringBuffer);
}
}
... Incomplete script, just a part shown for the question
}
So my question is double:
1- In an Android Activity are the methods called sequencially from the first line to the last one?, Is there a loop that makes the "pointer" go back to the first line once the last one is reached?
2-How can you determine which methods are going to be executed automatically (like onCreate() ) and which are going to wait until they are called by another method of the script.
Thank you very much for your time
The first thing to understand, and it's a vital point, is that this is not a script, it's code. If you think of it as a script, you won't "get" what the code is doing. A script executes from start to finish. It might branch out into a function but ultimately, things happen in order.
In Java ( and therefore Android), everything happens as a response to an event or a callback. Some of these events are raised by Android and Nickolaus has already pointed you to the Activity lifecycle which documents callbacks made by Android to your Activity and the precise order in which they happen. Other events are raised by Receivers, ContentProviders, Listeners etc.
Note that this order is not time based (although of course you can create time based code events) and doesn't happen one after another. They are called when the state of the Activity changes, and only when the state changes.
In the handlers for these callbacks, you can of course call your own functions, create instances of classes and call their methods, and do stuff in order, from top to bottom - but only inside the handler.
The first thing that happens when your app starts is that Android instantiates the Application class. Every app has an instance of the Application class, whether you know it or not, and that Application class instance also has a lifecycle similar to an Activity, so Application.onCreate() is the first event in the application to be fired. Once the Application class instance is instantiated, then the main activity, defined in you rmanifest, is created and it's onCreate() method is called.
After that, everything happens in response to a callback from, for example listeners (onClick, onReceive etc) or in response to events. From the end of your onCreate(), your code only executes when some other event happens.
You can shorten all this, and answer your question,by saying that sendMessage can only be called from somewhere inside a callback handler.
It gets more complicated when there are multiple threads executing code but that's for another day.
I hope that this helps rather than makes things more confusing!
Here take a look at the activity lifecycle this should explain it to you.
Related
I have an application that uses IntentService to run a background task where I pull data from a website, parse the data out, and create calendar events based on the results. Everything seems to be working create, except I'm running into an issue with rotation.
Using the code below, when I rotate the screen, the ProgressDialog box stays visible, but is never updated with new text when the process is updated, and never goes away once the call is completed. I'm using an IntentService instead of an ASyncTask because the user can also schedule the IntentService to run at other times without having to interface with the app. Any help is appreciated, thanks!
public void onCreate(Bundle savedInstanceState) {
Object retained = getLastNonConfigurationInstance();
if (retained instanceof CalendarHandler) {
// CH is a class level variable defined at the top which references my IntentService, aptly named CalendarHandler
ch = (CalendarHandler) retained;
ch.setActivity(this);
} else {
ch = null;
}
activity = this;
btnLogin.setOnClickListener(OnClickListener(View view) {
ch = new CalendarHandler();
ch.setActivity(MyTlc.this);
// Do other stuff, like run the intent service
}
}
public Handler handler = new Handler() {
public void handleMessage(Message message) {
// We read the information from the message and do something with it
// based on what the result code is
String result = message.getData().getString("status");
if (result.equals("ERROR")) {
activity.removeDialog(PROGRESS_DIALOG);
results.setText(message.getData().getString("error"));
} else if (result.equals("DONE")) {
activity.removeDialog(PROGRESS_DIALOG);
int count = message.getData().getInt("count", 0);
activity.results.setText("Added " + count + " shifts to the calendar");
} else {
activity.pDialog.setMessage(result);
}
super.handleMessage(message);
}
};
From what I understand, this should work, and like I said the ProgressDialog box does stay properly, I just can't seem to pass information to the dialog box after rotating.
I have been reading a lot about threads, handlers, loopers, etc and I am very confused. In my app I want to have the first Activity start a background worker. This background worker will continually request data from a TCP socket and (hopefully) post the new info to the UI thread as the data arrives. If the user transitions to a new Activity the background needs to keep doing it's thing but only send different messages to the UI thread so it can update the new layout accordingly.
here is what i have so far...This is in my main activity file
public class MyTCPTest extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set the layout
setContentView(R.layout.main);
// create a handler to handle messages from bg thread
Handler handler = new Handler();
BgWorkerThread bgw = new BgWorkerThread();
bgw.start();
}
in another file i define my background worker thread like this...
public class BgWorkerThread extends Thread {
#Override
public void run(){
while(true)
{
try {
// simulate a delay (send request for data, receive and interpret response)
sleep(1000);
// How do I send data here to the UI thread?
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
If the UI switches to a different Activity will this thread remain running? Also, how does the this thread know which activity to send the message to? Obviously I want it to always send the data to the Activity that is currently active. Does that happen automatically?
Finally, when the UI switches to a different activity the bgworker needs to be notified so that it can begin requesting data that is relevant to the new Activity layout. What is the best way to inform the worker of the change? Couldn't I just create a public method in the BgWorkerThread class that could be called when Activities load?
I've illustrated the same code and a more detailed steps in the followingSO Question. To summarize, in order to notify your UI and give it different context, I suggest the following:
Have a map that map requestId to a Handler (assuming you have the context of request id). You would register appropriate Handler in the Activity thus you could have the handlers behave differently for each Activity (e.g. updating various UI elements when it received the response from the server)
Change to AsyncTask for Threading Model as it has onProgressUpdate method that makes it easier to code in order to notify the UI Thread from the Background Thread
Here is the stub/pseudocode for your BackgroundThread
public class ResponseHandler extends AsyncTask<Void, String, Integer> {
boolean isConnectionClosed = false;
Map<Integer, Handler> requestIdToMapHandler;
public ResponseHandler() {
this.requestIdToMapHandler = new HashMap<Integer, Handler>();
}
#Override
protected Integer doInBackground(Void... params) {
int errorCode = 0;
try {
// while not connection is not close
while(!isConnectionClosed){
// blocking call from the device/server
String responseData = getResponse();
// once you get the data, you publish the progress
// this would be executed in the UI Thread
publishProgress(responseData);
}
} catch(Exception e) {
// error handling code that assigns appropriate error code
}
return errorCode;
}
#Override
protected void onPostExecute(Integer errorCode) {
// handle error on UI Thread
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
String responseData = values[0];
// the response contains the requestId that we need to extract
int requestId = extractId(responseData);
// next use the requestId to get the appropriate handler
Handler uiHandler = getUIHandler(requestId);
// send the message with data, note that this is just the illustration
// your data not necessary be jut String
Message message = uiHandler.obtainMessage();
message.obj = responseData;
uiHandler.sendMessage(message);
}
/***
* Stub code for illustration only
* Get the handler from the Map of requestId map to a Handler that you register from the UI
* #param requestId Request id that is mapped to a particular handler
* #return
*/
private Handler getUIHandler(int requestId) {
return null;
}
/***
* Stub code for illustration only, parse the response and get the request Id
* #param responseId
* #return
*/
private int extractId(String responseId) {
return 0;
}
/***
* Stub code for illustration only
* Call the server to get the TCP data. This is a blocking socket call that wait
* for the server response
* #return
*/
private String getResponse() {
return null;
}
}
I think you're looking for a UI Callback mechanism. I answered a similar question here: Best way to perform an action periodically [while an app is running] - Handler?. As you can see, I'm sending a message to the UI from the background thread by creating a Handler instance and then calling sendEmptyMessage(). There are other types of messages you can send, but this is a trivial example.
Hope that helps.
You can use
runOnUIThread(new Runnable {
public void run() {
//Update your UI here like update text view or imageview etc
}
});
Im doing a little app, its a memory game, you choose one card, it turns up, you choose the second card, it turns up, if they are the same they are out of the game, if they dont match, they are turned down again.
I have
public class PlayActivity extends Activity implements OnClickListener.
The flip events are trigged by click handlers, declared at public void onCreate(Bundle savedInstanceState) they work fine.
When the first card is selected, it calls my method Action, this sets the image from default (the card back) to the 'real' image (the card front). Fine so far.
My problem is the second card: when its selected, it calls method Action, where it should set the front image (lets call it 'middle action'), then a litle pause (a while loop doing nothing until x milliseconds), and then it checks what to do (if they match or not) and turn them down or take the out of the game. You can see where is the problem: the screen only displays the result after x milliseconds (the 'middle action' is not being draw).
Since I have done some little games with XNA, I know the loop Update-Draw, so I know here im updating the same thing twice so always the last one is drawn. But here, the only updating I can have is when click events are trigged, I need a periodic, constant update.
Help?
You can probably use a TimerTask in order to handle that. You can implement it like the following.
This probably isn't the most robust way to do it, but it is an idea. If I figure out a better way to do it in a short time I'll edit my post. :)
Also I would like to add that if you want to make a game that uses an update / draw loop you may need to use a SurfaceView to draw your game. Look at the example here http://developer.android.com/resources/samples/JetBoy/index.html
public class TestGameActivity extends Activity {
/* UIHandler prevents exceptions from
performing UI logic on a non-UI thread */
private static final int MESSAGE_HIDE_CARD = 0;
private class UIHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_HIDE_CARD:
ImageView cardView = (ImageView) msg.obj;
cardView.setImageResource(R.drawable.faceDownCard);
break;
}
}
}
private UIHandler handler = new UIHandler();
// Handle my click. V is the card view
public void onClick(View v) {
final int viewID = v.getId();
// Create a hide task
TimerTask hideTask = new TimerTask() {
#Override
public void run() {
// Construct a message so you won't get an exception
Message msg = new Message();
msg.what = MESSAGE_HIDE_CARD;
msg.obj = findViewById(viewID);
handler.sendMessage(msg);
}
};
// Schedule the task for 2 seconds
new Timer().schedule(hideTask, 2000);
}
}
I have a main menu with an action bar. On create, I run a thread that hits my server for a current status. When Complete, the thread calls a handler which kicks off a constantly running thread that cycles through the items and uses another handler call to change the test in the actionbar. The problem is that when I change views, I either get android.view.WindowLeaked or View not attached to window manager
Here is some sample code
public class MainMenuActivity extends ProtectedWithActionBarActivity{
private int STATUS_COUNTER;
private final int RESULT_STATUS_LOADED = 2000;
private final int RESULT_SHOW_STATUS = 2001;
private CurrentStatusModel currentStatus;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mainmenu);
ActionBar footerbar = (ActionBar)findViewById(R.id.footerbar);
footerbar.setTitle("Currently connected to " + PreferencesHelper.getCurrentEnvironment().name());
STATUS_COUNTER = 0;
statusLoadThread.start();
}
Thread statusLoadThread = new Thread()
{
#Override
public void run()
{
//set currentStatus with data from server
}
};
Thread statusDisplayThread = new Thread()
{
int sleep = 5000;
boolean threadDone = false;
public void done()
{
threadDone = true;
}
#Override
public void run()
{
while(true)
{
//pick message to send to handler
//increment STATUS_COUNTER or reset to 0 when out of bounds
try
{
sleep(sleep);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch(msg.what)
{
case RESULT_STATUS_LOADED:
statusDisplayThread.start();
break;
case RESULT_SHOW_STATUS:
ActionBar footerbar = (ActionBar)findViewById(R.id.footerbar);
String message = ((Object[])msg.obj)[0].toString();
OnClickListener listener = (OnClickListener)((Object[])msg.obj)[1];
footerbar.setTitle(message);
footerbar.setOnTitleClickListener(listener);
break;
case ActivityBase.RESULT_ERROR:
break;
}
}
};
}
I'm not sure if what I'm doing is just wrong or if there is something blatantly obvious that I am missing. What needs to happen is the threads need to stop any time I change screens. Should I use Thread.interrupt(); before starting the next activity?
AsyncTasc allows you to implement doInBackground(), where your thread can crank away at its task. This is similar to the functionality you'd get from Thread.
The real magic happens when you override onPreExecute() and onPostExecute(), which are both executed on the UI thread. This should keep you from getting messages about your Activity not being attached.
Edit - this answer contains a small code example for AsyncTask that could get you started.
You are trying to update UI elements after the owning Activity has been detached from the windowing system.
You will make your life a lot simpler if you use AsyncTask instead of vanilla threads (no handler needed, for one thing) and cancel() the background tasks from your Activity.onPause().
Can't you set a flag in onPause that each of your Threads checks for? If the flag is set then the thread drops out of its loop. Thus whenever the Activity is moved to the background each of your Threads will stop. You would need to handle restarting the threads in onResume. You could alternatively use the AsyncTask approach, but this is not guaranteed to actually cancel when you call its cancel() method, it only attempts to cancel the task.
I have made an application based on the android API's remote service application wich uses callbacks to notify the main activity for changes, the app works fine passing just a single int value from the remote service back to the activity.
The problem is that I would like to pass some Strings and not an int back in order to update the activiti's UI, how can I do that? I have thought of returning an object but there is lack of documentation and don't know how or if it is possible.
Ok just figure it out, I am posting the solution in order to help others with similar problem.
1.you need to make a separate aidl file that only contains the parcable object(see the link in the comments above).
Second in the callback interface import that interface and then add it as a parameter.
when you implement the method from the callback interface in your activity class add the returned object (this would be the parsable object) as the Message object and send that message.
in the handler just unmarshall the object and use it.
This is a sample code:
The Parcable's object interface:
package sap.max;
parcelable ParcableInfo;
The Callback interface:
package sap.max;
import sap.max.ParcableInfo;
/**
* A callback interface used by IMonitorService to send
* synchronous notifications back to its clients. Note that this is a
* one-way interface so the server does not block waiting for the client.
*/
oneway interface IRemoteServiceCallback {
/**
* Called when the service has a new value for you.
*/
void valueChanged(in ParcableInfo info);
}
The service's actions (note this is only what the service will call not complete code):
private final Handler mHandler = new Handler() {
#Override public void handleMessage(Message msg) {
switch (msg.what) {
// It is time to bump the value!
case REPORT_MSG: {
// Up it goes.
int value = ++mValue;
// Broadcast to all clients the new value.
final int N = mCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
try {
ParcableInfo parceInfo = new ParcableInfo(telephonyManager.getSubscriberId() );
parceInfo.setID(telephonyManager.getSubscriberId());
mCallbacks.getBroadcastItem(i).valueChanged(parceInfo);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
mCallbacks.finishBroadcast();
// Repeat every 3 second.
sendMessageDelayed(obtainMessage(REPORT_MSG), 1*3000);
} break;
default:
super.handleMessage(msg);
}
}
};
And finally the Activities part:
private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
#Override
public void valueChanged(ParcableInfo info) throws RemoteException {
CellInfo cellInfo = new CellInfo(null, null, null, null, null, null, null);
cellInfo.setDeviceId(info.ID);
Message msg = mHandler.obtainMessage(BUMP_MSG, info);
mHandler.sendMessage(msg);
}
And the message is handled by:
//this is the handler for the service...
private Handler mHandler = new Handler() {
#Override public void handleMessage(Message msg) {
switch (msg.what) {
case BUMP_MSG:
ParcableInfo info = (ParcableInfo) msg.obj;
configView.setText(info.ID);
Toast.makeText(AntiTheft.this, "Received from service: " + info.ID,
Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
};
Hope this helps others, thanks for the help fedj.
You can pass Strings by default and you should take a look to Parcelable objects (there is no lack of documentation)
I created the simple "ParcableInfo.aidl" file containing :
package my.package.name;
parcelable ParcableInfo;
However, when I build the project the "ParcableInfo.java" isn't generated in the "gen" folder.