Android Fragments and Separate Threads - android

I am using the android ViewPager : http://developer.android.com/reference/android/support/v4/view/ViewPager.html
This class allows you to basically slap a bunch of Views or Fragments into a group and page through them easily. My problem occurs when I try to create a separate thread to randomly shuffle through the views.
I extended a thread which would call setCurrentItem on my ViewPager which I passed in through an argument. When I did that I received this:
java.lang.IllegalStateException: Must be called from main thread of process
I figured all I would have to do to fix that would be to call a method from my activityfragment so I created changePageFromActivity to do the dirty work and called that by passing my activity to my thread. But that didn't work either. Below is the full stack trace:
FATAL EXCEPTION: Thread-11
java.lang.IllegalStateException: Must be called from main thread of process
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1392)
at android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:431)
at android.support.v4.app.FragmentStatePagerAdapter.finishUpdate(FragmentStatePagerAdapter.java:160)
at android.support.v4.view.ViewPager.populate(ViewPager.java:804)
at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:433)
at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:405)
at android.support.v4.view.ViewPager.setCurrentItem(ViewPager.java:397)
at com.lyansoft.music_visualizer.MusicVisualizerActivity.changePageFromActivity(MusicVisualizerActivity.java:144)
at com.lyansoft.music_visualizer.ShuffleThread.run(ShuffleThread.java:38)
After doing some research I gathered that Fragments are easily destroyed and result in a different thread so to prevent any problems ViewPager just made sure that the method I wanted had to be called from the main activity.
So my question is:
Is it possible to inject something along the lines of
run() { while(condition) { methodIWantHere(); sleep(timeout); } }
inside my main activity's thread without disrupting it?
OR
What is a better design pattern to achieve the effect I would like? (which is to change the view at consistent specified intervals)

A Handler would be an obvious choice.
final Runnable changeView = new Runnable()
{
public void run()
{
methodIWantHere();
handler.postDelayed(this, timeout);
}
};
handler.postDelayed(changeView, timeout);

Try to execute your piece of code by using
runOnUiThread(Runnable action).

Related

Android Threading (AsyncTasks) acting a little weird

From my main thread, I start an AsyncTask which will go through a list of images and for each image, it will do some processing on it. So basically, there's a for loop and inside it, another AsyncTask is called. I use an instance of a class which holds the boolean value for checking if each image is done with its processing, its called a dummyStructure.
Code of the main thread:
new BatchProcessor().execute()
the doInBackground of the BatchProcessor:
protected Void doInBackground(Void... params){
while(dummyStructure.isWorking())
{
//Try loop
thread.sleep(1000);
}
dummyStructure.setIsWorking(true); //basically sets the flag to true
for(String s: pictureList)
{
RunTheProcessingLoop().execute();
}
The Problem:
I tried debugging, and here's what the problem is imo, if I remove the line just outside the while loop dummyStrucutre.setIsWorking(true) then there are multiple asyncTasks called even before it finishes, and basically everything gets screwed up. However, if I don't remove that line, then the BatchProcessor AsyncTask gets caught in the while loop, while as the RunTheProcessingLoop AsyncTask never executes beyond its onPreExecute()(debugged to know that, I used Log.e() in every method of that asyncTask).
Definitely I'm missing something, any help? Thanks a lot! :)
What you are encountering is asynctasks getting piled up because you are starting one from another and not exiting the first. This is because the asynctasks are handled serially by a single thread by default. If you want to use multiple threads in parallel, you'd need to use your own thread executor. See the AsyncTask documentation for more details.
So after 2 days of posting this question and finding out more about what people posted, I figured this:
My main thread called for an AsyncTask and I wanted to wait for that AsyncTask to finish. So I used a boolean flag which the AsyncTask sets to false once it is done and I can queue another task. Here's the code:
class mExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
}}
Now, all you need to do is, whatever task/method/etc you want to run Asynchronously, simply create a thread and push it in that class, example:
Thread t = new Thread(new Runnable() {
public void run() {
new someshit().execute();
}
});
new mExecutor().execute(t);
and Tada! Now they both won't be queued/synchronized but would run in parallel.
If I am wrong, please correct me! Thanks! :)

main activity implementing a listener interface for updating UI

So first of all I figured out how to update the UI from another thread. I'm using AsyncTask and the Handler class for anything else
But in the first place I did it wrong and now I really want to find out what was wrong.
So my first try was to create an java interface MyListener with a methode updateUI() and let the main activity implement that listener interface. Than I registered the main activity as listener in a thread that handles http requests. From that thread I called MyListener.updateUI() after the request was done. updateUI() updates some textviews with the request's result.
This doesn't work in android. And I did not expect that. And I don't know why.
The exception told me that I have to make these updates from the UI thread.
I don't understand why updateUI() doesn't run in the UI-thread when I call MyListener.updateUI() with the main activity implementing updateUI() from the listener interface.
Could you explain me this behavior?
I see that you have excuted the http request in other thread, but you do not use AsyncTask, right? I suggest that you should use that class, excute http request in doInBackground method (because it works off main thread), and call MyListener.updateUI() on onPostExcute because this method is always get called in main thread. Have a try and let me know if it works or not.
Doesn't matter if you are calling MyListener.updateUI() in your MainActivity's onCreate() method or any other if you are doing that from another Thread it will throw you an exception, because you can update View's only in the applications Main Thread. To achieve this you can do something like that :
public static void updateUI(){
((Activity) mContext).runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
// update your UI here and be sure to give the activity context, not getApplicationContext();
}
});
}
put this in your Activity and in your onCreate() do something like that :
public static Context mContext;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mContext = this;
}
and call updateUI() from whatever thread you want to do it.
Of course this isn't the only way you can update your interface.You can take a look at AsyncTask implementation in Android SDK. It's a really powerful class and I think it will do the thing which you want more efficiently.
You were getting this error because the control was still inside the Background thread... though you have called the interface method which has been implemented in you activity.So in order to get the control back to your UI thread you need to post using a handler.The control does not goes from the Background thread to the UI thread by just calling the method which is located in your activity.
Hope this clarifies your doubt.

Showing Dialog in SurfaceView

My game is drawn onto a SurfaceView. I am using a Dialog as a level completion screen, but cannot get it to show (Dialog.show()).
I keep getting the following error:
01-30 16:45:34.425: E/AndroidRuntime(3415): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
I have a Game class which extends Activity and holds the SurfaceView. I think runOnUiThread() may be the solution, but after tireless searching have no idea how to implement it inside my SurfaceView.
Any help is appreciated and I will post my code if requested (just not sure which bits are actually relevant at the moment)
You're attempting to modify the UI thread from a worker thread which will give these errors. To prevent this try making a call to the runOnUiThread() method...
Game.this.runOnUiThread(new Runnable() {
public void run() {
customDialogObject.show();
}
});

Updating UI of Android activity from some other class running on different thread

I have a activity with a Listview and a adapter attached to it. I have a class which syncs data and hold it. (I think we should not care about from where data is coming) and it runs on a different thread. Now I want to know the clean way to update adapter but We should not call any function of activity from that class as it runs on different thread.
One way I know is to create handler in activity and pass it to other class and use it from there. But I want to know if activity is in background then activity's function can be called by UI thread or only when activity come in foreground.
I do not want to miss any update and want to update activity when it is in foreground.
If you want to update your listView from another class you can have a few ways to do that.
Send broadcastIntent from your worker class and add BroadcastReceiver to your activity and when you receive the right message, update your listview.
Second way is to create private or public class in your Activity which extends AsyncTask and in your doInBackground() do your work and in onPostExecute(result) update your listview.
Third way which I can imagine, but I don't think it's the best way create a static method is your activity which you can use from any other class for updatiogn your UI.
The best thing which you can use here at least in my opinion is AsyncTask.
this code will do what you want:
runOnUiThread(new Runnable() {
#Override
public void run() {
//your actions
}
});
We should not call any function of activity from that class as it runs on different thread.
That's wrong. (in java an object is not running in a thread. What you can say is that a method is running in the thread from which the method was called)
In Androïd (and in most UI frameworks) the rule is this:
You can only call a method updating UI from the UI thread.
If you have some code running on a thread (not the ui thread) and if that code need to update the UI : you can use the Handler of the UI-thread to post UI update code to the UI-thread. If the activity is not in the foreground when you post something to update it's UI : nevermind! the code you just post will be executed at some point in the future.
You should use AsyncTask, take a look at http://developer.android.com/reference/android/os/AsyncTask.html

Android Looper.prepare() and AsyncTask

Hi I have some questions regarding Looper.prepare() and AsyncTasks.
In my app I have an AsyncTask that's starting other AsyncTasks. I had 2 AsyncTasks Search and GetImage. The GetImage task is executed several times within the Search task. It works fine.
However recently I implemented image caching as described here:
http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html
After I implemented this I started getting intermittent crashes
02-09 17:40:43.334: W/System.err(25652): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
I'm not sure where I should call prepare(). Here's a rough outline of the code
Search extends AsyncTask{
#Override
protected void doInBackground(){
ArrayList<Object> objs = getDataFromServer();
ArrayList<View> views = new ArrayList<View>();
for(Object o: objs){
//multiple AsyncTasks may be started while creating views
views.add(createView(o));
}
for(View v: views){
publishProgess(v);
}
}
}
public View createView(Object o){
//create a view
ImageView iv = .....;
ImageDownloader.getInstance().download(url,iv);
}
The ImageDownloader can be seen in the link above, in it is another AsyncTask to download the image aswell. It also contains a Handler and Runnable for purging the cache which is reset every time a download occurs. I did make one change to the ImageDownloader, I made it a singleton.
public static ImageDownloader getInstance(){
if(instance == null){
//tried adding it here but it results in occasional
//cannot create more than one looper per thread error
Looper.prepare();
instance= new ImageDownloader();
}
return instance;
}
The ImageDownloader download method may be called 10's of times, which is creating an AysncTask for each of the downloads. So I've been scratching my head for the last few days, Hope you guys can help.
What is really going on is you are attempting to perform something on a background thread that requires the UI thread to run.
The Looper is a part of the system that ensures that transactions are done in order, and the device is responding as it should.
95% of the time when you get the Looper error, what it really means is you need to move part of your code to the UI thread, in Asynctask this means moving it to either onPostExecute or onProgressUpdate.
In your case it appears as if you are adding views, which is part of the UI and therefor would cause a problem. If that is not in fact what is causing the problem, an examination of the stacktrace should give you some clues.
As a side note, if you MUST call Looper.prepare() I would call it at the beginning of your thread. However, this is generally recommended to avoid the need to call it.

Categories

Resources