when I call addAdapter() several times in background, sometimes I got some duplicate message. e.g. when I call addAdapter(item1, item2, item3...), it prints item1, item2, item2...
ExaminationItem currentAddItem = null;
private void addAdapter(ExaminationItem item)
{
currentAddItem = item;
addhandler.sendEmptyMessage(1);
}
private Handler addhandler = new Handler() {
#Override
public void handleMessage(Message msg)
{
switch (msg.what) {
case 1:
if (currentAddItem != null) {
_adapter.add(currentAddItem);
Log.i(getClass().getName(), "---------------------------addhandler: currentAddItem._itemName = " + currentAddItem._itemName);
}
break;
default:
break;
}
}
};
That isn't surprising. Every time you call sendEmptyMessage(), you're adding a message to the thread's message queue. You're not adding your item to the queue, you're just sending a message to the Handler to access whatever the value of currentAddItem is at the time that the Handler processes the message. It doesn't get to see what the value was at the time that you sent the message. So you're likely to see both skipped items and duplicate items.
private void addAdapter(ExaminationItem item)
{
Message message = addhandler.obtainMessage();
message.what = 1;
message.obj = item;
addhandler.sendMessage(message);
}
private Handler addhandler = new Handler() {
public void handleMessage(Message msg)
{
switch (msg.what) {
case 1:
if (msg.obj != null) {
_adapter.add((ExaminationItem) msg.obj);
examination_scanner_detail_tv.setText("detect to keep fit.");
Log.i(getClass().getName(), "addhandler: msg.obj = " + msg.obj);
}
break;
default:
break;
}
}
};
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
This Code means that if I Click a button, a progress bar start 0 to 100%. And I want to make the progress bar reset, when I click a button before the progress bar reaches 100%.
Here is a part of my code.
This code is button listener.
public void Cal_btn(View v) {
Message msg;
switch (v.getId()) {
case R.id.Square:
if (Number.getText().toString().length() == 0) {
Toast.makeText(getApplicationContext(), "숫자를 입력하세요.", Toast.LENGTH_LONG).show();
} else {
pThread = new ProThread(pHandler);
pThread.setDaemon(true);
pThread.start();
Cal_Result.setVisibility(View.GONE);
progress.setVisibility(View.VISIBLE);
msg = new Message();
msg.what = 1;
msg.arg1 = Integer.parseInt(Number.getText().toString());
mThread.mBackHandler.sendMessage(msg);
}
break;
}
}
And this code is handler.
Handler pHandler = new Handler(){
public void handleMessage(Message msg){
if(msg.what == 3){
if(msg.arg1 == 100){
Cal_Result.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
}else{
progress.setProgress(msg.arg1);
}
}
}
};
And this code is Thread run code.
class ProThread extends Thread{
int proNum = 0;
Handler pHandler;
ProThread(Handler handler){
pHandler = handler;
}
public void run(){
while(proNum != 100) {
proNum++;
Message msg = new Message();
msg.what = 3;
msg.arg1 = proNum;
pHandler.sendMessage(msg);
try {
Thread.sleep(10);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Add a boolean member variable as a marker for "ProgressBar" started or not.
boolean isProgressBarRun;
When the button is clicked, change the status of this variable.
And if the button is clicked at first time, send a message.
And when you handle the message, re-send the messages every 10 ms in your "public void handleMessage(Message msg)" method.
And your onClick method can be written like below:
boolean isProgressBarRun = false;
...
public void Cal_btn(View v) {
Message msg;
switch (v.getId()) {
case R.id.Square:
if (Number.getText().toString().length() == 0) {
Toast.makeText(getApplicationContext(), "숫자를 입력하세요.", Toast.LENGTH_LONG).show();
} else {
if (isProgressBarRun) {
isProgressBarRun = false;
msg = new Message();
msg.what = 4; // to stop the progress bar
mThread.mBackHandler.sendMessage(msg);
msg.what = 3;
msg.arg1 = Integer.parseInt(Number.getText().toString());
mThread.mBackHandler.sendMessage(msg);
} else {
isProgressBarRun = true;
Cal_Result.setVisibility(View.GONE);
progress.setVisibility(View.VISIBLE);
msg = new Message();
msg.what = 1;
msg.arg1 = Integer.parseInt(Number.getText().toString());
mThread.mBackHandler.sendMessage(msg);
}
}
break;
}
}
Your handler can be changed like below:
Handler pHandler = new Handler(){
public void handleMessage(Message msg){
if(msg.what == 4){
progress.setVisibility(View.GONE);
} else {
progress.setProgress(msg.arg1);
Message message = new Message();
message .what = 3;
message .arg1 = msg.arg1 + 1;
pHandler.sendMessageDelayed(message, 10);
}
}
};
In summary, you don't need to implement Thread.
Upper codes are incorrect, just see the concept please.
My Android app uses JobService to perform my App Widget Refresh.
Today I received a single crash report of:-
Fatal Exception: java.lang.OutOfMemoryError: Could not allocate JNI Env
at java.lang.Thread.nativeCreate(Thread.java)
at java.lang.Thread.start(Thread.java:731)
at com.research.app_widget.WidgetUpdateJobService.onStartJob(SourceFile:29)
at android.app.job.JobService$JobHandler.handleMessage(JobService.java:143)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1496)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1386)
From the stack trace of this crash I have created over 500 instances of my HandlerThread
My onStartJob method resembles this:-
#Override
public boolean onStartJob(final JobParameters params) {
mServerHandlerThread = new HandlerThread("ServerApplication");
mServerHandlerThread.start();
mServerHandler = new Handler(mServerHandlerThread.getLooper(), new Handler.Callback() {
#Override
public boolean handleMessage(final Message msg) {
final int what = msg.what;
switch (what) {
case WHAT_WIDGET_REFRESH:
refreshWidget(params);
break;
default:
Log.e(TAG, "handleMessage: Unexpected WHAT = " + what);
}
return true;
}
});
mServerHandler.sendEmptyMessage(WHAT_WIDGET_REFRESH);
return true;
}
The refreshWidget(params) method is as follows:-
private void refreshWidget(final JobParameters params) {
final PersistableBundle persistableBundle = params.getExtras();
final int[] appWidgetIds = persistableBundle.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if (appWidgetIds == null) {
Log.e(TAG, "refreshWidget: appWidgetIds array is null");
} else {
for (int appWidgetId : appWidgetIds) {
new WidgetRemoteViewsHelper().configureViews(this.getApplicationContext(), appWidgetId, isListEmpty(persistableBundle));
}
}
jobFinished(params, false);
}
What I don't understand is that theres an Android limit of starting 100 scheduledJobs, so how have I managed to start over 500 HandlerThreads?
What I would like to know is, is the following an acceptable solution?
e.g. reusing a single HandlerThread Looper instance?
private final HandlerThread mServerHandlerThread;
{
mServerHandlerThread = new HandlerThread("ServerApplication");
mServerHandlerThread.start();
}
#Override
public boolean onStartJob(final JobParameters params) {
mServerHandler = new Handler(mServerHandlerThread.getLooper(), new Handler.Callback() {
#Override
public boolean handleMessage(final Message msg) {
final int what = msg.what;
switch (what) {
case WHAT_WIDGET_REFRESH:
refreshWidget(params);
break;
default:
Log.e(TAG, "handleMessage: Unexpected WHAT = " + what);
}
return true;
}
});
mServerHandler.sendEmptyMessage(WHAT_WIDGET_REFRESH);
return true;
}
From my testing so far this approach seems to work as required. I just felt uneasy about reusing a single HandlerThread.
Android question:
When I set text to a view, when the view will update the UI?
Here is my case 1:
for(int i=0;i< 2000; i++){
aTextView.setText("a"+i);
}
aTextView is a MyTextView, that extends from TextView. I overwirte the onDraw as:
public static int counterP = 0;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
counterP++;
Log.d("MYButton", "onDraw: " + counterP);
}
From the log, I can see, the printed counterP is not consequent. In the loop, the onDraw method is called only 2-4 times.
I did another test, case 2:
boolean notInit = true;
List<String> cmdList = null;
long stSelfRefresh = 0;
String contentStr;
TextView selfTv;
public void onSelfRefreshTv(View v) {
if (cmdList == null || cmdList.isEmpty()) {
Log.e(TAG, "empty now, reset");
notInit = true;
}
if (notInit) {
cmdList = new ArrayList<String>();
for (int i = 0; i < 2000; i++) {
cmdList.add("a" + i);
}
stSelfRefresh = SystemClock.uptimeMillis();
notInit = false;
selfTv = (MyTextView) findViewById(R.id.mytv);
}
contentStr = cmdList.remove(0);
Log.d(TAG, "contentStr = " + contentStr);
selfTv.setText(contentStr);
if (!cmdList.isEmpty()) {
if (!mHandler.sendEmptyMessage(99)) {
Log.e(TAG, "SKIP my self");
}
} else {
Log.d(TAG, "Cost time: " + (SystemClock.uptimeMillis() - stSelfRefresh));
}
}
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 99:
onSelfRefreshTv(null);
break;
default:
break;
}
}
};
The result of case 2 is the counterP is printed out consequent. 1-2000.
I don't know why the textView is updated for each time?
Do you have any idea for this?
Thanks in advance.
******add case 3***********
for(int i=0;i< 2000;i++){
contentStr = cmdList.remove(0);
mHandler.sendEmptyMessage(100);
}
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 100:
selfTv.setText(contentStr);
break;
default:
break;
}
}
};
******end of case 3**********
The test result for case 3 is similar with case 1.
It looks like you are running your code on the UI Thread.
This means: as long as your code is running, the drawing code can not run. The onDraw() is called after your loop finished counting and only the last value is actually drawn on the display.
It's as simple as that.
If you want something count up on your display, you can use a AsyncTask to move the counting part into a non-UI Thread:
new AsyncTask<Void,Integer, Void>() {
#Override
protected Void doInBackground(Void... voids) {
for (int i =0; i<2000; i++) {
publishProgress(i);
}
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
aTextView.setText("a"+values[0]);
}
};
Your second case is like this:
You prepare the list with all 2000 elements.
Set the first element of the list to the TextView and remove the item from the list.
Send yourself a message to run on the UI Thread as soon as the system thinks it is appropriate.
Let go the control of the UI Thread.
Your code is actually finished and the system calls onDraw().
The message from 4 gets processed and you start again with 2.
You see that the system gets control over the UI Thread in the middle of your loop. That's why it's counting visibly.
Case 3 is different from 2:
You are sending 2000 messages in on loop without letting the system handle it.
My test app below is roughly based on this threads tutorial by Cristian Baita.
It works as expected, with the exception that the message sent from myThread's run() method is never received by the MainActivity's handler.
I am passing the MainActivity's handler to myThread in the thread's constructor. I then use that handler's sendMessage() method to send a message back to the MainActivity, but the handler never seems to receive it. Why is this?
Note: I've found using breakpoints for debugging in eclipse is a pain with threads, so I ended up going over the top with Log statments instead to help follow the apps execution.
I've put the full code at the end of this post, but to summarise:
The constructor for the MyThread class takes a handler from the calling activity as shown below.
public class MyThread extends Thread {
// Reference to mainHandler from the mainThread
private Handler parentHandler;
// Constructor
public MyThread(Handler pHandler) {
parentHandler = pHandler;
}
When I create the thread in MainActivity's onCreate() method I pass it the handler mainHandler:
myThread = new MyThread(mainHandler);
myThread.start();
Then in MyThread's run() method I have:
Message messageToParent = Message.obtain();
messageToParent.what = 2;
Log.i("myThread", "About to send message to parent ...");
parentHandler.sendMessage(messageToParent);
The message should then be received by mainHandler defined in MainActivity:
public Handler mainHandler = new Handler() {
public void handleMessages(Message msg){
Log.i("MainActivity", "Message Received");
switch(msg.what) {
case 2:
Log.i("MainActivity", "Handled message. msg.what = " + msg.what);
....
If you watch the LogCat window when this runs, you will see that MainActivity never logs "Message Received" or "Handled message... ". So the message never arrives at it's destination.
The MainActivity:
public class MainActivity extends Activity {
private MyThread myThread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myThread = new MyThread(mainHandler);
myThread.start();
// Message the thread
Message msgToThread = Message.obtain();
msgToThread.what = 4;
Log.i("MainActivity", "About to send message to thread...");
myThread.getHandler().sendMessage(msgToThread);
}
public Handler mainHandler = new Handler() {
public void handleMessages(Message msg){
Log.i("MainActivity", "Message Received");
switch(msg.what) {
case 2:
Log.i("MainActivity", "Handled message. msg.what = " + msg.what);
// Message the thread
Message msgToThread = Message.obtain();
msgToThread.what = 6;
myThread.getHandler().sendMessage(msgToThread);
break;
default:
Log.i("MainActivity", "Unhandled message. msg.what = " + msg.what);
break;
}
}
};
}
The MyThread class:
public class MyThread extends Thread {
// Reference to mainHandler from the mainThread
private Handler parentHandler;
// Constructor
public MyThread(Handler pHandler) {
parentHandler = pHandler;
}
// Local handler for messages to this thread
private Handler myThreadHandler = new Handler() {
public void handleMessage(Message msg) {
switch(msg.what) {
case 4:
Log.i("myThread", "Handled message. msg.what = " + msg.what);
break;
case 6:
Log.i("myThread", "Handled message. msg.what = " + msg.what);
break;
default:
Log.i("myThread", "Unhandled message. msg.what = " + msg.what);
break;
}
}
};
#Override
public void run() {
super.run();
int count = 0;
boolean keepGoing = true;
try {
while(true) {
Log.i("myThread", "run() method - while loop is ticking ..." + count);
// some arbitrary conditions to make stuff happen
switch(count) {
case 5:
Message messageToParent = Message.obtain();
messageToParent.what = 2;
Log.i("myThread", "About to send message to parent ...");
parentHandler.sendMessage(messageToParent);
break;
case 10:
keepGoing = false;
break;
}
if(!keepGoing) {
Log.i("myThread", "myThread is going to stop");
break;
}
count++;
sleep(500);
}
}
catch (Exception e) {
Log.e("My Log", "Thread Loop Exception - " + e);
}
Log.i("myThread", "myThread has reached the end of it's run() method");
}
public Handler getHandler() {
return myThreadHandler;
}
}
From an initial look, it might be because your parent handler is private. change it to public and try once!
I've realized I misunderstood what we are doing when we write the handleMessage() method for our handler.
I thought we were writing a new method for the handler, so I named it something slightly different handleMessages() (notice the plural).
In fact what we are doing is overriding one of the handler's existing method.
In the sample code I was following, Cristian Baita does not use an #Override statement before his handleMessage() method. This is fair enough, since #Override is only a convenience for you and your compiler to highlight bugs. Embarrassingly it is my fault for assuming that because #Override wasn't there, we weren't overriding anything. Hopefully this slip up will help some others in their learning curve.
BTW: I would still thoroughly recommend Cristian Baita's 3 thread tutorials, as they are very clearly explained and easy to follow. The problem was mine, in making assumptions!
When I use Handler.dispatchMessage(msg), the handleMessage(Message msg) will be run on new thread but when I use Handler.sendMessage(msg), the handleMessage(Message msg) will be run on main thread. Who can tell me the difference between them?
Thanks!
Demo:
public class MainActivity extends Activity
{
private String TAG = "MainActivity";
private Handler mHandler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
Log.i(TAG, "Handler:" + Thread.currentThread().getId() + " & arg1=" + msg.arg1);
super.handleMessage(msg);
}
};
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.i(TAG, "Main:" + Thread.currentThread().getId());
testMethod();
}
private void testMethod()
{
Thread thread = new Thread()
{
#Override
public void run()
{
Log.i(TAG, "Thread:" + Thread.currentThread().getId());
Message msg = mHandler.obtainMessage();
msg.arg1 = 1;
mHandler.dispatchMessage(msg);
Message msg2 = mHandler.obtainMessage();
msg2.arg1 = 2;
mHandler.sendMessage(msg2);
}
};
thread.start();
}
}
Output:
04-19 11:32:10.452: INFO/MainActivity(774): Main:1
04-19 11:32:10.488: INFO/MainActivity(774): Thread:8
04-19 11:32:10.492: INFO/MainActivity(774): Handler:8 & arg1=1
04-19 11:32:10.635: INFO/MainActivity(774): Handler:1 & arg1=2
mHandler.dispatchMessage(msg) is like directly calling handleMessage(Message msg) and I don't know when that would be useful. The point of Handlers is the ability to send messages to other threads. That's what you do with sendMessage.
Edit: as you can see it just calls handleMessage() for you.
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// callback = the Runnable you can post "instead of" Messages.
msg.callback.run();
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
If You Call the Handler.dispatchMessage() in the Main Thread Then The Message is processed in Main Thread.
If You Call The Handler.dispatchMessage() in the Worker Thread Then The Message is Processed in Worker Thread.
When You Call Handler.sendMessage(msg) The Message is Processed in the Thread Which Created The Handler.
The messages sent with Handler.sendMessage() will be handled on the thread you created in testMethod().
The messages sent with Handler.dispatchMessage() are handled on the main thread.