I am want to pass data back from a Thread to Activity (which created the thread).
So I am doing like described on Android documentation:
public class MyActivity extends Activity {
[ . . . ]
// Need handler for callbacks to the UI thread
final Handler mHandler = new Handler();
// Create runnable for posting
final Runnable mUpdateResults = new Runnable() {
public void run() {
updateResultsInUi();
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
[ . . . ]
}
protected void startLongRunningOperation() {
// Fire off a thread to do some work that we shouldn't do directly in the UI thread
Thread t = new Thread() {
public void run() {
mResults = doSomethingExpensive();
mHandler.post(mUpdateResults);
}
};
t.start();
}
private void updateResultsInUi() {
// Back in the UI thread -- update our UI elements based on the data in mResults
[ . . . ]
}
}
Only one thing I am missing here - where and how should be defined mResults so I could access it from both Activity and Thread, and also would be able to modify as needed? If I define it as final in MyActivity, I can't change it anymore in Thread - as it is shown in example...
Thanks!
If you define mResults in the class and not the method, you can change it from either location. For example:
protected Object mResults = null;
(Use protected because it's faster)
Related
I am working on an android application, that fetches image from Internet and show in the user interface. I am using RecyclerView for showing the image. I am planning to download the image using a separate thread. and update RecyclerView via the handler. I dont know wether this concept is correct or not, (I know AsyncTask, but for learning purpose I am trying to implement Handler.)
So I coded for the same as below
private void loadNewsThumbnailImage(ArrayList<DataItem> dataList) {
for (DataItem item : DataList) { //DataItem is the model class
loadThumbnailFromInternet(item);
}
}
private void loadThumbnailFromInternet(final DataItem dataItem) {
Thread imageDowloaderThread = new Thread(new Runnable() {
#Override
public void run() {
Bitmap bitmap = null;
try {
bitmap = getDataItemBitmap(dataItem.getmImageUrl());
dataItem.setmThumbnail(bitmap);
new Handler().post(new Runnable() { // Tried new Handler(Looper.myLopper()) also
#Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
});
imageDowloaderThread.start();
}
I have executed this code but I am getting error, and application is terminated, I don't know why this is happening . please any one help me to sort it out. and explain what is the problem for the current code.
(Please do not suggest to use AsyncTask (I have tried that and it works fine))
UPDATE
Error getting :java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
Your application is getting terminated because you are calling notifyDataSetChanged() from a non UI Thread.
Replace:
new Handler().post(new Runnable() { // Tried new Handler(Looper.myLopper()) also
#Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
With this:
new Handler(Looper.getMainLooper()).post(new Runnable() { // Tried new Handler(Looper.myLopper()) also
#Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
The thread you defined does not have a Looper, and no message queue,so you can not send message in this thread. AsyncTask has its own Looper which you can find it in its source code. This is handler defined in AsyncTask:
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
#SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
#Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
When I want to setText to a textView, the application force closes and gives me this error:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
This is a part of my code :
Thread ThreadRecording = new Thread(new Runnable() {
public void run() {
while(recorder)
{
if (body_num = 10)
{
TextView loog = (TextView)findViewById(R.id.textView1);
loog.setText("Total : 10, Thank you");
}
}
}
});
ThreadRecording.start();
There's a button; if the user clicks it, it calls a function to set body_num = body_num + 1 and the thread checks if body_num == 10
Only the original thread that created a view hierarchy can touch its views
Because just only with UI (main) thread, are you able to manipulate views. You can use Hanlder to update your views.
There are two main uses for a Handler:
To schedule messages and runnables to be executed as some point in the future; and
To enqueue an action to be performed on a different thread than your own. When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler's message queue and processed when appropriate."
-- from "developer.android.com"
Example:
Backgroud thread:
new Thread() {
public void run() {
while(recorder)
{
if (body_num = 10)
{
messageHandler.sendEmptyMessage(0);
}
}
}
}.start();
Your handler which is put in main thread:
private Handler messageHandler = new Handler() {
public void handleMessage(Message msg) {
//update your view here
}
};
You can only modify views in UI thread, you need to use Handler, such as,
Handler h = new Handler(context.getMainLooper()) {
public void handleMessage(Message msg) {
TextView loog = (TextView)findViewById(R.id.textView1);
loog.setText(msg.obj);
}
};
Thread ThreadRecording = new Thread(new Runnable() {
public void run() {
while(recorder)
{
if (body_num = 10)
{
Message message = new Message();
message.obj = "Total : 10, Thank you";
handler.sendMessage(message);
}
}
}
});
ThreadRecording.start();
This can also be achieved by using a AsyncTask. Below is the example code by which a string resource (R.string.your_text) is set in a TextView:
public class CommonSetText extends AsyncTask<Void, Void, Integer> {
private WeakReference<TextView> textView;
private int stringID;
public CommonSetText ( TextView textView, Integer stringID) {
this.textView = new WeakReference<>(textView);
this.stringID = stringID;
}
#Override
protected Integer doInBackground ( Void... voids ) {
return stringID;
}
#Override
protected void onPostExecute ( Integer stringID ) {
super.onPostExecute ( stringID );
textView.get ().setText ( stringID );
}
}
And the text is then set to text view using the below code:
new CommonSetText ( textView, R.string.your_text ).execute ( );
I am having a progress dialog for a process. But i am taking a null pointer exception in my thread. But, when i remove the progress dialog. I am no longer taking an exception.
My code is as this
public class PlayedActivity extends ListActivity {
private PullToRefreshListView listView;
final Context context = this;
public Handler handler;
Runnable sendNumbers2;
List<On> playedOn;
DatabaseHandlerOn db;
private ProgressDialog m_ProgressDialog;
private ArrayList<On> m_results = null;
private PlayedOnAdapter m_adapter;
#SuppressLint({ "HandlerLeak", "HandlerLeak" })
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_playedonnumara);
db = new DatabaseHandlerOnNumara(getApplicationContext());
m_results = new ArrayList<OnNumara>();
this.m_adapter = new PlayedOnNumaraAdapter(this, R.layout.playedrowon, m_results);
this.setListAdapter(this.m_adapter);
sendNumbers2 = new Runnable() {
#Override
public void run() {
playedOn = db.getAllContacts();
for (On on : playedOn) {
m_results.add(on);
}
Collections.reverse(m_results);
//m_ProgressDialog.dismiss();
handler.sendEmptyMessage(0);
}
};
Thread thread = new Thread(sendNumbers2,"sendNumbers2");
thread.start();
/*m_ProgressDialog = ProgressDialog.show(PlayedOnNumaraActivity.this,
"",getString(R.string.PleaseWait), true);
m_ProgressDialog.setCancelable(true);
*/
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
m_adapter.notifyDataSetChanged();
}
};
}
}
}
The code above is working and takes no exception when progress dialog codes are commented
Without your LogCat logs, I can only guess.
m_ProgressDialog is defined after you start your thread. Why? Define it before the thread is started.
Also, I would recommend an AsyncTask for this, instead. See Painless Threading for details on that.
In my Main Activity, I have a Thread that is doing alot of stuff, including adding some records to a database. In my second activity, which inherit from the Main Activity, I want to do a query to my database. But I need to check if the first thread in the Main Activity is finished, what I've done so far is:
public class History extends Main {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!(MainThread.isAlive())) {
getFromDatabase();
}
}
}
This is my getFromDatabase() method
pd = ProgressDialog.show(this, "Please Wait",
"Getting cases from database", false);
Thread t = new Thread(this);
t.start();
which will call this run method:
#Override
public void run() {
ArrayList<Case> c = db1.getAllCases();
Message msg = handler.obtainMessage();
msg.obj = c;
handler.sendMessage(msg);
}
private Handler handler = new Handler() {
#SuppressWarnings("unchecked")
#Override
public void handleMessage(Message m) {
pd.dismiss();
list = (ArrayList<Case>) m.obj;
tempList = getCaseNumberToTempList(list);
tempCaseList = createTempList(list);
lv.setAdapter(new CustomAdapter(History.this, list));
lv.setTextFilterEnabled(true);
}
};
But if I do so, the following line of code will crash my application, it will give a NullPointerException:
if(!(MainThread.isAlive())) {
getFromDatabase();
}
How can I be sure that that the first thread is finished with all the work before I query the database from my history activity?
You can make the Thread in the getFromDatabase() method a static class level variable, write a static get method for it in your Activity, and check for isAlive() in your child Activity.
How about simply using a semaphore variable that you modify from the thread once it has reached a certain state?
I create a thread to update my data and try to do notifyDataSetChanged at my ListView.
private class ReceiverThread extends Thread {
#Override
public void run() {
//up-to-date
mAdapter.notifyDataSetChanged();
}
The error occurs at line:
mAdapter.notifyDataSetChanged();
Error:
12-29 16:44:39.946: E/AndroidRuntime(9026): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
How should I modify it?
Use runOnUiThread() method to execute the UI action from a Non-UI thread.
private class ReceiverThread extends Thread {
#Override
public void run() {
Activity_name.this.runOnUiThread(new Runnable() {
#Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
}
You can not touch the views of the UI from other thread. For your problem you can use either AsyncTask, runOnUiThread or handler.
All The Best
You cant access UI thread from other thread.You have to use handler to perform this.You can send message to handler inside your run method and update UI (call mAdapter.notifyDataSetChanged()) inside handler.
access the UI thread from other threads
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
my approach whe i use other Threads for work:
private AbsListView _boundedView;
private BasicAdapter _syncAdapter;
/** bind view to adapter */
public void bindViewToSearchAdapter(AbsListView view) {
_boundedView = view;
_boundedView.setAdapter(_syncAdapter);
}
/** update view on UI Thread */
public void updateBoundedView() {
if(_boundedView!=null) {
_boundedView.post(new Runnable() {
#Override
public void run() {
if (_syncAdapter != null) {
_syncAdapter.notifyDataSetChanged();
}
}
});
}
}
btw notifyDatasetChanged() method hooks to DataSetObservable class object of AbsListView which is set first by involving AbsListView.setAdaptert(Adapter) method by setting callback to Adapter.registerDataSetObserver(DataSetObserver);
You can write in this way also.
new Handler().postDelayed(new Runnable() {
public void run() {
test();
}
}, 100);
private void test() {
this.notifyDataSetChanged();
}
just test it.