I'm running a simple application that on load shows a list of items, when clicked opens up a video player playing the media.
I'm having an issue that when destroying the player and returning to the list view, that there is a long delay before any events are being registered. This is due to the tear down of the player taking a few seconds.
I had asked the support crew and they recommended the best way to go about it was to simply hide the player and show again when new media was clicked but I've been reading into threads and it seems like this might be the best approach to use instead? I'm not exactly sure how they work though.
If I kill the player service in onDestroy will initiating a new one still be held up?
public class DemoPlayer extends Activity {
private Player player;
private SeekBarScrubber scrubber;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.player);
// Set the media url holder
String mediaUrl = "";
player = new Player(
// inject player container view into ADK Player
(ViewGroup) this.findViewById(R.id.tpPlayer)
);
// Build application UI
scrubber = (SeekBarScrubber) findViewById(R.id.scrubber);
scrubber.setMediaPlayerControl(player.asMediaPlayerControl());
final Activity activity = this;
try {
URI mediaUri = new URI(mediaUrl);
player.playReleaseUrl(mediaUri);
} catch (URISyntaxException e) {
AlertDialog alertDialog = new AlertDialog.Builder(activity).create();
alertDialog.setTitle("Invalid URL");
alertDialog.setMessage("Issue loading media");
alertDialog.setCancelable(true);
alertDialog.show();
}
}
#Override
protected void onDestroy() {
new Thread(new Runnable() {
public void run() {
if (player != null)
player.getLifecycle().destroy();
player = null;
}
}).start();
ViewGroup viewGroup = (ViewGroup) this.findViewById(R.id.tpPlayer);
viewGroup.removeView(scrubber);
scrubber = null;
super.onDestroy();
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
finish();// call finish() on click of back button
}
return super.onKeyDown(keyCode, event);
}
}
TL;DR: How to kill player without holding up the UI thread since it takes a few seconds to destroy the player. Also will loading a new player quickly cause issues?
Currently get the error below
failed access media controller because player thread is shutdown
Skipped 374 frames! The application may be doing too much work on its main thread.
The player is attached to the view when the view is created not the other way around. Therefore i think that your best option is to move the player to a singleton and start/stop him regardless of the activity lifecycle. of course u can do the shutdown on back thread (i suggest u read about threadpools and executers because i rarely see Thread.start() on android.
also:
ViewGroup viewGroup = (ViewGroup) this.findViewById(R.id.tpPlayer);
viewGroup.removeView(scrubber);
scrubber = null;
this is unnecessary.
Related
this is my first question on stack (great community, thanks!)
So, I have this problem:
I'm developing an android application with a tab layout. Whenever I navigate to the "Status" tab, I'd like to show a circular ProgressBar that is updated according to the value of a variable. Let's call this variable prog. Moreover, I'd like to display in the center of the ProgressBar a TextView that displays the same value of prog.
I have came up with this part of code:
public class StatusFragment extends Fragment
{
private ProgressBar torqueRefProgressBar;
private TextView torqueRefTextView;
RunningThreadExample r;
private Thread t1;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
//Inflate the layout for this fragment
return ...;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
// Initializations and other parts not relevant for the question...
this.torqueRefProgressBar = getView().findViewById(R.id.torqueRefProgressBar);
this.torqueRefTextView = getView().findViewById(R.id.torqueRefTextView);
t1 = new Thread( this.torqueRefProgressBar, this.torqueRefTextView)); // passing arguments by value but values are references to the objects, therefore I'm passing by ref
t1.start();
}
}
In this Fragment I create the ProgressBar and the TextView and then pass them to the Runnable as you can see below
public class RunningThreadExample implements Runnable
{
ProgressBar torqueRefProgBar_thread;
TextView torqueRefTextView_thread;
public RunningThreadExample(ProgressBar progressBar, TextView textView)
{
this.torqueRefProgBar_thread = progressBar;
this.torqueRefTextView_thread = textView;
this.endScale = this.torqueRefProgBar_thread.getMax();
}
#Override
public void run()
{
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
float i = 0;
double temp = 0;
while(Running)
{
try
{
Thread.sleep(1000/60); // update freq 60Hz
}
catch (InterruptedException e)
{
e.printStackTrace();
}
temp = getCurrentVariableValue(); // Not shown here (returns a double)
this.torqueRefProgBar_thread.setProgress((int) temp);
this.torqueRefTextView_thread.setText(String.valueOf(temp));
}
Log.i(INFO_TAG,"Finishing thread!!!!!");
}
}
I'd like this update to run all the time the app is working (for this reason I've neglected AsyncTask). After a while, it happens that the ProgressBar stops updating, while the TextView continues to work without any problem.
I've been reading that a possible cause could be that the calling setProgress(temp) might stuck the UI. However I can't understand while the TextView is still working.
Can you suggest a better way to make the progressBar update?
Thank you!
See this thread:
Android basics: running code in the UI thread
So to summarize, Android requires certain types and sections of code to run in a specific context. Many tools such as Handlers are available to help with these tasks but it was difficult to learn to begin with.
You cannot affect View from any Thread other than Main Thread. Do the following in run() method
runOnUiThread(new Runnable() {
//
run(){
this.torqueRefProgBar_thread.setProgress((int) temp);
} this.torqueRefTextView_thread.setText(String.valueOf(temp));
);
I have a Timer in my App that infinitely runs an Animation. like this:
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
//Running Animation Code
}
});
}
}, 1000, 1000);
Now I realized that this code runs even if user click Back Button of android. if fact it runs in the background and it seems uses a lot of memory.
I need this code run ONLY if user in the app. In fact when user click on Back Button, this Timer goes to end and if user clicks on Home Button, after a while that user doesn't use the App, terminates this Timer.
What I need is to prevent using memory. Because i realized if this codes runs a while, App freezes! I need a normal behavior.
If your Activity is the last element in the BackStack, then it will be put in the background as if you pressed the Home button.
As such, the onPause() method is triggered.
You can thus cancel your animation there.
#Override protected void onPause() {
this.timer.cancel();
}
You should as well start your animation in the onResume() method.
Note that onResume() is also called right after onCreate(); so it's even suitable to start the animation from a cold app start.
#Override protected void onResume() {
this.timer.scheduleAtFixedRate(...);
}
onPause() will be also called if you start another Application from your app (e.g: a Ringtone Picker). In the same way, when you head back to your app, onResume() will be triggered.
There is no need to add the same line of code in onBackPressed().
Also, what's the point in stopping the animation in onStop() or onDestroy()?
Do it in onPause() already. When your are app goes into the background, the animation will already be canceled and won't be using as much memory.
Don't know why I see such complicated answers.
You can do it like this, in onBackPressed() or onDestroy(), whatever suits you.
if (t != null) {
t.cancel();
}
If you need, you can start timer in onResume() and cancel it in onStop(), it entirely depend on you requirement.
If a caller wants to terminate a timer's task execution thread
rapidly, the caller should invoke the timer's cancel method. - Android Timer documentation
You should also see purge and
How to stop the Timer in android?
Disclaimer: This might not be the 100% best way to do this and it might be considered bad practice by some.
I have used the below code in a production app and it works. I have however edited it (removed app specific references and code) into a basic sample that should give you a very good start.
The static mIsAppVisible variable can be called anywhere (via your App class) in your app to check if code should run based on the condition that the app needs to be in focus/visible.
You can also check mIsAppInBackground in your activities that extend ParentActivity to see if the app is actually interactive, etc.
public class App extends Application {
public static boolean mIsAppVisible = false;
...
}
Create a "Parent" activity class, that all your other activities extend.
public class ParentActivity extends Activity {
public static boolean mIsBackPressed = false;
public static boolean mIsAppInBackground = false;
private static boolean mIsWindowFocused = false;
public boolean mFailed = false;
private boolean mWasScreenOn = true;
#Override
protected void onStart() {
applicationWillEnterForeground();
super.onStart();
}
#Override
protected void onStop() {
super.onStop();
applicationDidEnterBackground();
}
#Override
public void finish() {
super.finish();
// If something calls "finish()" it needs to behave similarly to
// pressing the back button to "close" an activity.
mIsBackPressed = true;
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
mIsWindowFocused = hasFocus;
if (mIsBackPressed && !hasFocus) {
mIsBackPressed = false;
mIsWindowFocused = true;
}
if (!mIsWindowFocused && mFailed)
applicationDidEnterBackground();
if (isScreenOn() && App.mIsAppVisible && hasFocus) {
// App is back in focus. Do something here...
// this can occur when the notification shade is
// pulled down and hidden again, for example.
}
super.onWindowFocusChanged(hasFocus);
}
#Override
public void onResume() {
super.onResume();
if (!mWasScreenOn && mIsWindowFocused)
onWindowFocusChanged(true);
}
#Override
public void onBackPressed() {
// this is for any "sub" activities that you might have
if (!(this instanceof MainActivity))
mIsBackPressed = true;
if (isTaskRoot()) {
// If we are "closing" the app
App.mIsAppVisible = false;
super.onBackPressed();
} else
super.onBackPressed();
}
private void applicationWillEnterForeground() {
if (mIsAppInBackground) {
mIsAppInBackground = false;
App.mIsAppVisible = true;
// App is back in foreground. Do something here...
// this happens when the app was backgrounded and is
// now returning
} else
mFailed = false;
}
private void applicationDidEnterBackground() {
if (!mIsWindowFocused || !isScreenOn()) {
mIsAppInBackground = true;
App.mIsAppVisible = false;
mFailed = false;
// App is not in focus. Do something here...
} else if (!mFailed)
mFailed = true;
}
private boolean isScreenOn() {
boolean screenState = false;
try {
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
screenState = powerManager.isInteractive();
} catch (Exception e) {
Log.e(TAG, "isScreenOn", e);
}
mWasScreenOn = screenState;
return screenState;
}
}
For your use you might want to create a method in your activity (code snippet assumes MainActivity) that handles the animation to call the t.cancel(); method that penguin suggested. You could then in the ParentActivity.applicationDidEnterBackground() method add the following:
if (this instanceof MainActivity) {
((MainActivity) this).cancelTimer();
}
Or you could add the timer to the ParentActivity class and then not need the instanceof check or the extra method.
I need execute a method when the fragment is visible (to the user).
Example:
I have 2 buttons (button 1 and button 2) ,
2 fragments(fragment 1 and fragment 2)
and the method loadImages() inside the class fragment 2.
when I press "button2" I want to replace fragment 1 by fragment 2
and then after the fragment 2 is visible (to the user) call loadImages().
I tried to use onResume() in the fragment class but it calls the method before the fragment is visible and it makes some delay to the transition.
I tried setUserVisibleHint() too and did not work.
A good example is the Instagram app. when you click on profile it loads the profile activity first and then import all the images.
I hope someone can help me. I will appreciate your help so much. Thank you.
Use the ViewTreeObserver callbacks:
#Override
public void onViewCreated(View v, Bundle savedInstanceState) {
super.onViewCreated(v, savedInstanceState);
final View view = v;
// Add a callback to be invoked when the view is drawn
view.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
#Override
public void onDraw() {
// Immediately detach the listener so it only is called once
view.getViewTreeObserver().removeOnDrawListener(this);
// You're visible! Do your stuff.
loadImages();
}
});
}
I'm a little confused by what you are trying to do. It sounds like the images are loading too fast for you... so does that mean that you have the images ready to display? And that is a bad thing?
My guess (and this is just a guess) is that Instagram does not have the profile pictures in memory, so they have to make an API call to retrieve them, which is why they show up on a delay. If the same is the case for you, consider starting an AsyncTask in the onResume method of the fragment. Do whatever loading you need to do for the images in the background, and then make the images appear in the onPostExecute callback on the main thread. Make sure you only start the task if the images are not already loaded.
However, if you already have the images loaded in memory, and you just want a delay before they appear to the user, then you can do a postDelayed method on Handler. Something like this:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
loadImages();
}
}, 1000);
Edit
As kcoppock points out, the handler code is pretty bad. I meant it to be a quick example, but it is so wrong I should not have included it in the first place. A more complete answer would be:
private Handler handler;
public void onResume(){
super.onResume();
if(handler == null){
handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
loadImages();
}
}, 1000);
}
}
public void onDestroyView(){
super.onDestroyView();
handler.removeCallbacksAndMessages(null);
handler = null;
}
Use the onActivityCreated() callBck
I am having a problem updating the view in android every x seconds.
To get to know how android works I am writing a small game.
It has a GameController which holds the game loop. Inside the loop, the logic is executed and afterwards the gui will be informed about the changes.
The problem is that the changes cannot be seen in the view. The view stays the same until the game loop finishes and updates then only once.
Here is my code (the important parts):
GameController.java:
IGui gui;
public void play() {
while (playing) {
getAndProcessInput();
updateGui();
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
}
}
}
private void updateGui() {
gui.setPlayer(x, y);
// ...
}
GameActivity.java:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
GridView gridview = (GridView) findViewById(R.id.GridView1);
TextAdapter = new TextAdapter(this);
gridview.setAdapter(textAdapter);
GameController c = new GameController();
// here, the game loop shoud start.
// what i tried:
// new Thread(c).start(); <-- causes crash
// c.play(); <-- causes view to frease until game loop is done
this.runOnUiThread(c); <-- causes view to frease until game loop is done
}
TextAdapter.java:
public class TextAdapter extends BaseAdapter implements IGui {
private final Context context;
private final String[] texts;
public TextAdapter(Context context) {
this.context = context;
texts = new String[height * width];
for (int i = 0; i < texts.length; i++) {
texts[i] = " ";
}
}
public int getCount() {
return height * width;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
TextView tv;
if (convertView == null) {
tv = new TextView(context);
tv.setLayoutParams(new GridView.LayoutParams(25, 25));
} else {
tv = (TextView) convertView;
}
tv.setText(texts[position]);
return tv; // <-- this happens only when game loop is done, not everytime something changed
}
#Override
public void setPlayer(int x, int y) {
texts[width * y + x] = "X";
// this.notifyDataSetChanged(); <-- this does not help (view still freases)
// gridview.invalidateViews(); does not help either
}
}
I googled a lot and tried a lot as well (and I do know that similar questions where asked here already, but they did not help me either), but somehow it just does not work.
I cannot get it do work that the view and logic run on different theads in android, and if they run on the same thread the logic blocks the view.
Any help would be greatly appreciated.
// edit:
If I try new Thread(c).start(); LogCat sais:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
And if I add Looper.prepare(); :
java.lang.RuntimeException: Unable to start activity ComponentInfo{GameActivity}: java.lang.RuntimeException: Only one Looper may be created per thread
If I try this.runOnUiThread(c); there are no errors.
First, this doesn't seems like the way to crate a game, you will need to use SurfaceView, or GLSurfaceView to better do what you want.
You can also look for Cocos2d for android, it's a 2D platform (that was ported from iPhone) that makes you life easier:
http://code.google.com/p/cocos2d-android/
I muse warn you though, I tried it a couple months back, and it was not production grade yet, it did crash from time to time.
Anyway, if you still want to continue heading your way I'll try answering your question:
I think you are messing too much with the way these stuff should work. try understanding first how handlers work with threads.
Do anything you want on your thread:
new Thread(new Runnable()
{
#Override
public void run()
{
try
{
calculateGameChangesHere();
handler.sendEmptyMessage(SUCCESS);
}
catch (Exception e)
{
handler.sendEmptyMessage(FAILURE);
}
}
}).start();
When your data is ready, tell the handler to put it in a view and show it:
protected Handler handler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
if (msg.what == SUCCESS)
{
setCalculatedDataToaView(); // the data you calculated from your thread can now be shown in one of your views.
}
else if (msg.what == FAILURE)
{
errorHandlerHere();//could be your toasts or any other error handling...
}
}
};
This goes to everything that requires heavy processing/networking that shouldn't block your UI thread.
Hope this helps.
Not sure what you are trying to achieve by refreshing a listView every few seconds. Anyways if you are writing some 2d games, you have to use SurfaceView , which is especially meant for continuous refreshing.
Had the same problem and solved it using a pretty simple code.
Tips:
GridView must be refreshed by the UI thread
To display every change you must keep the loop and the Sleep method away from the UI thread
Solution:
Method to update the grid (put on your Activity that you build your GridView at the first place or wherever)
public void UpdateGrid(){
//your logic
//your way to change grid values (there are tones of other ways)
CustomGridAdapter cga = new CustomGridAdapter(this, values);
gridView.setAdapter(cga);
gridView.invalidateViews();
}
Build the non UI Thread that does the work
public class DataReceiver implements Runnable {
private MainActivity mainActivity;
public DataReceiver(MainActivity ma) {
mainActivity = ma;
}
#Override
public void run() {
for (int i = 0; i < 100; i++) {
mainActivity.runOnUiThread(new Runnable() {
public void run() {
//update the grid here
mainActivity.UpdateGrid();
}
});
//sleep here
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
I have an android app I am just experimenting things on and I cannot seem to figure out why my app force closes when I update a TextView via a while loop. When I comment out the updateText method it runs fine.
public class GameThread extends Thread {
Thread t;
private int i;
private boolean running;
private long sleepTime;
GameView gv;
public GameThread() {
t = new Thread(this);
t.start();
i = 0;
sleepTime = 1000;
}
public void initView(GameView v) {
this.gv = v;
}
public void setRunning(boolean b) {
this.running = b;
}
public boolean getRunning() {
return running;
}
public void run() {
while(running) {
i++;
update();
try {
t.sleep(sleepTime);
} catch(InterruptedException e) {
}
}
}
public void update() {
gv.setText(i); // when this is uncommented, it causes force close
Log.v("Semajhan", "i = " + i);
}
public class GameView extends LinearLayout {
public TextView tv;
public GameView(Context c) {
super(c);
this.setBackgroundColor(Color.WHITE);
tv = new TextView(c);
tv.setTextColor(Color.BLACK);
tv.setTextSize(20);
this.addView(tv);
}
public void setText(int i) {
tv.setText("i count: " + i);
}
public class Exp extends Activity {
GameThread t;
GameView v;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
v = new GameView(this);
setContentView(v);
t = new GameThread();
t.setRunning(true);
t.initView(v);
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (t.getRunning() == true) {
t.setRunning(false);
Log.v("Semajhan", "STOPPED");
} else {
t.setRunning(true);
Log.v("Semajhan", "RESTART");
}
}
return true;
}
protected void onDestroy() {
Log.v("Semajhan", "DESTROYING");
super.onDestroy();
}
protected void onStop() {
Log.v("Semajhan", "Stopping");
super.onStop();
}
I though i'd post the whole app since it is relatively small and so that I could get some help without confusion.
First, when you get a Force Close dialog, use adb logcat, DDMS, or the DDMS perspective in Eclipse to examine LogCat and look at the stack trace associated with your crash.
In this case, your exception will be something to the effect of "Cannot modify the user interface from a non-UI thread". You are attempting to call setText() from a background thread, which is not supported.
Using a GameThread makes sense if you are using 2D/3D graphics. It is not an appropriate pattern for widget-based applications. There are many, many, many, many examples that demonstrate how to create widget-based applications without the use of a GameThread.
You have to call it from the UI thread.
For more info check: Painless Threading .
If you decide to use a Handler, the easiest solution for you will be to:
Extend a View, override it's onDraw , in it draw the game objects, after you have calculated the game data for them first of course
The Handler: (in your Activity)
private Handler playHandler = new Handler() {
public void handleMessage(Message msg) {
gameView.postInvalidate(); // gameView is the View that you extended
}
};
The game thread has a simple
Message.obtain(playHandler).sendToTarget();
In 2 words, the View is responsible for the drawing (you can move the calculations in a separate class, and call it before the onDraw), the thread is responsible only for scheduled calls to the Handler, and the Handler is responsible only to tell the View to redraw itself.
You cannot update the UI of your app outside of the UI Thread, which is the 'main' thread you start in. In onCreate(Context) of you app, you are creating the game thread object, which is what is doing the updating of your UI.
You should use a Handler:
http://developer.android.com/reference/android/os/Handler.html