Understanding the Handler in ListActivity - android

I'm looking at the ListActivity source code, and I'm seeing that a private Handler is being defined, and that a Runnable is posted to this handler in the onContentChanged() method.
I don't quite get the point of this, as the handlers, as I understand it, are there for inter-thread communication. Here, the definition of the handler and the posting is happening on the same thread, and no delay is specified in the post() call. I can't see the handler being used for anything else, either.
I've probably misunderstood something about the use of handlers here. Why is it done the way it is here, and not by just running mList.focusableViewAvailable() (the call inside the runnable) directly? Wouldn't the result be the same?
Beneath is what I believe are the relevant portions of the ListActivity source code:
public class ListActivity extends Activity {
protected ListView mList;
private Handler mHandler = new Handler();
private Runnable mRequestFocus = new Runnable() {
public void run() {
mList.focusableViewAvailable(mList);
}
};
/**
* Updates the screen state (current list and other views) when the
* content changes.
*
* #see Activity#onContentChanged()
*/
#Override
public void onContentChanged() {
super.onContentChanged();
View emptyView = findViewById(com.android.internal.R.id.empty);
mList = (ListView)findViewById(com.android.internal.R.id.list);
if (mList == null) {
throw new RuntimeException(
"Your content must have a ListView whose id attribute is " +
"'android.R.id.list'");
}
if (emptyView != null) {
mList.setEmptyView(emptyView);
}
mList.setOnItemClickListener(mOnClickListener);
if (mFinishedStart) {
setListAdapter(mAdapter);
}
mHandler.post(mRequestFocus);
mFinishedStart = true;
}
}

Why is it done the way it is here, and not by just running mList.focusableViewAvailable() (the call inside the runnable) directly? Wouldn't the result be the same?
Your concern should not be the Handler. Your concern should be the call to post(). A Handler is not even really needed, as post() is available on View -- this code may pre-date that, though.
post() takes a Runnable and puts it on the message queue for the main application thread. As such, it will not get processed until all other messages that are presently on that queue get processed (FIFO). Presumably, ListActivity needs some other message on the queue to be processed first before focusableViewAvailable() will work successfully.

Related

Notify when all onCreateViewHolder inside a RecylerView have finished

RecyclerView calls onCreateViewHolder a bunch of times and then just keeps binding the data to these views. My view creation is slightly expensive and hence I need to defer rest of the UI tasks until my RecyclerView is done creating all the views.
I tried adding a ViewTreeObserver.OnGlobalLayoutListener but this callback gets called before even the first onCreateViewHolder() call.
Any idea how do I go about it?
After some research I've found out a solution with Handler. As you I'm looking for a beautiful code and this is a bit messy for me. But works perfectly anyway.
Handler is a class that you can use in a way to post message and/or Runnable, which will be added in a queue, then executed when that queue is finished.
My plan is, given that the adapter works on the UI, (inflate ect...) the creation and initialization (all onCreateViewHolder and onBindViewHolder) are added at a moment in the handler of the main thread.
That means that if you post a message in the main thread queue (the same obligatory used by your adapter), then the message will be executed after any previous request (after your adapted has finished to initialize everything).
Exemple :
Main activity
Initialization of the handler :
private Handler mHandler;
#Override
protected void onCreate(Bundle iSavedInstanceState) {
...
mHandler = new Handler(Looper.getMainLooper());
}
Initialization of your CustomAdapter :
private void initializeAdapter(...) {
MyCustomAdapter lMyNewAdapter = new MyCustomAdapter(...)
...
lNewAdapter.SetOnFirstViewHolderCreation(new
MyCustomAdapter.OnFirstViewHolderCreation {
#Override
public void onCreation() {
mHandler.post(new Runnable() {
#Override
public void run() {
// Finally here, the code you want to execute
// At the end of any Create and Bind VH of your
// Adapter
}
});
}
});
}
MyCustomAdapter
private boolean mIsViewHolderCreationStarted;
private OnFirstViewHolderCreation mOnFirstViewHolderCreation;
public CustomItemViewAdapter onCreateViewHolder(
#NonNull ViewGroup iViewGroup, int iI) {
...
if (!mIsViewHolderCreationStarted) {
mIsViewHolderCreationStarted = true;
if (mOnFirstViewHolderCreation != null) {
// It's at this point that we want to add a new request
// in the handler. When we're sure the request of the
// adapter has begun.
mOnFirstViewHolderCreation.onCreation();
}
}
}
public void setOnFirstViewHolderCreation(OnFirstViewHolderCreation iAction) {
mOnFirstViewHolderCreation = iAction;
}
public interface OnFirstViewHolderCreation {
void onCreation();
}
Note
Be aware that this solution will execute a code at the end of the first initialization of the enteer page that it is possible to show in a case of a RecyclerView.
A onCreateViewHolder might be called in case the screen is scrolled.
Which means that this solution does not guarantee you this handler message is executed after all possible onCreateViewHolder.
It only helps you to avoid an overload on the MainThread, during the greedy work of the adapter init.
Something else, in case you're using animations with your adapter to make it appears smoothly or something else (one of the good reasons to use this way to do), don't forget to put your RecyclerView in VISIBLE and not GONE, otherwise, the initialization of the adapter never happens.

Android Handler.getLooper() returns null

I have following problem. I have this implementation of my Thread with Looper.
public class GeoLocationThread extends Thread{
public Handler handler;
private General general;
public void run(){
Looper.prepare();
handler = new IncomingHandler(general);
Looper.loop();
}
public GeoLocationThread(General general){
this.general=general;
}
private static class IncomingHandler extends Handler{
private final WeakReference<General> mService;
IncomingHandler(General service) {
mService = new WeakReference<General>(service);
}
#Override
public void handleMessage(Message msg)
{
General service = mService.get();
if (service != null) {
Location location=service.getLl().getLocation();
if(location.getAccuracy()<40){
service.setOrigin(new GeoPoint((int) (location.getLatitude() * 1E6),(int) (location.getLongitude() * 1E6)));
}
}
}
}
}
and i would like to do the following:
GeoLocationThread locationThread=new GeoLocationThread(this);
locationThread.start();
lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, ll, locationThread.handler.getLooper());
Where lm is LocationManager. From my log and testing I am able to say that locationThread.handler.getLooper() returns null instead of the Looper.
I don't know why it is null. I have tried to call locationThread.isAlive() which has returned true. I have also tried to get locationThread.handler; which I know is not null.
I have also done the lot of googling, but I haven't found more than the documentaion.
Thank you very much in advance for your answers.
Your code is reading null most likely because the operations two are not synchronous with each other. You cannot successfully call getLooper() on a Handler until Looper.prepare() is finished and the Handler is constructed. Because Thread.start() does not block while the other thread executes (of course, why would it? that would defeat the purpose of the new Thread) you have created a race condition between the run() block of the Thread and the code trying to set up the location listener. This will produce different results on different devices based on who can execute first.
Furthermore, registering location updates is already an asynchronous process, so one wonders why the secondary thread is needed? You can simply request updates to your listener without passing in a secondary Looper and the listener will get data posted when new updates are available, the main thread does not stay blocks during this process.
Do you have to call super() in your constructor? Maybe the Looper is not getting set because the parent constructor is not being called?
Ok, try this. Make this:
public class GeoLocationThread extends Thread{
be this:
public class GeoLocationThread extends HandlerThread{
then you can do this.getLooper() when you construct the Handler or when you need the looper.

Only one Looper may be created per thread

I have an app which request data information on internet (client-server app), but this communication is very slow, thus i have decided to create an AsyncTask to manage the delay.
inside of doInBackground i call Looper.prepare() then a my "view generator (which retrives data)".
in detail (the problem):
I have an activity that dinamically create the rows of a list view. but every time i try to inflate rows, android throws a Looper exception "Only one Looper may be created per thread"
i followed the steps:
call Looper.preapare()
use a first inflaction to create a container of my list
use a second inflaction to create a list row
I suppose I cannot inflate two times but i don't know how i can resolve that
AsyncTask
private class DrawerView extends AsyncTask<ActivityGroup, String, View>{
Exception exc=null;
#Override protected void onPreExecute() {
super.onPreExecute();
}
#Override protected View doInBackground(ActivityGroup... params) {
try {
Looper.prepare();
return processAct();
}catch (ConnectionException e) {
exc =e;
return null;
}
catch (Exception e) {
exc = e;
return null;
}
}
#Override protected void onPostExecute(View result) {
super.onPostExecute(result);
if(exc!= null){
Utils.usrMessage(getApplicationContext(), "Oh Noo!:\n"+exc.getMessage());
Utils.logErr(getApplicationContext(), exc);
finish();
}
if(result!= null){
setContentView(result);
}
}
}
processAct() is an abstract method implemented in this way
#Override protected View processAct() throws Exception {
Bundle bundle = getIntent().getExtras();
User user = (User)bundle.getSerializable("user");
Team team = Team.getTeamInformation(this,user.getTeamId());
ArrayList<Player> players =Player.getPlayerList(this,user.getTeamId());
PlayersListAdapter view = new PlayersListAdapter(this,players,team);
return view;
}
PlayerListAdapter is the class which builds/sets first view (list container)..here the first inflation
public PlayersListAdapter(Context context, ArrayList<Player> players,Team team) throws Exception{
super(context);
View view = inflate(getContext(), R.layout.team_players, this);
TextView tv_teamName = (TextView)view.findViewById(R.id.tbplrs_tmnm);
TextView tv_playersNum = (TextView)view.findViewById(R.id.tbplrs_nplrs);
tv_teamName.setText(team.getName());
String msg = players.size()+" ";
msg += (players.size()!=1)?context.getString(R.string.playerPlural):context.getString(R.string.playerSingle);
tv_playersNum.setText(msg);
ListView lView = (ListView)view.findViewById(R.id.tbplrs_plrslst);
PlayersRowListAdapter plAdapter = new PlayersRowListAdapter(context, players);
lView.setAdapter(plAdapter);
}
at last PlayerRowListAdapter which extends BaseAdapter,...here the second inflation
#Override public View getView(int position, View view, ViewGroup parent) {
if (view == null){
LayoutInflater lInflator = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = lInflator.inflate(R.layout.team_player_singlplayer,null);
}
....
....
}
N.B. if i drop the second adapter PlayerRowListAdapter...all works fine...(obviously without list)
Regards
p.s. sorry for my english
Instead of just calling Looper.prepare();, first check if Looper does not already exist for your Thread, if not, call that function. Like this:
if (Looper.myLooper()==null)
Looper.prepare();
The only reason you need to call Looper.prepare() and Looper.loop() is when you want to have a message Handler in a thread that is not the UI thread. Basically, it keeps the thread alive forever so that the Handler that was created inside the thread can still send and receive messages. The same goes for callback methods like LocationListener or something similar. You are responsible for killing the thread when it is done by calling Looper.getMyLooper().quit() inside the thread that it is in.
If you are inflating views in the UI thread, then you do not need to call Looper.prepare() or Looper.loop() as this is already done in the background. You should never inflate Views outside the UI thread.
AsyncTask already has its own Looper. If you want to update your UI from your doInBackground() method use publishProgress(..) which then invokes onProgressUpdate(..) in the main thread. In your onProgressUpdate you can inflate your Views and add them to your UI.
Edit: example code: http://developer.android.com/reference/android/os/AsyncTask.html
Here's how I worked around this.
As the Developer Reference for AsyncTask says,
doInBackground creates a new thread to manage it (this has forced me to call Looper.prepare()), while onPostExecute() uses the main thread.
So I sliced processAct() in two methods: prepareData() which retrieves data and createView() which calls adapter.
I have put the first method into doInBackground(), and the second one (createView()) I have put into onPostExecute().

Android: proper way to pass a message from background thread to UI thread?

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
}
});

Update textView from thread

In my OnCreate method I have created a thread that listens to incoming message!
In OnCreate() {
//Some code
myThread = new Thread() {
#Override
public void run() {
receiveMyMessages();
}
};
myThread.start();
// Some code related to sending out by pressing button etc.
}
Then, receiveMyMessage() functions…
Public void receiveMyMessage()
{
//Receive the message and put it in String str;
str = receivedAllTheMessage();
// << here I want to be able to update this str to a textView. But, How?
}
I checked this article but it did not work for me, no luck!
Any updates to the UI in an Android application must happen in the UI thread. If you spawn a thread to do work in the background you must marshal the results back to the UI thread before you touch a View. You can use the Handler class to perform the marshaling:
public class TestActivity extends Activity {
// Handler gets created on the UI-thread
private Handler mHandler = new Handler();
// This gets executed in a non-UI thread:
public void receiveMyMessage() {
final String str = receivedAllTheMessage();
mHandler.post(new Runnable() {
#Override
public void run() {
// This gets executed on the UI thread so it can safely modify Views
mTextView.setText(str);
}
});
}
The AsyncTask class simplifies a lot of the details for you and is also something you could look into. For example, I believe it provides you with a thread pool to help mitigate some of the cost associated with spawning a new thread each time you want to do background work.
Android supports message-passing concurrency using handlers and sendMessage(msg). (It is also possible to use handlers for shared-memory concurrency.) One tip is to call thread.setDaemon(true) if you wish the thread to die when the app dies. The other tip is to have only one handler and use message.what and a switch statement in the message handler to route messages.
Code and Code

Categories

Resources