I have a fragment that stems off the main activity. I am trying to have a textbox update with the users GPS location as they move around. I currently have it so every time you resume the fragment it updates, but I would like it to happen automatically every 10 seconds or so.
I am currently attempting to use runOnUiThread, which didn't cause my app to crash but didn't seem to do anything.
Within the fragment:
#Override
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
TextView newText = getView().findViewById(R.id.wText);
newText.setText(getStringCoordinates);
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
newText.setText(getStringCoordinates);
}
});
}
Try using a handler, something like this should work
private Handler myHandler;
private static final int DELAY = 10000;
#Override
protected void onResume() {
super.onResume();
myHandler = new Handler(Looper.getMainLooper());
checkAgain();
}
private void checkAgain() {
myHandler.postDelayed(()-> checkGps(),DELAY);
}
private void checkGps() {
//do stuff here
checkAgain();
}
#Override
protected void onStop() {
super.onStop();
myHandler.removeCallbacksAndMessages(null);
myHandler = null;
}
basically it sends a message to the main thread every 10 seconds to check gps
the code may be wrong cause I'm writing it off the top of my head, but it should give you a good start
Maybe this is working
public class c_Thread_Update_Fragment extends Thread {
int i =0;
c_Thread_Update_Fragment(FragmentManager fm, ViewPager vp)
{
this.fragmentManager =fm;
this.mViewpager =vp;
}
#Override
public void run() {
while(true)
{
f.getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
yourfragmentclass.updateData(i);
}
});
i++;
sleep(1000);
}
}
public static void setFragment(Fragment f){
f =f;
}
}
Implement a public static void update (xxx){} in yourfragmentclass
Use setFragment(f) in your Fragment adapterclass and pass the current fragment.
Related
I'm writing an app that will display the current download speed which will be updated every second. My Runnable class is able to update the UI with the value, but when I try to place it inside a loop so that it will continuously run and update the UI TextView every second, the app now hangs.
This is my MainActivity.java:
public class MainActivity extends Activity implements SpeedMeter.TaskRunnableSpeedMeterMethods{
private Thread mSpeedMeterThread;
private Handler mHandler;
private TextView downloadSpeedOutput;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
downloadSpeedOutput = (TextView) findViewById(R.id.speed);
mHandler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message inputMessage) {
SpeedMeter speedMeter = (SpeedMeter) inputMessage.obj;
downloadSpeedOutput.setText(Long.toString(speedMeter.getmDownloadSpeedKB()));
}
};
SpeedMeter speedMeter = new SpeedMeter(this);
speedMeter.run();
// Creates an explicit intent for an Activity in your app
Intent resultIntent = new Intent(this, MainActivity.class);
}
#Override
public void setSpeedMeterThread(Thread currentThread) {
mSpeedMeterThread = currentThread;
}
#Override
public void setInternetSpeed(SpeedMeter speedMeter) {
Message completeMessage = mHandler.obtainMessage(1, speedMeter);
completeMessage.sendToTarget();
}
}
And here's the other SpeedMeter.java:
public class SpeedMeter implements Runnable {
final TaskRunnableSpeedMeterMethods mMainActivity;
private long mDownloadSpeedKB;
public SpeedMeter(TaskRunnableSpeedMeterMethods mainActivity) {
mMainActivity = mainActivity;
}
#Override
public void run() {
mMainActivity.setSpeedMeterThread(Thread.currentThread());
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
// while(true) {
long rxBytesPrevious = TrafficStats.getTotalRxBytes();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long rxBytesCurrent = TrafficStats.getTotalRxBytes();
long downloadSpeed = rxBytesCurrent - rxBytesPrevious;
setmDownloadSpeedKB(downloadSpeed/1000);
mMainActivity.setInternetSpeed(this);
// }
}
public long getmDownloadSpeedKB() {
return mDownloadSpeedKB;
}
public void setmDownloadSpeedKB(long mDownloadSpeedKB) {
this.mDownloadSpeedKB = mDownloadSpeedKB;
}
interface TaskRunnableSpeedMeterMethods {
void setSpeedMeterThread(Thread currentThread);
void setInternetSpeed(SpeedMeter speedMeter);
}
}
Any help will be appreciated!
You didnt start your runnable as a new thread instead you called the run function like a normal function (so u do the while loop on ur UI thread which blocks it)
replace
speedMeter.run();<br />
SpeedMeter speedMeter = new SpeedMeter(this);
with
new Thread(new SpeedMeter(this)).start();
see https://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html for more infos on how to start a Runnable :)
The ideal way to do this would be to create an AsyncTask that would post a message to your UI thread, after it complete the task in the doInBackground() call.
Standards
also the interface structure you are following does not make sense and does not follow good standards. Usually an interface is used as a callback, which is basically what you are doing. But the standard is to say onSomethingChangedA() or onSomethingChangedB() from OnSomethingChangedListener interface.
I think your loop is always true so app hangs its better to create a boolean and use while(mboolean) and put this in your loop
if(something){
mboolean=false;
}
you can also use CountDownTimer.
for example:
new CountDownTimer(miliseconds,1000)
//if you have download speed and download size you can find miliseconds
#Override
public void onTick(long l) {
//something you want to do every seconds
}
#Override
public void onFinish() {
//something you want to do on finish
}
I have a Fragment which sets up a ListView and creates a Handler to update the Listview periodically. However, it looks like the Handler still runs after the Fragment has been destroyed.
The following is the code.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//boilerplate code
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
assignAdapter();
handler.postDelayed(this, 15000);
}
});
return v;
}
Updating the ListView after the destruction of the Fragment causes the app to crash. How can I cause the Handler to stop as the Fragment gets destroyed? I would also like to know what effects if any pausing the app has on the Handler as well.
You need to implement handler like this
private Handler myHandler;
private Runnable myRunnable = new Runnable() {
#Override
public void run() {
//Do Something
}
};
#Override
public void onDestroy () {
mHandler.removeCallbacks(myRunnable);
super.onDestroy ();
}
You need to store a reference to your handler and runnable in the fragment, and then when the fragment is destroyed you need to remove callbacks from the handler passing in the runnable.
private Handler mHandler;
private Runnable mRunnable;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//boilerplate code
mRunnable = new Runnable() {
#Override
public void run() {
assignAdapter();
handler.postDelayed(this, 15000);
}
};
mHandler = new Handler(mRunnable);
mHandler.post();
return v;
}
#Override
public void onDestroy() {
mHandler.removeCallbacks(mRunnable);
super.onDestroy();
}
Another way of stopping the handler with the use of WeakReference to the fragment:
static final class UpdateUIRunnable implements Runnable {
final WeakReference<RouteGuideFragment> weakRefToParent;
final Handler handler;
public UpdateUIRunnable(RouteGuideFragment fragment, Handler handler) {
weakRefToParent = new WeakReference<RouteGuideFragment>(fragment);
this.handler = handler;
}
public void scheduleNextRun() {
handler.postDelayed(this, INTERVAL_TO_REDRAW_UI);
}
#Override
public void run() {
RouteGuideFragment fragment = weakRefToParent.get();
if (fragment == null || fragment.hasBeenDestroyed()) {
Log.d("UIUpdateRunnable", "Killing updater -> fragment has been destroyed.");
return;
}
if (fragment.adapter != null) {
try {
fragment.adapter.forceUpdate();
} finally {
// schedule again
this.scheduleNextRun();
}
}
}
}
where fragment.hasBeenDestroyed() is simply a getter for mDestroyed property of a fragment:
#Override
public void onDestroy() {
super.onDestroy();
mDestroyed = true;
}
Someone posted another question similar and the problem is due to a bug in the ChildFragmentManager. Basically, the ChildFragmentManager ends up with a broken internal state when it is detached from the Activity. Have a look at the original answer here
I have a set of methods and each method takes time to do its task.
For instance,
After Method setRules() is executed,
The progress level should increase to 30.
After Method getLogs() is executed,
The progress level should increase to 60.
After Method getChartView() is executed,
The progress level should increase to 100.
But the ProgressBar increases till 30 and not goes beyond that although my all the methods are executing.
Code -
public class DialogPopup extends DialogFragment
{
private static int myProgress=0;
private ProgressBar progressBar;
private int progressStatus=0;
private Handler myHandler=new Handler();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
super.onCreateView(inflater, container, savedInstanceState);
view = inflater.inflate(R.layout.main,container, false);
progressBar = (ProgressBar) view.findViewById(R.id.progressBar1);
beginYourTask();
return view;
}
public void beginYourTask()
{
myProgress=0;
progressBar=(ProgressBar)view.findViewById(R.id.myProgress);
progressBar.setMax(100);
//progressBar.setVisibility(View.VISIBLE);
/*Do some work in background thread*/
new Thread(new Runnable()
{
#Override
public void run()
{
progressStatus=setRules();
myHandler.post(new Runnable()
{
public void run()
{
progressBar.setProgress(progressStatus);
}
});
progressStatus=getLogs();
myHandler.post(new Runnable()
{
public void run()
{
progressBar.setProgress(progressStatus);
}
});
progressStatus=getChartView();
myHandler.post(new Runnable()
{
public void run()
{
progressBar.setProgress(progressStatus);
}
});
/*Hides the Progress bar*/
myHandler.post(new Runnable()
{
#Override
public void run()
{
progressBar.setVisibility(View.GONE);
progressStatus=0;
myProgress=0;
}
});
}
private int setRules()
{
//Code here takes time
//set progress to 30
return 30;
}
private int getLogs()
{
//Code here takes time
//set progress to 60
return 60;
}
private int getChartView()
{
//Code here takes time
//set progress to 100
return 100;
}
}).start();
}
NOTE:
I took the help to create such Progress Bar from the tutorial -android-progress-bar-horizontal.
Use an AsyncTask for this kind of thing:
public void beginYourTask()
{
new AsyncTask<Void, Integer, Void>(){
#Override
public Void doInBackground(Void...params){
publishProgress(setRules());
publishProgress(getLogs());
publishProgress(getChartView());
return null;
}
#Override
public void onProgressUpdate(Integer... progress) {
progressBar.setProgress(progress[0]);
}
#Override
public void onPostExecute(Void result){
progressBar.setVisibility(View.GONE);
}
}.execute();
}
That's the idea, there may be typos.
The tutorial from your link hasn't been updated for a while and normally you don't want to use a Thread in this situation because AsyncTask is designed to save you from all the thread problems you would encounter while using Thread
Read the documentation on this and you'll know when to use a Thread or an AsyncTask
This question already has answers here:
Updating Android UI using threads
(4 answers)
Android toast message from a separate thread class
(1 answer)
Closed 9 years ago.
I'm having some trouble trying to update automaticaly a view in my android activity.
The application display some message like a chat. Im using a ListView to put the message with a ArrayAdapter.
I use this metod to update the ListView
public void loadMessages() {
ArrayList<String> messages = this.dbHelper.getMessages();
conversationArrayAdapter.clear();
conversationArrayAdapter.addAll(messages);
conversationArrayAdapter.notifyDataSetChanged();
}
My idea is to put a thread that call that metod, but when i try to do this i have the following error.
Only the original thread that created a view hierarchy can touch its view.
because you are trying to access or update UI elements from Thread . to avoid this error you will meed to use runOnUiThread for updating UI from Thread as :
Your_Current_Activity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
// Update UI here
loadMessages();
}
});
and second solution is use AsyncTask instead of thread
Use this code.
public class MainActivity extends Activity
{
private Timer autoUpdate;
#Override
public void onResume()
{
super.onResume();
autoUpdate = new Timer();
autoUpdate.schedule(new TimerTask()
{
#Override
public void run()
{
runOnUiThread(new Runnable()
{
public void run()
{
updateScore();
}
});
}
}, 0, 5000); // updates each 5 seconds
}
#Override
public void onPause()
{
autoUpdate.cancel();
super.onPause();
}
#Override
public void onCreate(Bundle savedInstanceState)
{
// initialize view layout
super.onCreate(savedInstanceState);
setContentView(R.layout.cleanermain);
super.onResume();
}
private void updateScore()
{
// decide output
// update cricket score
}
}
UI should be updated only from the UI (Main) thread.
Here is a solution using AsyncTask.
public void asyncCallWithSchedule() {
final Handler handler = new Handler();
Timer timer = new Timer();
TimerTask doAsynchronousTask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
public void run() {
try {
new SearchAsync().execute(txtSearch.getText().toString());
}
});
}
};
timer.schedule(doAsynchronousTask, 0, 2000);
}
}
AsyncTask class:
private class SearchAsync extends
AsyncTask < String, Object, List < Users >> {
#Override
protected List < Users > doInBackground(String...params) {
// Call DB here
return null;
}
#Override
protected void onPostExecute(List < Users > result) {
super.onPostExecute(result);
// Update UI here
}
}
Simple:
public void loadMessages() {
ArrayList<String> messages = this.dbHelper.getMessages();
conversationArrayAdapter.clear();
conversationArrayAdapter.addAll(messages);
new Handler(Looper.getMainLooper()).post(new Runnable() {
public void run() {
conversationArrayAdapter.notifyDataSetChanged();
}
});
}
I have a problem with pageviewer. I want to my page scroll every two seconds. I try something like this:
handler.postDelayed(new Runnable() {
public void run() {
viewPager.setCurrentItem(viewPager.getCurrentItem()+1, true);
}
},2000);
But it works only when I start activity. If i put this code to public void onPageSelected(int page) it works but I want to do when I click and manually scroll page I want to stop this handler but it doeasn't work. How I can do that?
You should define your Runnable and Handler like this:
private boolean pagerMoved = false;
private static final long ANIM_VIEWPAGER_DELAY = 2000;
private Handler h = new Handler();
private Runnable animateViewPager = new Runnable() {
public void run() {
if (!pagerMoved) {
viewPager.setCurrentItem(viewPager.getCurrentItem()+1, true);
h.postDelayed(animationFrame, ANIM_VIEWPAGER_DELAY);
}
}
};
Make sure that you setup and tear down in your onPause & onResume methods
#Override
public void onPause() {
super.onPause();
if (h != null) {
h.removeCallbacks(animateViewPager);
}
}
#Override
public void onResume() {
super.onResume();
h.postDelayed(animateViewPager, ANIM_VIEWPAGER_DELAY);
}
Finally, you'll need to listen for a touch event on your viewpager so that you can set pagerMoved to true (which will then stop further automatic page transitions).