Android: update UI from thread - android

To update a seekbar, I am using the following code:
My problem is that anytime the seekBar.setProgress() is call, other element on the UI become freezed, so I would like to have a different thread that update the seekBar in the main thread.
How to proceed ?
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
try {
int pos;
switch (msg.what) {
case SHOW_PROGRESS:
pos = setProgress();
if (!mDragging && mBoundService.isPlaying()) {
msg = obtainMessage(SHOW_PROGRESS);
sendMessageDelayed(msg, 100 - (pos % 1000));
}
break;
}
} catch (Exception e) {
}
}
};
private int setProgress() {
if (mBoundService == null || mDragging) {
return 0;
}
int position = mBoundService.getCurrentPosition();
int duration = mBoundService.getDuration();
if (sliderSeekBar != null) {
if (duration > 0) {
// use long to avoid overflow
long pos = 1000L * position / duration;
sliderSeekBar.setProgress((int) pos);
}
}
if (sliderTimerStop != null)
sliderTimerStop.setText(stringForTime(duration));
if (sliderTimerStart != null)
sliderTimerStart.setText(stringForTime(position));
return position;
}

Activities have a runOnUiThread method that allows separate threads to update UI components. Your setProgress method would end up looking like:
private int setProgress() {
if (mBoundService == null || mDragging) {
return 0;
}
final int position = mBoundService.getCurrentPosition();
final int duration = mBoundService.getDuration();
runOnUiThread(new Runnable(){
#Override
public void run(){
if (sliderSeekBar != null) {
if (duration > 0) {
// use long to avoid overflow
long pos = 1000L * position / duration;
sliderSeekBar.setProgress((int) pos);
}
}
if (sliderTimerStop != null)
sliderTimerStop.setText(stringForTime(duration));
if (sliderTimerStart != null)
sliderTimerStart.setText(stringForTime(position));
}
});
return position;
}

Related

Display elapse time for automated viewpager change

I have implemented an auto changing viewpager with fragments. What I want is to show elapse time for each fragment. I have implemented it using threads but not working as I expected. On each onPageSelected event, new thread is creating. I think thread are heavy weighted. I tried to interrupt the thread. But no luck. Is this ok or do we have any other way of implementing this?
private ViewPager2 lifeMainViewPager;
private final Handler sliderHandler = new Handler();
private final int handlerTimeNormal = 1000 * 16;
private final int handlerTimeForHospital = 1000 * 80;
private int viewPagerPosition = 0;
CommonFragmentData deathFragment = new CommonFragmentData();
deathFragment.setmFragment(new LifeDeathClaimsFragment());
deathFragment.setFragmentName(getResources().getString(R.string.life_service_level_claims));
CommonFragmentData deathFragment2 = new CommonFragmentData();
deathFragment2.setmFragment(new LifeDeathClaimsFragment());
deathFragment2.setFragmentName(getResources().getString(R.string.life_service_level_claims));
CommonFragmentData hospitalFragment = new CommonFragmentData();
hospitalFragment.setmFragment(new LifeHospitalClaimsFragment());
hospitalFragment.setFragmentName(getResources().getString(R.string.life_service_level_hospital_claims));
CommonFragmentData policyFragment = new CommonFragmentData();
policyFragment.setmFragment(new LifeNewBusinessPolicyFragment());
policyFragment.setFragmentName(getResources().getString(R.string.life_policy_summary_new_business));
CommonFragmentData proposalFragment = new CommonFragmentData();
proposalFragment.setmFragment(new LifeNewBusinessProposalFragment());
proposalFragment.setFragmentName(getResources().getString(R.string.life_proposal_summary_new_business));
CommonFragmentData proposalFragment2 = new CommonFragmentData();
proposalFragment2.setmFragment(new LifeNewBusinessProposalFragment());
proposalFragment2.setFragmentName(getResources().getString(R.string.life_proposal_summary_new_business));
LifeMainViewpagerAdapter lifeMainViewPagerAdapter = new LifeMainViewpagerAdapter(getSupportFragmentManager(), getLifecycle());
// add Fragments in your ViewPagerFragmentAdapter class
lifeMainViewPagerAdapter.addFragment(deathFragment);
lifeMainViewPagerAdapter.addFragment(proposalFragment);
lifeMainViewPagerAdapter.addFragment(policyFragment);
lifeMainViewPagerAdapter.addFragment(hospitalFragment);
lifeMainViewPagerAdapter.addFragment(deathFragment2);
lifeMainViewPagerAdapter.addFragment(proposalFragment2);
// set Orientation in your ViewPager2
lifeMainViewPager.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
lifeMainViewPager.setAdapter(lifeMainViewPagerAdapter);
lifeMainViewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
#Override
public void onPageSelected(int position) {
super.onPageSelected(position);
viewPagerPosition = position;
headerTitleTxt.setText(lifeMainViewPagerAdapter.getFragmentName(viewPagerPosition));
sliderHandler.removeCallbacks(sliderRunnable);
Thread.currentThread().interrupt();
if (viewPagerPosition == 3) {
sliderHandler.postDelayed(sliderRunnable, handlerTimeForHospital);
new Thread(() -> {
for (int i = handlerTimeForHospital / 1000; i >= 0; i--) {
int finalI = i;
runOnUiThread(() -> timerTxt.setText(finalI + ""));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
} else {
sliderHandler.postDelayed(sliderRunnable, handlerTimeNormal);
new Thread(() -> {
for (int i = handlerTimeNormal / 1000; i >= 0; i--) {
int finalI = i;
runOnUiThread(() -> timerTxt.setText(finalI + ""));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
#Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
if (state == ViewPager2.SCROLL_STATE_IDLE || state == ViewPager2.SCROLL_STATE_DRAGGING) {
int curr = lifeMainViewPager.getCurrentItem();
if (lifeMainViewPager.getAdapter() != null) {
int itemCount = lifeMainViewPager.getAdapter().getItemCount();
if (curr == itemCount - 1) {
lifeMainViewPager.setCurrentItem(1, false);
} else if (curr == 0) {
lifeMainViewPager.setCurrentItem(itemCount - 2, false);
}
}
}
}
});
lifeMainViewPager.setCurrentItem(1);
#Override
protected void onResume() {
super.onResume();
if (viewPagerPosition == 3) {
sliderHandler.postDelayed(sliderRunnable, handlerTimeForHospital);
} else {
sliderHandler.postDelayed(sliderRunnable, handlerTimeNormal);
}
}
private final Runnable sliderRunnable = new Runnable() {
#Override
public void run() {
lifeMainViewPager.setCurrentItem(lifeMainViewPager.getCurrentItem() + 1);
}
};

Seekbar being reset to 0 every time when seekTo() is called

I implemented my video player and seekbar as below and I found when seekTo() function is done the thumb(position) will be reset to 0 just after buffering ends. The thumb only stays on where I dragged to for a second and being reset.
However the video works fine, it starts playing from where I dragged to every time, so my question is why the thumb position is not consistent with player?
public int setVideoProgress(int currentProgress) {
if (mVideoView == null)
return -1;
long time = currentProgress > 0 ? currentProgress : mVideoView.getCurrentPosition();
long length = mVideoView.getDuration();
Log.v(TAG, "setVideoProgress: "+time);
// Update all view elements
mPlayerSeekbar.setMax((int) length);
mPlayerSeekbar.setProgress((int) time);
if (time >= 0) {
String progress = time + "/" + length;
mPlayerPosition.setText(progress);
}
Message msg = new Message();
msg.what = UPDATE_SEEKBAR;
if (mHandler != null)
mHandler.sendMessageDelayed(msg, 1000);
return (int) time;
}
private int mVideoProgress = 0;
private SeekBar.OnSeekBarChangeListener mSeekBarListener = new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
mVideoProgress = progress;
mHandler.removeMessages(HIDDEN_SEEKBAR);
Message msg = new Message();
msg.what = HIDDEN_SEEKBAR;
mHandler.sendMessageDelayed(msg, 3000);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
mVideoView.seekTo(mVideoProgress);
setVideoProgress(mVideoProgress);
}
};
private IMediaPlayer.OnPreparedListener mOnPreparedListener = new IMediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(IMediaPlayer mp) {
if (mVideoView != null) {
mVideoView.setVideoScalingMode(KSYMediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
mVideoView.start();
setVideoProgress(0);
}
}
};
mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case UPDATE_SEEKBAR:
setVideoProgress(0);
break;
case HIDDEN_SEEKBAR:
mPlayerControlShow = false;
mPlayerControl.setVisibility(View.GONE);
break;
}
}
};
System logs as below:
08-11 23:06:19.193 20910-20910: setVideoProgress: 212743 (note: onStopTrackingTouch)
08-11 23:06:19.214 20910-20910: Buffering Start.
08-11 23:06:19.567 20910-20910: Buffering End.
08-11 23:06:19.682 20910-20910: setVideoProgress: 14 (reset to start point somehow)

Setting value of textview via handler

I have a handler inside oncreate of an activity. It receives a value from handler.sendEmptyMessage.
handleMessage is fired and it reaches till the line where I try to update the textview as shown below:
mImageCountText.setText("" + mCountText);
But the text of textview never gets changed. What am I missing here?
Is there anything obvious that causes this issue?
Any help is much appreciated.
EDIT
Handler code
Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
final int what = msg.what;
if (what == Constants.HANDLER_APP_UPDATE) {
if (!UserHelper.isAppBuildVerionSameAsUpdate(HomeActivity.this)) {
updateNotificationAlert();
showAppUpdatePopUp();
}
} else if (what == Constants.HANDLER_COLLECTION_UPDATE) {
//TODO: Refresh collection
} else {
mCountText = what;
if (!Utils.isTablet()) {
if (mCountText == 0) {
mImageCountText.setVisibility(View.INVISIBLE);
} else {
mImageCountText.setVisibility(View.VISIBLE);
mImageCountText.setText("" + mCountText); // this does not work
}
} else {
if (mCountText == 0) {
mCollectionsFragment.refreshAfterUpload();
mCountTextForUplaod.setVisibility(View.INVISIBLE);
} else {
mCollectionsFragment.refreshAfterUpload();
mCountTextForUplaod.setVisibility(View.VISIBLE);
mCountTextForUplaod.setText("" + mCountText);
}
}
}
}
};
Onreceive from where value is sent
#Override
public void onReceive(final Context context, final Intent intent) {
Runnable runnable = new Runnable() {
public void run() {
if (intent.getAction() != null && intent.getAction().equals(Constants.BROADCAST_INTENT_FILTER)) {
boolean broadcastStatus = intent.getBooleanExtra(Constants.BROADCAST_DATA_STATUS, false);
String broadcastStatusMessage = intent.getStringExtra(Constants.BROADCAST_DATA_STATUS_MESAGE);
if (broadcastStatus) {
mCountText = PreferenceHelper.getFromPreference(context, Constants.RECENT_IMAGES_COUNT, 0);
handler.sendEmptyMessage(PreferenceHelper.getFromPreference(context, Constants.RECENT_IMAGES_COUNT, 0));
}
} else {
if (intent.getAction() != null && intent.getAction().equals(Constants.BROADCAST_ACTION_APP_UPDATE)) {
handler.sendEmptyMessage(Constants.HANDLER_APP_UPDATE);
} else if (intent.getAction() != null && intent.getAction().equals(Constants.BROADCAST_ACTION_COLLECTION_UPDATE)) {
handler.sendEmptyMessage(Constants.HANDLER_COLLECTION_UPDATE);
}
}
}
};
Thread mythread = new Thread(runnable);
mythread.start();
Your code is too complex. You don't need the handler and definitively not the thread. Thy it like this:
#Override
public void onReceive(final Context context, final Intent intent) {
if (intent.getAction() != null && intent.getAction().equals(Constants.BROADCAST_INTENT_FILTER)) {
boolean broadcastStatus = intent.getBooleanExtra(Constants.BROADCAST_DATA_STATUS, false);
String broadcastStatusMessage = intent.getStringExtra(Constants.BROADCAST_DATA_STATUS_MESAGE);
if (broadcastStatus) {
mCountText = PreferenceHelper.getFromPreference(context, Constants.RECENT_IMAGES_COUNT, 0);
if (mCountText == 0) {
mCollectionsFragment.refreshAfterUpload();
mCountTextForUplaod.setVisibility(View.INVISIBLE);
} else {
mCollectionsFragment.refreshAfterUpload();
mCountTextForUplaod.setVisibility(View.VISIBLE);
mCountTextForUplaod.setText("" + mCountText);
}
}
} else {
if (intent.getAction() != null && intent.getAction().equals(Constants.BROADCAST_ACTION_APP_UPDATE)) {
if (!UserHelper.isAppBuildVerionSameAsUpdate(HomeActivity.this)) {
updateNotificationAlert();
showAppUpdatePopUp();
}
} else if (intent.getAction() != null && intent.getAction().equals(Constants.BROADCAST_ACTION_COLLECTION_UPDATE)) {
// TODO
}
}
}

Androidish way to organize multithreading (code review)?

I need to download thousands of objects. I need to be able to pause and resume downloads and to display the progress. I'm not good in multithreading, so I've compiled my code from different sources (I've simplified maths and UI but left logic intact):
public class DownloadActivity extends Activity {
private static final int MSG_FINISH = 1;
private static final int MSG_PROGRESS = 2;
private long total;
private ProgressBar progress;
private static DownloadThread thread;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.download);
progress = (ProgressBar) findViewById(R.id.progress);
total = 10000;
progress.setMax((int) total);
if (thread == null)
thread = new DownloadThread(progressHandler, 15000, 25000);
else
thread.setHandler(progressHandler);
((Button) findViewById(R.id.start_button)).setEnabled(thread.paused());
((Button) findViewById(R.id.pause_button)).setEnabled(! thread.paused());
((Button) findViewById(R.id.start_button)).setOnClickListener(startOnClickListener);
((Button) findViewById(R.id.pause_button)).setOnClickListener(pauseOnClickListener);
}
#Override
public void onBackPressed()
{
thread.pause();
thread = null;
super.onBackPressed();
}
private OnClickListener startOnClickListener = new OnClickListener() {
public void onClick(View v) {
((Button) findViewById(R.id.start_button)).setEnabled(false);
thread.unpause();
((Button) findViewById(R.id.pause_button)).setEnabled(true);
}
};
private OnClickListener pauseOnClickListener = new OnClickListener() {
public void onClick(View v) {
((Button) findViewById(R.id.pause_button)).setEnabled(false);
thread.pause();
((Button) findViewById(R.id.start_button)).setEnabled(true);
}
};
final Handler progressHandler = new Handler() {
public void handleMessage(Message msg)
{
switch (msg.what)
{
case MSG_PROGRESS:
if (progress != null)
{
long current = msg.getData().getLong("current");
progress.setProgress((int) current);
}
break;
case MSG_FINISH:
Button pause = ((Button) findViewById(R.id.pause_button));
if (pause != null)
pause.setEnabled(false);
break;
}
}
};
private class DownloadThread extends Thread
{
Handler handler;
long current;
long x;
long x2;
LinkedList<Long> pendingList;
Thread threadA;
Thread threadB;
Thread threadC;
Thread threadD;
boolean paused = true;
DownloadThread(Handler h, long x1, long x2)
{
current = 0;
this.x = x1;
this.x2 = x2;
pendingList = new LinkedList<Long>();
handler = h;
threadA = new Thread(this);
threadA.start();
threadB = new Thread(this);
threadB.start();
threadC = new Thread(this);
threadC.start();
threadD = new Thread(this);
threadD.start();
}
public void run()
{
while (! isInterrupted())
{
synchronized (this)
{
if (paused)
{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
continue;
}
}
Long l;
synchronized (pendingList)
{
if (pendingList.size() == 0)
{
x++;
if (x > x2)
{
continue;
}
l = new Long(x);
pendingList.add(l);
synchronized (this)
{
notifyAll();
}
continue;
}
else
{
l = pendingList.poll();
if (l == null)
{
synchronized (this)
{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
continue;
}
}
}
Object d = DownloadFactory.download(l);
if (d != null)
{
synchronized (DownloadActivity.this)
{
current++;
sendProgress();
}
}
else
{
synchronized (pendingList)
{
pendingList.add(l);
}
}
}
}
public void interrupt()
{
threadA.interrupt();
threadB.interrupt();
threadC.interrupt();
threadD.interrupt();
}
public synchronized boolean paused()
{
return paused;
}
public synchronized void pause()
{
paused = true;
}
public synchronized void unpause()
{
sendProgress();
paused = false;
notifyAll();
}
public synchronized void setHandler(Handler h)
{
handler = h;
sendProgress();
}
private void sendProgress()
{
Message msg = handler.obtainMessage(MSG_PROGRESS);
Bundle b = new Bundle();
b.putLong("current", current);
msg.setData(b);
handler.sendMessage(msg);
if (current == total)
handler.sendEmptyMessage(MSG_FINISH);
}
}
}
This code works fine and does everything I want but I understand that it is ugly and not correct (at least in putting nesting threads). So what is the nice and androidish way to accomplish the same task?
Ok I am not a concurrency professional but using a Thread which contains 4 other threads, too, sounds pretty evil :)
If you just want to make a download manager this is way beyond necessary work. If you use API 9 or above, check the DownloadManager.
Otherwise I recommend to use a simple Manager class that contains a queue and handles the add/remove and start/restart/stop of the downloads. If the queue has an element, I would start a AsyncTask which downloads the element and removes it from the queue if successfully downloaded. If not it tries to resume.

Android, Handler's error

This is a bit weird, but I have no idea where the problem is.
In my onCreate() I have this code:
GameRunningNotesTimer().start();
and then out of onCreate I have this code:
Thread GameRunningNotesTimer = new Thread(new Runnable() {
public void run() {
int sleepingTime;
try {
if (r_settings.getGameOver() == 0) {
sleepingTime = 1000 - (r_settings.getInternalLevel() * 100);
if (r_settings.getInternalLevel() == 0) {
Thread.sleep(1000);
} else {
if (sleepingTime <= 399)
{
sleepingTime = 350;
}
Thread.sleep(sleepingTime);
}
if (r_settings.getGameOver() == 1){ gameOver(); }
myHandler2.sendEmptyMessage(0);
} // End of if (r_settings.getGameOver()
} catch (Exception e) { Log.e("MUSIC!!!!!", "Error in activity", e); }
}// End of run
}); // End of GameRunningNotesTimer()
final Handler myHandler2 = new Handler() {
#Override
public void handleMessage(Message msg) {
//text2.setText(""+item[0]);
int z = 1;
if (r_settings.getGameStarted() == true)
{
changeNoteFromTimer();
} else {
startingCountdown(z);
}
} // end of handleMessage()
};
but this GameRunningNotesTimer().start(); is underlined in red (in Eclipse) and when I mouseover it it says: The method GameRunningNotesTimer() is undefined for the type GameScr
What am I doing wrong? another thread/handler in the same class is not giving me this problem.
Thanks!
It should be GameRunningNotesTimer.start(); not GameRunningNotesTimer().start();

Categories

Resources