I have created an ExoPlayer inside a fragment. If during the playback I press the Home button, and get back to the player, the player seems to be empty, the play/pause button does not work anymore. The video is gone. The video player view seems to be completely empty.
public class PlaybackFragment extends Fragment implements View.OnClickListener, EventListener, PlaybackControlView.VisibilityListener {
private static final String TAG = PlaybackFragment.class.getSimpleName();
Video mVideo; //Video object passed in for playback
SimpleExoPlayerView mExoPlayerView;
private TextView mVideoTitle;
private SimpleExoPlayer mExoPlayer;
private FrameLayout mFullScreenButton; //Holds the fullscreen icon
private Dialog mFullScreenDialog;
private ImageView mFullScreenIcon;
private boolean mExoPlayerFullScreen; //full-screen dialog
private static final String LOG_TAG = PlaybackFragment.class.getSimpleName();
/**
* Root ViewGroup holding the fragment
*/
private ViewGroup mPlayerViewContainer;
/**
* Context used across the class
*/
private Context mContext;
/**
* Activity to which the fragment is added
*/
private Activity mActivity;
/**
* Uri received from calling activity containing Uri to the video to be played
*/
private Uri mUri;
public PlaybackFragment() {
super();
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
Log.d(TAG, "onCreate: ");
super.onCreate(savedInstanceState);
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
Log.d(TAG, "onCreateView: ");
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_playback, container, false);
mVideoTitle = root.findViewById(R.id.playback_video_title);
mExoPlayerView = root.findViewById(R.id.exoPlayerView);
mPlayerViewContainer = (ViewGroup) mExoPlayerView.getParent();
return root;
}
/**
* Sets the Uri to video to be played
* #param mediaUri
*/
public void playUri(Uri mediaUri) {
mUri = mediaUri;
}
#Override
public void onViewStateRestored(#Nullable Bundle savedInstanceState) {
Log.d(TAG, "onViewStateRestored: ");
super.onViewStateRestored(savedInstanceState);
}
private void preparePlayback() {
mExoPlayerView.setControllerVisibilityListener(this);
mExoPlayerView.requestFocus();
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector =
new DefaultTrackSelector(trackSelectionFactory);
mExoPlayer =
ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector);
mExoPlayerView.setPlayer(mExoPlayer);
DataSource.Factory dsFactory = new DefaultDataSourceFactory(mContext,
com.google.android.exoplayer2.util.Util.getUserAgent(mContext, mActivity.getApplicationInfo().processName), null);
// Produces Extractor instances for parsing the media data.
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
MediaSource videoSource = new ExtractorMediaSource(mUri,
dsFactory, extractorsFactory, null, null);
// Prepare the player with the source.
mExoPlayer.prepare(videoSource);
//start player
mExoPlayer.setPlayWhenReady(true);
mExoPlayer.addListener(this);
// initFullscreenDialog();
initFullscreenButton();
}
#Override
public void onVisibilityChange(int visibility) {
}
#Override
public void onTimelineChanged(Timeline timeline, Object manifest) {
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
Log.d(TAG, "onActivityCreated: ");
super.onActivityCreated(savedInstanceState);
mContext = mActivity = getActivity();
preparePlayback();
}
#Override
public void onStart() {
super.onStart();
Log.d(TAG, "onStart: ");
}
#Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
}
#Override
public void onLoadingChanged(boolean isLoading) {
}
#Override
public void onClick(View v) {
}
public void pause() {
mExoPlayer.stop();
}
//Interface to trigger playback-finish event in the parent activity
public interface OnPlayStateListener {
void onPlayFinish();
}
OnPlayStateListener mCallback;
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if(playbackState == Player.STATE_ENDED) {
quitFullScreen(); //Leave fullscreen after playback finished
mCallback.onPlayFinish();
Log.i(LOG_TAG,"onPlayFinish() triggered");
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mCallback = (OnPlayStateListener) context;
} catch (ClassCastException e){
throw new ClassCastException(context.toString() + " must implement OnPlayStateListener");
}
}
#Override
public void onRepeatModeChanged(int repeatMode) {
}
#Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
}
#Override
public void onPlayerError(ExoPlaybackException error) {
Toast.makeText(mContext,error.getLocalizedMessage(),Toast.LENGTH_SHORT).show();
}
#Override
public void onPositionDiscontinuity(int reason) {
}
#Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
}
#Override
public void onSeekProcessed() {
}
private void goFullScreen() {
Util.showFullScreen(mActivity, mExoPlayerView);
// ((ViewGroup) mExoPlayerView.getParent()).removeView(mExoPlayerView);
// mFullScreenDialog.addContentView(mExoPlayerView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
mFullScreenIcon.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.ic_fullscreen_exit));
mExoPlayerFullScreen = true;
// mFullScreenDialog.show();
}
private void quitFullScreen() {
Util.quitFullScreen(mActivity, mPlayerViewContainer, mExoPlayerView);
// ((ViewGroup) mExoPlayerView.getParent()).removeView(mExoPlayerView);
// FrameLayout frame = mActivity.findViewById(R.id.main_media_frame);
// frame.addView(mExoPlayerView);
mExoPlayerFullScreen = false;
// mFullScreenDialog.dismiss();
mFullScreenIcon.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.ic_fullscreen_expand));
}
private void initFullscreenButton() {
PlaybackControlView controlView = mExoPlayerView.findViewById(R.id.exo_controller);
mFullScreenIcon = controlView.findViewById(R.id.exo_fullscreen_icon);
mFullScreenButton = controlView.findViewById(R.id.exo_fullscreen_button);
mFullScreenButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!mExoPlayerFullScreen) {
goFullScreen();
}
else {
quitFullScreen();
}
}
});
}
private void initFullscreenDialog() {
//The following uses anonymous class to create a custom dialog
mFullScreenDialog = new Dialog(mContext, android.R.style.Theme_DeviceDefault_NoActionBar_Fullscreen) {
#Override
public void onBackPressed() {
if(mExoPlayerFullScreen) {
quitFullScreen();
}
super.onBackPressed();
}
};
}
#Override
public void onDestroy() {
super.onDestroy();
if(mExoPlayer != null) mExoPlayer.release();
Log.d(TAG, "onDestroy: ");
}
/**
* Moves forward or backward by step milliseconds <br/>
* #param step specifies step size. It is a positive or negative integer for moving forward and
* backward, respectively.
*/
public void movePlayPos(int step) {
mExoPlayer.seekTo(mExoPlayer.getCurrentPosition() + step);
}
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
Log.d(TAG, "onSaveInstanceState: ");
super.onSaveInstanceState(outState);
outState.putLong("position", mExoPlayer.getCurrentPosition());
}
}
I have checked the fragment lifecycle in Logcat, but it seems that after I get back to the player activity, only the fragment's onStart() is called. So, there is no way to restore the state. Any ideas how I can save a video playback position upon pause and resume playback on the activity's onResume?
You can save the current position of exoplayer in onPause() and then seek to that position in onResume().
try this:
long position;
#Override
protected void onPause() {
super.onPause();
if(mExoPlayerView != null && mExoPlayerView.getPlayWhenReady()) {
position = mExoPlayerView.getCurrentPosition();
mExoPlayerView.setPlayWhenReady(false);
}
}
#Override
protected void onResume() {
super.onResume();
if(mExoPlayerView != null) {
mExoPlayerView.seekTo(position);
mExoPlayerView.setPlayWhenReady(true);
}
}
Related
First, none of the solution worked provided on stackoverflow because of either change in the Android or my problem was totally different.
What I want to achive - I want to detect when my view (for example - ThirdFragment) comes in focus and becomes visible to user. Also, it should detect when user swipes away from the current view to next view or previous view.
Why I want to do that? - Well, I am creating a media player in one of the view as shown in the example fragment code and user can play any music listed in the view by tapping on the music in the list but when user swipes to next screen it should stop.
What is the problem? - The problem is mediaplayer doesn't stop right after switching from the view but [***] if I swipe two step away, it stops but not cleanly, it jitters in the background.
What I have tried before - I have tried this setUserVisibleHint but it wasn't working because it has been depricated. onPause, onDetach, onStop none is working as mentioned here[***]
/*------Home ViewPager----- */
public class HomeFragment extends Fragment {
private ViewPager viewPager;
private String[] titles = {"First","Second","Third","Fourth"};
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
TextView subtitle = getActivity().findViewById(R.id.toolbar_right_subtitle);
subtitle.setText(titles[0]);
return inflater.inflate(R.layout.fragment_home, container, false);
}
#Override
public void onViewCreated(#NonNull final View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
/* pages */
viewPager = view.findViewById(R.id.interfaces);
InterfaceAdapter interfacePagerAdapter = new InterfaceAdapter(getActivity().getSupportFragmentManager(),0);
viewPager.setAdapter(interfacePagerAdapter);
viewPager.setCurrentItem(0);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
TextView subtitle = getActivity().findViewById(R.id.toolbar_right_subtitle);
subtitle.setText(titles[position]);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}
}
/*---------- My Fragment Adapter --------- */
public class InterfaceAdapter extends FragmentStatePagerAdapter {
public InterfaceAdapter(#NonNull FragmentManager fm, int behavior) {
super(fm, behavior);
}
#NonNull
#Override
public Fragment getItem(int position) {
switch (position){
case 0:
return new FirstFragment();
case 1:
return new SecondFragment();
case 2:
return new ThirdFragment();
case 3:
return new FourthFragment();
default:
return null;
}
}
#Override
public int getCount() {
return 4;
}
}
/*---------- One of my Fragment --------- */
public class ThirdFragment extends Fragment {
//------Media Player ----------
private MusicAdapter musicAdapter;
private List<SongData> songsList;
private ListView listView;
private static MediaPlayer mediaPlayer;
private Handler handler = new Handler();
private Runnable runnable;
private ImageView playButton, prevButton, nextButton;
private SeekBar seekbarPayer;
private TextView playerTime,songTitle;
private int pos = 0;
public ThirdFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
eventViewModels = new ViewModelProvider((FragmentActivity) getContext()).get(EventViewModels.class);
mediaPlayer = new MediaPlayer();
getAllSongs();
return inflater.inflate(R.layout.fragment_focus, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//--------Music Player---------------
mediaPlayer = new MediaPlayer();
playButton = view.findViewById(R.id.playpause_player);
prevButton = view.findViewById(R.id.prev_player);
nextButton = view.findViewById(R.id.next_player);
seekbarPayer = view.findViewById(R.id.seekbar_player);
playerTime = view.findViewById(R.id.playertime);
songTitle = view.findViewById(R.id.songname);
songTitle.setSelected(true);
songsList = new ArrayList<>();
musicAdapter = new MusicAdapter(getActivity(), songsList);
listView = view.findViewById(R.id.songList);
listView.setAdapter(musicAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
pos = position;
playMusicFile(songsList.get(position).getUri(),songsList.get(position).getTitle());
}
});
//play or pause music
playButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
playPause();
}
});
//next music
nextButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (pos < songsList.size() - 1) {
pos++;
} else {
pos = 0;
}
playMusicFile(songsList.get(pos).getUri(),songsList.get(pos).getTitle());
}
});
//next music
prevButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (pos > 0) {
pos--;
} else {
pos = songsList.size() - 1;
}
playMusicFile(songsList.get(pos).getUri(),songsList.get(pos).getTitle());
}
});
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
eventViewModels.getTrigger().observe((LifecycleOwner) getContext(), new Observer<Integer>() {
#Override
public void onChanged(#Nullable final Integer val) {
assert val!=null;
Log.d("ObserverValue",val+"");
}
});
}
#Override
public void onStart() {
super.onStart();
getAllSongs();
Log.d("SystemStatus","Start");
}
#Override
public void onResume() {
super.onResume();
getAllSongs();
Log.d("SystemStatus","Resume");
}
#Override
public void onStop() {
super.onStop();
stopPlayer();
Log.d("SystemStatus","Stop");
}
#Override
public void onDetach() {
super.onDetach();
stopPlayer();
Log.d("SystemStatus","Detach");
}
#Override
public void onPause() {
super.onPause();
stopPlayer();
Log.d("SystemStatus","Pause");
if (eventViewModels != null && eventViewModels.getTrigger().hasObservers()) {
eventViewModels.getTrigger().removeObservers(this);
}
}
//---Media Player-----
private void getAllSongs() {
/*
* get all songs and add it into songlist
*/
}
private void playMusicFile(String filePath,final String title){
if(mediaPlayer!=null) {
mediaPlayer.reset();
try {
mediaPlayer.setDataSource(filePath);
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
stopPlayer();
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void playPause() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}else {
mediaPlayer.pause();
}
}
private void stopPlayer() {
if(mediaPlayer.isPlaying()){
mediaPlayer.stop();
mediaPlayer.reset();
}
}
}
Edit:
Thanks to #ianhanniballake for providing the solution.
It solved my problem "partially". I can stop the mediaplayer but I can't stop my observer observing the event on pause. code update
You're passing in 0 as the behavior, which corresponds to the deprecated BEHAVIOR_SET_USER_VISIBLE_HINT. By using that behavior, setUserVisibleHint() will be called with true when your fragment becomes the current page and false when it is not.
If you switch to the not deprecated BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT, then only the current page will get onResume(). When the page changes, that Fragment will get a callback to onPause() and the newly active fragment will get a call to onResume().
Below is My fragment that contains exoplayer which will inflate with draggable panel in Activity. My problem is while swiping the draggable panel left or right video is closing but audio is still playing in background. If I swipe off the draggable panel audio also needs to closed.Tried many sources couldn't found any solutions.Also In both fragment and Activity while swipping off the draggable panel onPause() method is not triggred. Also public void onClosedToLeft() method also not getting called.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.dragpanelone, container, false);
try {
exoPlayerView = (SimpleExoPlayerView)v.findViewById(R.id.exoplayer);
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelector trackSelector = new DefaultTrackSelector(new AdaptiveTrackSelection.Factory(bandwidthMeter));
exoplayer = ExoPlayerFactory.newSimpleInstance(getActivity(), trackSelector);
Uri uri = Uri.parse(videoURL);
DefaultHttpDataSourceFactory dataSourceFactory = new DefaultHttpDataSourceFactory("exoplayer_video");
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
MediaSource mediaSource = new ExtractorMediaSource(uri, dataSourceFactory, extractorsFactory, null, null);
exoPlayerView.setPlayer(exoplayer);
exoplayer.prepare(mediaSource);
exoplayer.setPlayWhenReady(true);
} catch (Exception e){
e.printStackTrace();
}
return v;
}
#Override
public void onDestroy() {
super.onDestroy();
exoplayer.release();
}
#Override
public void onStop() {
super.onStop();
exoplayer.setPlayWhenReady(false);
}
public void pausePlayback() {
exoplayer.setPlayWhenReady(false); //pauses the playback if it's playing
}
private void releasePlayer() {
if (exoplayer != null) {
exoplayer.release();
exoplayer.setPlayWhenReady(false);
exoplayer = null;
}
}
public static void closePlayer()
{
exoplayer.clearVideoSurface();
}
}
This is my Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeDraggablepanel();
hookDraggablePanelListners();
}
public void initializeDraggablepanel(){
draggablepanel = (DraggablePanel)findViewById(R.id.draggable_panel);
draggablepanel.setFragmentManager(getSupportFragmentManager());
draggablepanel.setTopFragment(new DragpanelFragOne());
draggablepanel.setBottomFragment(new DragPanelTwo());
draggablepanel.setTopViewHeight(550);
draggablepanel.initializeView();
}
private void hookDraggablePanelListners(){
draggablepanel.setDraggableListener(new DraggableListener() {
#Override
public void onMaximized() {
}
#Override
public void onMinimized() {
}
#Override
public void onClosedToLeft() {
DragpanelFragOne.closePlayer();
Intent i = new Intent(MainActivity.this,HomeScreen.class);
startActivity(i);
}
#Override
public void onClosedToRight() {
DragpanelFragOne.closePlayer();
Intent i = new Intent(MainActivity.this,HomeScreen.class);
startActivity(i);
}
});
}
#Override
public void onPause() {
super.onPause();
DragpanelFragOne.closePlayer();
}
#Override
public void onStop() {
super.onStop();
draggablepanel.closeToLeft();
}
#Override
public void onBackPressed() {
draggablepanel.isClickToMinimizeEnabled();
Intent i = new Intent(MainActivity.this,HomeScreen.class);
startActivity(i);
}
}
Solution:
You need to call the method stop() on the exoplayer object whenever you are closing the player, like this:
if (exoPlayer != null) {
exoPlayer.setPlayWhenReady(false);
exoPlayer.stop();
exoPlayer.seekTo(0);
}
Hope this helps.
In my App, I have fragment in which I, alongside with other, play video in ExoPlayer. I set it up as simple as possible and the player itself works flawlessly, even with media session.
But the problem is that on device rotation, the video loads again in background (there seems to be two layers - one standard, functioning normally, loading video and playing it; and second layer in background. I can hear the sound of the video shortly after the activity recreates.
I am stopping and releasing the player and media session, but it didn't help.
MediaFragment.java
public class StepViewFragment extends Fragment implements Player.EventListener{
//vars
public StepViewFragment() {
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
//attach callbacks for buttons
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_step_view, container, false);
//Bind view ButterKnife
ButterKnife.bind(this, rootView);
//get Step from Bundle
Bundle bundle = this.getArguments();
if (bundle != null) {
step = bundle.getParcelable(AppConstants.STEP_BUNDLE_KEY);
recipe = bundle.getParcelable(AppConstants.RECIPE_BUNDLE_KEY);
}
mainHandler = new Handler();
//...
//setup ui
//...
if (step.videoURL.equals("")) {
mediaCard.setVisibility(View.GONE);
} else {
playVideo();
initMediaSession();
}
return rootView;
}
public void initMediaSession (){
mExoPlayer.addListener(this);
mMediaSession = new MediaSessionCompat(getContext(), TAG);
mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS);
mMediaSession.setMediaButtonReceiver(null);
mStateBuilder = new PlaybackStateCompat.Builder()
.setActions(
PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE
);
mMediaSession.setPlaybackState(mStateBuilder.build());
mMediaSession.setCallback(new MySessionCallback());
mMediaSession.setActive(true);
}
public void playVideo() {
if (mExoPlayer == null) {
DefaultTrackSelector trackSelector = new DefaultTrackSelector();
LoadControl loadControl = new DefaultLoadControl();
RenderersFactory renderersFactory = new DefaultRenderersFactory(getContext());
mExoPlayer = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector, loadControl);
mPlayerView.setPlayer(mExoPlayer);
String videoUrl = step.videoURL;
Uri mp4VideoUri = Uri.parse(videoUrl);
String userAgent = Util.getUserAgent(getContext(), "BakingApp");
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(getContext(), userAgent);
ExtractorMediaSource.Factory mediaSource = new ExtractorMediaSource.Factory(dataSourceFactory);
mExoPlayer.prepare(mediaSource.createMediaSource(mp4VideoUri));
mPlayerView.hideController();
mExoPlayer.setPlayWhenReady(true);
}
}
public void releasePlayer() {
mExoPlayer.stop();
mExoPlayer.release();
mExoPlayer = null;
mMediaSession.release();
mPlayerView = null;
}
#Override
public void onStop() {
if (mExoPlayer != null) {
releasePlayer();
}
super.onStop();
}
#Override
public void onDestroyView() {
super.onDestroyView();
if (mExoPlayer != null) {
releasePlayer();
}
}
#Override
public void onPause() {
super.onPause();
if (mExoPlayer != null) {
releasePlayer();
}
}
#Override
public void onDetach() {
super.onDetach();
if (mExoPlayer != null) {
releasePlayer();
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (mExoPlayer != null){
releasePlayer();
}
}
#Override
public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
}
#Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
}
#Override
public void onLoadingChanged(boolean isLoading) {
}
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if ((playbackState == Player.STATE_READY) && playWhenReady) {
mStateBuilder.setState(PlaybackStateCompat.STATE_PLAYING, mExoPlayer.getCurrentPosition(), 1f);
} else if ((playbackState == Player.STATE_READY)){
mStateBuilder.setState(PlaybackStateCompat.STATE_PAUSED, mExoPlayer.getCurrentPosition(), 1f);
}
mMediaSession.setPlaybackState(mStateBuilder.build());
Log.d("HOVNOOOO", "Playback State Changed");
}
#Override
public void onRepeatModeChanged(int repeatMode) {
}
#Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
}
#Override
public void onPlayerError(ExoPlaybackException error) {
}
#Override
public void onPositionDiscontinuity(int reason) {
}
#Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
}
#Override
public void onSeekProcessed() {
}
private class MySessionCallback extends MediaSessionCompat.Callback {
#Override
public void onPlay() {
mExoPlayer.setPlayWhenReady(true);
}
#Override
public void onPause() {
mExoPlayer.setPlayWhenReady(false);
}
}
Solved...
I was using add() to create the Fragment, so on rotation it added new one.
Using replace instead solved the problem.
I'm working on a music player for android and i'm stuck at this problem.
By now i can play a song with musicservice and send it to background, i also display a notification with current playing song.
What i need is to re-open the main activity from the song notification and continue playing the song, it actually starts the desired activity but the music service is re-created and it stops the current playing song.
Here is my code.
MusicService.java
public class MusicService extends Service implements
MediaPlayer.OnPreparedListener,
MediaPlayer.OnErrorListener,
MediaPlayer.OnCompletionListener {
private final IBinder musicBind = new MusicBinder();
//media player
private MediaPlayer player;
//song list
private ArrayList<SongModel> songs;
//current position
private int songPosition;
public MusicService() {
}
public void onCreate() {
//create the service
super.onCreate();
//initialize position
songPosition = 0;
//create player
player = new MediaPlayer();
initMusicPlayer();
}
public void initMusicPlayer() {
//set player properties
player.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setOnPreparedListener(this);
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
}
public void setList(ArrayList<SongModel> theSongs) {
songs = theSongs;
}
public class MusicBinder extends Binder {
public MusicService getService() {
return MusicService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return musicBind;
}
#Override
public boolean onUnbind(Intent intent) {
player.stop();
player.release();
return false;
}
public int getPosition() {
return player.getCurrentPosition();
}
public int getCurrenListPosition() {
return songPosition;
}
public int getDuration() {
return player.getDuration();
}
public boolean isPlaying() {
return player.isPlaying();
}
public void pausePlayer() {
player.pause();
}
public void stopPlayer() {
player.stop();
}
public void seekToPosition(int posn) {
player.seekTo(posn);
}
public void start() {
player.start();
}
public void playSong() {
try {
//play a song
player.reset();
SongModel playSong = songs.get(songPosition);
String trackUrl = playSong.getFileUrl();
player.setDataSource(trackUrl);
player.prepareAsync();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onCompletion(MediaPlayer mp) {
if (mp.getCurrentPosition() == 0) {
mp.reset();
}
}
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mp.reset();
return false;
}
#Override
public void onPrepared(MediaPlayer mp) {
//start playback
mp.start();
SongModel playingSong = songs.get(songPosition);
Intent intent = new Intent(this, NavDrawerMainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
Notification.Builder builder = new Notification.Builder(this);
builder.setContentIntent(pendingIntent)
.setSmallIcon(R.drawable.ic_action_playing)
.setTicker(playingSong.getTitle())
.setOngoing(true)
.setContentTitle(playingSong.getTitle())
.setContentText(playingSong.getArtistName());
Notification notification = builder.build();
startForeground((int) playingSong.getSongId(), notification);
}
#Override
public void onDestroy() {
stopForeground(true);
}
public void setSong(int songIndex) {
songPosition = songIndex;
}
}
DiscoverSongsFragment.java
public class DiscoverSongsFragment extends Fragment
implements MediaController.MediaPlayerControl {
JazzyGridView songsContainer;
SongsHelper songsHelper;
SongsAdapter songsAdapter;
ArrayList<SongModel> songsArrayList;
ConnectionState connectionState;
Context mContext;
private static View rootView;
SongModel currentSong;
SeekBar nowPlayingSeekBar;
final Handler handler = new Handler();
// this value contains the song duration in milliseconds.
// Look at getDuration() method in MediaPlayer class
int mediaFileLengthInMilliseconds;
View nowPlayingLayout;
boolean nowPlayingLayoutVisible;
TextView nowPlayingTitle;
TextView nowPlayingArtist;
ImageButton nowPlayingCover;
ImageButton nowPlayingStop;
private MusicService musicService;
private Intent playIntent;
private boolean musicBound = false;
private boolean playbackPaused = false;
private int mCurrentTransitionEffect = JazzyHelper.SLIDE_IN;
public static DiscoverSongsFragment newInstance() {
return new DiscoverSongsFragment();
}
public DiscoverSongsFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_discover_songs, container, false);
mContext = rootView.getContext();
setupViews(rootView);
return rootView;
}
#Override
public void onStart() {
super.onStart();
if (playIntent == null) {
playIntent = new Intent(mContext, MusicService.class);
mContext.bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE);
mContext.startService(playIntent);
}
}
#Override
public void onResume() {
super.onResume();
if (isPlaying()) {
showNowPlayingLayout();
primarySeekBarProgressUpdater();
}
}
#Override
public void onDestroy() {
mContext.stopService(playIntent);
musicService = null;
super.onDestroy();
}
private void hideNowPlayingLayout() {
nowPlayingLayoutVisible = false;
nowPlayingLayout.setVisibility(View.GONE);
Animation animationFadeIn = AnimationUtils.loadAnimation(mContext, R.anim.fade_out);
nowPlayingLayout.startAnimation(animationFadeIn);
}
private void showNowPlayingLayout() {
nowPlayingLayoutVisible = true;
nowPlayingLayout.setVisibility(View.VISIBLE);
Animation animationFadeIn = AnimationUtils.loadAnimation(mContext, R.anim.fade_in);
nowPlayingLayout.startAnimation(animationFadeIn);
}
private void setupViews(View rootView) {
songsHelper = new SongsHelper();
songsArrayList = new ArrayList<SongModel>();
connectionState = new ConnectionState(mContext);
songsAdapter = new SongsAdapter(mContext, songsArrayList);
nowPlayingLayout = rootView.findViewById(R.id.nowPlayingLayout);
nowPlayingLayout.setVisibility(View.GONE);
nowPlayingLayoutVisible = false;
songsContainer = (JazzyGridView) rootView.findViewById(R.id.songsContainerView);
songsContainer.setTransitionEffect(mCurrentTransitionEffect);
songsContainer.setAdapter(songsAdapter);
songsContainer.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
musicService.setSong(position);
musicService.playSong();
if (playbackPaused) {
playbackPaused = false;
}
currentSong = songsArrayList.get(position);
// gets the song length in milliseconds from URL
mediaFileLengthInMilliseconds = getDuration();
if (currentSong != null) {
nowPlayingTitle.setText(currentSong.getTitle());
nowPlayingArtist.setText(currentSong.getArtistName());
nowPlayingCover.setImageBitmap(currentSong.getCoverArt());
}
primarySeekBarProgressUpdater();
if (!nowPlayingLayoutVisible) {
showNowPlayingLayout();
}
}
});
nowPlayingSeekBar = (SeekBar) rootView.findViewById(R.id.nowPlayingSeekbar);
nowPlayingSeekBar.setMax(99);
nowPlayingTitle = (TextView) rootView.findViewById(R.id.nowPlayingTitle);
nowPlayingArtist = (TextView) rootView.findViewById(R.id.nowPlayingArtist);
nowPlayingStop = (ImageButton) rootView.findViewById(R.id.nowPlayingStop);
nowPlayingStop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (isPlaying()) {
currentSong = null;
playbackPaused = false;
musicService.stopPlayer();
mediaFileLengthInMilliseconds = 0;
nowPlayingSeekBar.setProgress(0);
hideNowPlayingLayout();
}
}
});
nowPlayingCover = (ImageButton) rootView.findViewById(R.id.nowPlayingCover);
nowPlayingCover.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
Intent intent = new Intent(mContext, SongDetailsActivity.class);
intent.putExtra("Title", currentSong.getTitle());
intent.putExtra("Artist", currentSong.getArtistName());
intent.putExtra("Album", currentSong.getAlbumName());
intent.putExtra("Genre", currentSong.getGenre());
intent.putExtra("CoverUrl", currentSong.getCoverArtUrl());
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
});
getSongs();
hideNowPlayingLayout();
}
private void getSongs() {
if (!connectionState.isConnectedToInternet()) {
}
songsAdapter.clear();
String songsUrl = Constants.getAPI_SONGS_URL();
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(songsUrl, new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray jsonArray) {
if (jsonArray != null) {
for (int i = 0; i < jsonArray.length(); i++) {
try {
JSONObject jsonObject = jsonArray.getJSONObject(i);
SongModel song = songsHelper.getSongFromJson(jsonObject);
songsAdapter.add(song);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
}
});
AppController.getInstance().addToRequestQueue(jsonArrayRequest);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
}
#Override
public void onDetach() {
super.onDetach();
}
/**
* Method which updates the SeekBar primary progress by current song playing position
*/
private void primarySeekBarProgressUpdater() {
nowPlayingSeekBar.setProgress((int) (((float) getCurrentPosition() / getDuration()) * 100));
//if (isPlaying()) {
Runnable runnable = new Runnable() {
public void run() {
primarySeekBarProgressUpdater();
}
};
handler.postDelayed(runnable, 1000);
//}
}
#Override
public void start() {
musicService.start();
}
#Override
public void pause() {
playbackPaused = true;
musicService.pausePlayer();
}
#Override
public int getDuration() {
if (musicService != null && musicBound && musicService.isPlaying()) {
return musicService.getDuration();
}
return 0;
}
#Override
public int getCurrentPosition() {
if (musicService != null && musicBound && musicService.isPlaying()) {
return musicService.getPosition();
}
return 0;
}
public int getCurrentListPosition() {
if (musicService != null && musicBound && musicService.isPlaying()) {
return musicService.getCurrenListPosition();
}
return 0;
}
#Override
public void seekTo(int pos) {
musicService.seekToPosition(pos);
}
#Override
public boolean isPlaying() {
if (musicService != null && musicBound && musicService.isPlaying()) {
return musicService.isPlaying();
}
return false;
}
#Override
public int getBufferPercentage() {
return 0;
}
#Override
public boolean canPause() {
return true;
}
#Override
public boolean canSeekBackward() {
return true;
}
#Override
public boolean canSeekForward() {
return true;
}
#Override
public int getAudioSessionId() {
return 0;
}
//connect to the service
private ServiceConnection musicConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
//get service
musicService = binder.getService();
//pass list
musicService.setList(songsArrayList);
musicBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
musicBound = false;
}
};
}
(The fragment also re-creates itself when navigating through drawer menu items)
I hope somebody can help me achieve this. I dont know how to maintane the state when re-starting the MainActivity (by the way, im using navdrawer to hold fragments)
Include the currently playing song in your notification intent. Update the intent as the song changes. Include the flag to clear top and the flag to update current in the notification intent. :( sorry IDK if I have the flags right for your situation but you'll have a place to do more research.
In your service where you create the notification intent.
// link the notifications to the recorder activity
Intent resultIntent = new Intent(context, KmlReader.class);
resultIntent
.setAction(ServiceLocationRecorder.INTENT_COM_GOSYLVESTER_BESTRIDES_LOCATION_RECORDER);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent resultPendingIntent = PendingIntent
.getActivity(context, 0, resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
return mBuilder.build();
}
Then in main onCreate check the bundle for the name of the currently playing song and display it. Notice how I check for the existence of a bundle key before using it.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
String intentaction = intent.getAction();
// First Run checks
if (savedInstanceState == null) {
// first run init
...
} else {
// get the saved_Instance state
// always get the default when key doesn't exist
// default is null for this example
currentCameraPosition = savedInstanceState
.containsKey(SAVED_INSTANCE_CAMERA_POSITION) ? (CameraPosition) savedInstanceState
.getParcelable(SAVED_INSTANCE_CAMERA_POSITION) : null;
...
I have a trouble with getting Activity(Nullpointerexception) after that I have rotate screen and received callback from AsyncTask to update my views of the fragment. If I wont change orientation then everything is OK(but not all the time, sometimes this bug appears)
My main activity:
public class MainActivity extends SherlockFragmentActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pager_layout);
fm = getSupportFragmentManager();
fm.addOnBackStackChangedListener(this);
session = new SessionManager(getApplicationContext());
if (session.isAuthorizated()) {
disableTabs();
FragmentTransaction ft = fm.beginTransaction();
if (session.termsAndConditions()) {
ft.replace(android.R.id.content, new TermsAndConditionsFragment(), "terms-and-conditions").commit();
}
}
} else {
enableTabs();
mTabsAdapter = new TabsAdapter(this, mViewPager);
mTabsAdapter.addTab(actionBar.newTab().setText("Log in"), LoginFragment.class, null);
mTabsAdapter.addTab(actionBar.newTab().setText("Calculator"), CalculatorFragment.class, null);
}
}
That`s my fragment:
public class TermsAndConditionsFragment extends SherlockFragment implements OnClickListener, OnTouchListener, OnEditorActionListener, ValueSelectedListener, AsyncUpdateViewsListener {
private static final String TAG = "TermsAndConditionsFragment";
private TermsAndConditionsManager termsAndConditionsM;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
prepareData();
}
public void prepareData() {
if (getSherlockActivity() == null)
Log.d(TAG, "Activity is null");
termsAndConditionsM = new TermsAndConditionsManager(getSherlockActivity().getApplicationContext());
termsAndConditions = termsAndConditionsM.getTermsAndConditions();
...
// some stuff
...
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = init(inflater, container);
return rootView;
}
private View init(LayoutInflater inflater, ViewGroup container) {
rootView = inflater.inflate(R.layout.fragment_terms_and_conditions, container, false);
//bla bla bla
return rootView;
}
public void updateTermsAndConditionsView() {
//update views here
}
#Override
public void onClick(View v) {
ft = fm.beginTransaction();
switch (v.getId()) {
case R.id.etHowMuch:
d = NumberPaymentsPickerFragment.newInstance(getSherlockActivity(), Integer.valueOf(howMuch.replace("£", "")), 0);
d.setValueSelectedListener(this);
d.show(getFragmentManager(), Const.HOW_MUCH);
break;
}
}
#Override
public void onValueSelected() {
Bundle args = new Bundle();
...
ExecuteServerTaskBackground task = new ExecuteServerTaskBackground(getSherlockActivity());
task.setAsyncUpdateViewsListener(this);
task.action = ServerAPI.GET_TERMS_AND_CONDITIONS;
task.args = args;
task.execute();
}
#Override
public void onUpdateViews() {
prepareData();
updateTermsAndConditionsView();
}
}
My AsyncTask with callback:
public class ExecuteServerTaskBackground extends AsyncTask<Void, Void, Void> {
private static final String TAG = "ExecuteServerTaskBackground";
Activity mActivity;
Context mContext;
private AsyncUpdateViewsListener callback;
public ExecuteServerTaskBackground(Activity activity) {
this.mActivity = activity;
this.mContext = activity.getApplicationContext();
}
public void setAsyncUpdateViewsListener(AsyncUpdateViewsListener listener) {
callback = listener;
}
#Override
protected Void doInBackground(Void... params) {
ServerAPI server = new ServerAPI(mContext);
if (!args.isEmpty())
msg = server.serverRequest(action, args);
else
msg = server.serverRequest(action, null);
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
callback.onUpdateViews();
}
}
Why does it behave so? How can I get activity correctly if I change orientation.
EDIT:
As I understand correctly nullpointer appears after orientation changed and asynctask executed due to wrong reference between asyctask and Activity. Recreated activity doesnt have this reference thats why when I receive callback I use wrong activity reference which isn`t exist anymore. But how can I save current activity reference?
EDIT:
I have decided to try realize my task throughout Service and that`s what I have done.
Activity:
public class MainFragment extends Fragment implements ServiceExecutorListener, OnClickListener {
private static final String TAG = MainFragment.class.getName();
Button btnSend, btnCheck;
TextView serviceStatus;
Intent intent;
Boolean bound = false;
ServiceConnection sConn;
RESTService service;
ProgressDialog pd = new ProgressDialog();
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
intent = new Intent(getActivity(), RESTService.class);
getActivity().startService(intent);
sConn = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
Log.d(TAG, "MainFragment onServiceConnected");
service = ((RESTService.MyBinder) binder).getService();
service.registerListener(MainFragment.this);
if (service.taskIsDone())
serviceStatus.setText(service.getResult());
bound = true;
}
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "MainFragment onServiceDisconnected");
bound = false;
}
};
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.main_fragment, container, false);
serviceStatus = (TextView) rootView.findViewById(R.id.tvServiceStatusValue);
btnSend = (Button) rootView.findViewById(R.id.btnSend);
btnCheck = (Button) rootView.findViewById(R.id.btnCheck);
btnSend.setOnClickListener(this);
btnCheck.setOnClickListener(this);
return rootView;
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnSend:
pd.show(getFragmentManager(), "ProgressDialog");
service.run(7);
service.run(2);
service.run(4);
break;
case R.id.btnCheck:
if (service != null)
serviceStatus.setText(String.valueOf(service.taskIsDone()) + service.getTasksCount());
break;
}
}
#Override
public void onStart() {
super.onStart();
Log.d(TAG, "Bind service");
getActivity().bindService(intent, sConn, 0);
}
#Override
public void onPause() {
super.onDestroy();
Log.d(TAG, "onDestroy: Unbind service");
if (!bound)
return;
getActivity().unbindService(sConn);
service.unregisterListener(this);
bound = false;
}
#Override
public void onComplete(String result) {
Log.d(TAG, "Task Completed");
pd.dismiss();
serviceStatus.setText(result);
}
}
Dialog:
public class ProgressDialog extends DialogFragment implements OnClickListener {
final String TAG = ProgressDialog.class.getName();
public Dialog onCreateDialog(Bundle savedInstanceState) {
setRetainInstance(true);
AlertDialog.Builder adb = new AlertDialog.Builder(getActivity())
.setTitle("Title!")
.setPositiveButton(R.string.yes, this)
.setNegativeButton(R.string.no, this)
.setNeutralButton(R.string.maybe, this)
.setCancelable(false)
.setMessage(R.string.message_text)
.setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
return true;
}
});
return adb.create();
}
public void onClick(DialogInterface dialog, int which) {
int i = 0;
switch (which) {
case Dialog.BUTTON_POSITIVE:
i = R.string.yes;
break;
case Dialog.BUTTON_NEGATIVE:
i = R.string.no;
break;
case Dialog.BUTTON_NEUTRAL:
i = R.string.maybe;
break;
}
if (i > 0)
Log.d(TAG, "Dialog 2: " + getResources().getString(i));
}
public void onDismiss(DialogInterface dialog) {
Log.d(TAG, "Dialog 2: onDismiss");
// Fix to avoid simple dialog dismiss in orientation change
if ((getDialog() != null) && getRetainInstance())
getDialog().setDismissMessage(null);
super.onDestroyView();
}
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
Log.d(TAG, "Dialog 2: onCancel");
}
}
Service:
public class RESTService extends Service {
final String TAG = RESTService.class.getName();
MyBinder binder = new MyBinder();
ArrayList<ServiceExecutorListener> listeners = new ArrayList<ServiceExecutorListener>();
Handler h = new Handler();
RequestManager mRequest;
ExecutorService es;
Object obj;
int time;
StringBuilder builder;
String result = null;
public void onCreate() {
super.onCreate();
Log.d(TAG, "RESTService onCreate");
es = Executors.newFixedThreadPool(1);
obj = new Object();
builder = new StringBuilder();
}
public void run(int time) {
RunRequest rr = new RunRequest(time);
es.execute(rr);
}
class RunRequest implements Runnable {
int time;
public RunRequest(int time) {
this.time = time;
Log.d(TAG, "RunRequest create");
}
public void run() {
Log.d(TAG, "RunRequest start, time = " + time);
try {
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Log.d(TAG, "RunRequest obj = " + obj.getClass());
} catch (NullPointerException e) {
Log.d(TAG, "RunRequest error, null pointer");
}
builder.append("result " + time + ", ");
result = builder.toString();
sendCallback();
}
}
private void sendCallback() {
h.post(new Runnable() {
#Override
public void run() {
for (ServiceExecutorListener listener : listeners)
listener.onComplete();
}
});
}
public boolean taskIsDone() {
if (result != null)
return true;
return false;
}
public String getResult() {
return result;
}
public void registerListener(ServiceExecutorListener listener) {
listeners.add(listener);
}
public void unregisterListener(ServiceExecutorListener listener) {
listeners.remove(listener);
}
public IBinder onBind(Intent intent) {
Log.d(TAG, "RESTService onBind");
return binder;
}
public boolean onUnbind(Intent intent) {
Log.d(TAG, "RESTService onUnbind");
return true;
}
public class MyBinder extends Binder {
public RESTService getService() {
return RESTService.this;
}
}
}
As you mention in your edit, the current Activity is destroyed and recreated on orientation change.
But how can I save current activity reference?
You shouldn't. The previous Activity is no longer valid. This will not only cause NPEs but also memory leaks because the AsyncTask might hold the reference to old Activity, maybe forever.
Solution is to use Loaders.