I'm new to Android development and I need some help with a little problem.
I have a background Service that runs my media player and communicate with my PlayerActivity.
So far so good.
I need to schedule the execution of the tracks in different periods. i.e Play track x for one minute than play track y for 30 seconds etc.
So I call MyTimer thread form the PlayerActivity, this thread throws event at the specific time,
the PlayerActivity catches the Event and calls the MediaplayerService next() method.
My Problem is if I call next() without the thread it works fine, If i call it with the thread I get
mContext is null, can't getMirrorDisplayStatus!!!
with mediaplayer warning (1, 902)
I've tried to run this thread with from the PlayerActivity via Handler.post() and runOnUiThread()
and I get the same error.
below is the code for MyTimer Thread.
public class MyTimer implements Runnable {
private Object mPauseLock;
private boolean mPaused;
private boolean mFinished;
private TimeSection section;
public Trainer()
{
mPauseLock = new Object();
mPaused = false;
mFinished = false;
BusProvider.getInstance().register(this);
}
#Override
public void run()
{
while (!mFinished)
{
TimerManager tm = TimerManager.getInstace();
this.section = tm.getCurrentTimeSection();
if(this.section == null)
mFinished = true;
else
{
tm.inc();
// produce sectionChangeEvent event
BusProvider.getInstance().post(produceSectionEvent());
try
{
Thread.sleep((this.section.getTypeDuration() * 1000));
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
synchronized (mPauseLock)
{
while (mPaused)
{
try
{
mPauseLock.wait();
} catch (InterruptedException e)
{
}
}
}
}
}
/**
* Call this on pause.
*/
public void onPause() {
synchronized (mPauseLock) {
mPaused = true;
}
}
/**
* Call this on resume.
*/
public void onResume() {
synchronized (mPauseLock) {
mPaused = false;
mPauseLock.notifyAll();
}
}
#Produce public TimeSectionEvent produceSectionEvent(){
return new TimeSectionEvent(this.section);
}
}
some code of the Player Activity:
public class PlayerActivity implements
IMediaPlayerServiceClient{
private MediaPlayerService mService;
....
/**
* Binds to the instance of MediaPlayerService. If no instance of
* MediaPlayerService exists, it first starts a new instance of the service.
*/
public void bindToService()
{
Intent intent = new Intent(this, MediaPlayerService.class);
if (MediaPlayerServiceRunning())
{
// Bind to LocalService
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
} else
{
startService(intent);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
}
.....
/*
* This is how I start the timing thread
*/
private void startTiming()
{
mTimer = new MyTimer();
runOnUiThread(mTimer);
// trainingHandler.post(mTrainer);
}
public void next(TimeSection section)
{
mService.next(section);
}
/*
* Here I catch the TimeSectionEvent from MyTimer thread
*/
#Subscribe public void onSectionChanged(TimeSectionEvent e)
{
TimeSection section = e.getSection();
if(section != null)
next(section);
}
each activity has context so from the exception that you got i guess its a problem with the context of the MediaPlayerService.
you are using bindService(...) to bind the intent to the MediaPlayerService that should bind the context.
try to check if that binding is still there when you are trying to do "mService.next(section);"
Problem SOLVED!
The problem was that I was that the timing thread was running on the UI thread -> calling Thread.sleep made the UI thread sleeping as well.
So I end up using IntentService that doing the timer thread job in background.
public class TimingService extends IntentService{
private Object mPauseLock;
private boolean mPaused;
private boolean mFinished;
private TimeSection section;
public TimingService()
{
super("TimingService");
mPauseLock = new Object();
mPaused = false;
mFinished = false;
BusProvider.getInstance().register(this);
}
#Override
protected void onHandleIntent(Intent intent)
{
while (!mFinished)
{
TimerManager tm = TimerManager.getInstace();
this.section = tm.getCurrentTimeSection();
if(this.section == null)
mFinished = true;
else
{
tm.inc();
// produce sectionChangeEvent event
BusProvider.getInstance().post(produceSectionEvent());
try
{
Thread.sleep((this.section.getTypeDuration() * 1000));
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
synchronized (mPauseLock)
{
while (mPaused)
{
try
{
mPauseLock.wait();
} catch (InterruptedException e)
{
}
}
}
}
}
/**
* Call this on pause.
*/
public void onPause() {
synchronized (mPauseLock) {
mPaused = true;
}
}
/**
* Call this on resume.
*/
public void onResume() {
synchronized (mPauseLock) {
mPaused = false;
mPauseLock.notifyAll();
}
}
#Produce public TimeSectionEvent produceSectionEvent(){
return new TimeSectionEvent(this.section);
}
#Subscribe
public void onPauseEvent(PauseEvent e)
{
this.onPause();
}
#Subscribe
public void onResumeEvent(ResumeEvent e)
{
this.onResume();
}
}
Related
I have a location tracking app. I have a Foreground service that when the app goes into the background, it continues to get the location. That part works fine. If I output the location I can see the different points and correct timestamps.
While in the background I need to POST that data to an API endpoint. My GPSHeartbeat class is a singleton and it exposes a function to let me update the Singletons location property.
While in the foreground, everything works fine. When in the background, the location IS updated, but the singleton has the last location from BEFORE it went into the background.
My APICommunicator is firing in the background on its interval like it should, it just doesn't have the correct Location.
Here is the broadcast receiver that is responsible for listening to the Foreground services location change.
This works fine in the background and in the foreground. It is successfully getting the updated location.
private void onNewLocation(Location location)
{
Log.i(TAG, "onNewLocationRec'd: " + location);
mLocation = location;
// Notify anyone listening for broadcasts about the new location.
Intent intent = new Intent(ACTION_BROADCAST);
intent.putExtra(EXTRA_LOCATION, location);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
// Update notification content if running as a foreground service.
if (serviceIsRunningInForeground(this)) {
mNotificationManager.notify(NOTIFICATION_ID, getNotification());
}
}
The BroadcastReceiver is an inner class of an Activity called HomeActivity. This gets the CORRECT location from the service. If I output the log, it is the same as what the Service broadcast.
public class HomeActivity extends AppCompatActivity
{
private GPSHeartbeat mGPSHeartbeat;
private GPSReceiver myReceiver;
private LocationUpdatesService mService = null;
private boolean mBound = false;
private final ServiceConnection mServiceConnection = new ServiceConnection()
{
#Override
public void onServiceConnected(ComponentName name, IBinder service)
{
LocationUpdatesService.LocalBinder binder = (LocationUpdatesService.LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name)
{
mService = null;
mBound = false;
}
};
private void GPSBeginRequestingUpdates()
{
//Wait 5 seconds to spin up
(new Handler()).postDelayed(this::StartGPSUpdates, 5000);
}
private void StartGPSUpdates()
{
mService.requestLocationUpdates();
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
myReceiver = new GPSReceiver();
setContentView(R.layout.activity_home);
mGPSHeartbeat = GPSHeartbeat.instance(getApplicationContext()).setInterval(6);
}
#Override
protected void onStart()
{
super.onStart();
bindService(new Intent(getApplicationContext(), LocationUpdatesService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop()
{
if (mBound) {
unbindService(mServiceConnection);
mBound = false;
}
super.onStop();
}
#Override
protected void onResume()
{
super.onResume();
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(myReceiver, new IntentFilter(LocationUpdatesService.ACTION_BROADCAST));
GPSBeginRequestingUpdates();
}
#Override
protected void onPause()
{
LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(myReceiver);
super.onPause();
}
private class GPSReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Location location = intent.getParcelableExtra(LocationUpdatesService.EXTRA_LOCATION);
if (location != null) {
Log.i(TAG, "\nonReceived New Location: " + GPSUtils.getLocationText(location));
GPSHeartbeat.instance(context.getApplicationContext()).SetLocation(location);
}
}
}
}
The Singleton. The SetLocation() does receive the correct location. It is only during my POST request that the APICommunicator is using the GPSHeartbeat's old location. Even though it was just updated.
How do I make sure I update to the correct location?
public class GPSHeartbeat extends Service {
private static String TAG = "GPSHeartbeat";
private static volatile GPSHeartbeat _instance;
private final WeakReference<Context> mContextRef;
private Boolean isRunning = false;
private int mInterval;
private Location mLocation;
private Handler mHandler;
private ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
private Future mLongRunningTaskFuture;
private Runnable mStatusChecker = new Runnable()
{
#Override
public void run()
{
try {
tick(); //this function can change value of mInterval.
}
finally {
if (isRunning()) {
// 100% guarantee that this always happens, even if your update method throws an exception
mHandler.postDelayed(mStatusChecker, mInterval);
}
}
}
};
private GPSHeartbeat(Context context)
{
mContextRef = new WeakReference<>(context.getApplicationContext());
}
public static GPSHeartbeat instance(Context context)
{
if (_instance == null) {
_instance = new GPSHeartbeat(context);
} else {
if (!context.equals(_instance.mContextRef.get())) {
_instance = null;
_instance = new GPSHeartbeat(context);
}
}
return _instance;
}
public void SetLocation(Location loc)
{
Log.i(TAG, "setLocation(): " + loc);
this.mLocation = loc;
}
public GPSHeartbeat setInterval(int interval)
{
this.mInterval = interval * 1000;
return this;
}
public void start()
{
if (isRunning()) return;
mHandler = new Handler();
mLongRunningTaskFuture = mExecutorService.submit(mStatusChecker);
mStatusChecker.run();
isRunning = true;
}
public void stop()
{
if (mHandler != null) {
mHandler.removeCallbacks(mStatusChecker);
}
if (mLongRunningTaskFuture != null) {
//kill the task:
try {
mLongRunningTaskFuture.cancel(true);
mLongRunningTaskFuture = null;
mHandler = null;
}
catch (Exception e) {
Log.e(TAG, "Unable to cancel task: " + e.getLocalizedMessage());
}
}
isRunning = false;
}
public Location currentLocation()
{
return mLocation;
}
public boolean isRunning()
{
return isRunning;
}
private void tick()
{
// Fire off the APICommuncator.Post() method
}
#Nullable
#Override
public IBinder onBind(Intent intent)
{
return null;
}
}
The APICommuncator
public class APICommuncator
{
private static String TAG = "APICommuncator";
private static volatile APICommuncator _instance;
private final WeakReference<Context> mContextRef;
private GPSHeartbeat _gpsHeartbeat;
private APICommuncator(Context context)
{
mContextRef = new WeakReference<>(context.getApplicationContext());
_gpsHeartbeat = GPSHeartbeat.instance(context.getApplicationContext());
}
public static APICommuncator i(Context context)
{
if (_instance == null) {
_instance = new APICommuncator(context);
} else {
if (!context.equals(_instance.mContextRef.get())) {
_instance = null;
_instance = new APICommuncator(context);
}
}
return _instance;
}
public void Post(){
// Do the background thing and grab
// getLocationNode() which gets the OLD location before it went to the background.
}
private JSONObject getLocationNode()
{
Location location = _gpsHeartbeat.currentLocation();
if (location == null) {
return null;
}
JSONObject node = null;
try {
node = new JSONObject();
node.put("Latitude", String.valueOf(location.getLatitude()));
node.put("Longitude", String.valueOf(location.getLongitude()));
node.put("HAccuracy", String.valueOf(location.getAccuracy()));
node.put("VAccuracy", String.valueOf(location.getAccuracy()));
node.put("Altitude", String.valueOf(location.getAltitude()));
node.put("Speed", String.valueOf(location.getSpeed() * 2.237));
node.put("Heading", String.valueOf(location.getBearing()));
node.put("Timestamp", String.valueOf((location.getTime() / 1000)));
}
catch (JSONException | NullPointerException e) {
e.printStackTrace();
}
return node;
}
}
In the Manifest:
<service
android:name=".gpsheartbeat.GPSHeartbeat"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
<service
android:name=".gpsheartbeat.LocationUpdatesService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="location" />
Actually I don't see that you are using foreground service. Not foreground service would be killed very soon after the application goes background. Plus communication with API should be in the scope of foreground service because activity could be killed by the system.
I need to create a new Thread that will execute a method when and only when the MessageReceivedEvent is updated inside the main ListenerAdapter, and let the thread sleep whenever the variable is not updated.
This thread should run independently from the main thread and not stop new requests.
This is the thread class,
private static final class Worker extends Thread {
private volatile boolean running = false;
MessageReceivedEvent event; //this must update along with the listener
private boolean validateData() {
if (//something) {
return true;
}
return false;
}
private void waitForInput() {
boolean hasInput = false;
try {
while (!hasInput) {
hasInput = validateData();
if (!hasInput) {
Thread.sleep(10);
}
}
} catch (InterruptedException iex) {
Thread.currentThread().interrupt();
}
}
#Override
public void run() {
running = true;
while (running) {
waitForInput();
//do something
}
}
}
It is an inner class ran by a request from the main thread,
the MessageReceivedEvent inside it must be updated only when the actual event from the listener changes, otherwise it should do nothing.
When testing, it will only execute based on the MessageEvent that triggered the thread, how can I make this thread receive the updates?
public class Listener extends ListenerAdapter {
public static MessageReceivedEvent msgEvnt;
#Override
public void onMessageReceived(MessageReceivedEvent msgEvnt) {
Listener.msgEvnt = msgEvnt;
}
This is all the listener does, update the variable whenever there is a new messageEvent.
You can use a Condition Variable to accomplish this.
final ReentrantLock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
private void waitForInput() {
lock.lock();
Listener.msgEvnt = null;
try {
while (Listener.msgEvnt == null)
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
#Override
public void onMessageReceived(MessageReceivedEvent event) {
lock.lock();
try {
Listener.msgEvnt = msgEvnt;
condition.signal();
} finally {
lock.unlock();
}
}
See ReentrantLock and Condition
You can use a BlockingQueue
final BlockingQueue queue = new ConcurrentBlockingQueue();
private MessageReceivedEvent waitForInput() throws InterruptedException {
return queue.take();
}
#Override
public void onMessageReceived(MessageReceivedEvent event) {
queue.put(event);
}
You can use a Callback, this is what I would recommend.
Consumer<? super MessageReceivedEvent> callback;
private void onInput(Consumer<? super MessageReceivedEvent> callback) {
this.callback = callback;
}
#Override
public void onMessageReceived(MessageReceivedEvent event) {
if (this.callback != null)
this.callback.accept(event);
this.callback = null;
}
Example use:
listener.waitForInput(event -> {
System.out.printf("%#s: %s\n", event.getAuthor(), event.getMessage().getContentDisplay());
});
This is already provided by JDA-Utilities
I know there are tons of questions and answers on this topic here but I am not able to resolve the below issue to resume a thread in my app.
heres is my runnable:-
Runnable ViewPagerVisibleScroll= new Runnable() {
#Override
public void run() {
if (i <= mAdapter.getCount() - 1) {
verticalViewPager.setCurrentItem(i, true);
handler.postDelayed(ViewPagerVisibleScroll, 3000);
i++;
while (isPaused) {
synchronized (ViewPagerVisibleScroll) {
// wait for resume() to be called
try {
ViewPagerVisibleScroll.wait();
// isPaused = false;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
};
pause and resume methods:-
public void pause() {
synchronized (ViewPagerVisibleScroll) {
isPaused = true;
}
}
public synchronized void resume() {
synchronized (ViewPagerVisibleScroll) {
isPaused = false;
// notify anybody waiting on "this"
ViewPagerVisibleScroll.notify();
}
}
My problem is that thread will pause() when I call pause method but it will not resume when I call resume(). Please help me to get rid of this issue.
First of all, I see this code:
public void pause() {
synchronized (ViewPagerVisibleScroll) {
isPaused = true; //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}
}
public synchronized void resume() {
synchronized (ViewPagerVisibleScroll) {
isPaused = false;
// notify anybody waiting on "this"
ViewPagerVisibleScroll.notify();
}
}
and this:
while (isPaused)
I can guarantee that if you invoke method pause(), i.e. you set isPaused to true, then this loop will be executed while isPaused is true. You should use !isPaused:
while (!isPaused)
i.e. while NOT paused do something. This was just an introduction.
So, back to your problem, I recoment you do next:
1). Create static final objects for synchronization:
private static final Object syncObj = new Object();
private static final Object stopSyncObj = new Object();
2). Implement your runnable as inner class:
public class MyRunnable implements Runnable {
private boolean isPaused;
private boolean isStopped;
public MyRunnable(){
synchronized (stopSyncObj){
isStopped = false;
}
synchronized (syncObj){
isPaused = false;
}
}
#Override
public void run() {
if (i <= mAdapter.getCount() - 1) {
verticalViewPager.setCurrentItem(i, true);
handler.postDelayed(ViewPagerVisibleScroll, 3000);
i++;
//Check thread
boolean isStopped = false;
while (!isStopped) {
synchronized (syncObj) {
boolean isPaused = false;
synchronized (syncObj){
isPaused = this.isPaused;
}
if(isPaused) {
// wait for resume() to be called
try {
syncObj.wait();
// isPaused = false;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
//do somethinghere
}
}
//Check 'stop' flag
synchronized (stopSyncObj){
isStopped = this.isStopped;
}
}
}
}
//This method stops runnable forever
public void stopRunnable(){
synchronized (stopSyncObj){
isStopped = true;
}
}
public void pause(){
synchronized (syncObj){
isPaused = true;
}
}
public void resume(){
synchronized (syncObj){
isPaused = false;
syncObj.notifyAll();
}
}
}
3). Use it:
Runnable ViewPagerVisibleScroll = new MyRunnable();
UPDATED ANSWER
For autoscrolling use ScheduledThreadPoolExecutor:
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
executor.schedule(new Runnable() {
#Override
public void run() {
//scroll viewpager
}
}, 3, TimeUnit.SECONDS);
To stop it use:
executor.shutdown();
I'm having trouble with my app. What I want is:
MainActivity launches & creates a scheduled thread which periodically checks whether the WiFi service is enabled. If it IS, go to a new activity. If it ISN'T then launch a warning & take the user to the WiFi settings page.
When the user comes back to the Main Activity, the MainActivity code will now sense the WiFi service is enabled & send them to the second activity.
I have this working. Here's the code:
#Override
protected void onResume()
{
super.onResume();
ScheduledExecutorService oScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
try
{
oScheduledExecutor.scheduleAtFixedRate({RUNNABLE HERE}, 0, 5, TimeUnit.SECONDS);
}
catch (Exception e)
{
System.out.println("(MainActivity) Caught Exception here. #1");
System.out.println("(MainActivity) Error: " + e.getMessage() + " Cause: " + e.getCause());
e.printStackTrace();
}
}
#Override
protected void onStart()
{
super.onStart();
// Assign WifiManager to System service
oWiFiManager = (WifiManager) getSystemService(WIFI_SERVICE);
// Create Runnable
oWiFiUpdater = new Runnable() {
#Override
public void run()
{
// If we should show WiFi Disabled
if (shouldShowWiFiAlert())
{
runOnUiThread(new Runnable() {
#Override
public void run() {
launchWiFiDisabledAlert();
}
});
}
Intent oAPListIntent = new Intent(getApplicationContext(), APList.class);
startActivity(oAPListIntent);
}
}
};
However when we are on the second activity, the first thread is still running. I thought that when the Activity is removed from View, all threads cease running??
I want the executor to only run when the Activity is viewable! Any ideas!?
EDIT: Answer thanks to inspiration from njzk2
#Override
protected void onResume()
{
super.onResume();
createWiFiAlertDialog();
boolean bWiFiEnabling = wifiEnabling();
while (bWiFiEnabling)
{
try
{
doSyncedWait(500);
}
catch (Exception e)
{
System.out.println("(MainActivity) Exception caught waiting. " + e.getMessage());
}
bWiFiEnabling = wifiEnabling();
}
boolean bWiFiEnabled = wifiReady();
if (!bWiFiEnabled)
{
runOnUiThread(new Runnable() {
#Override
public void run() {
AlertDialog oAlertDialog = m_oAlertDialog;
oAlertDialog.show();
}
});
}
else
{
Intent oIntent = new Intent(this,APList.class);
startActivity(oIntent);
}
}
private boolean wifiEnabling()
{
WifiManager oWiFiManager = m_oWiFiManager;
if (oWiFiManager == null) return false;
if (oWiFiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLING) return true;
return false;
}
private boolean wifiReady()
{
WifiManager oWiFiManager = m_oWiFiManager;
if (oWiFiManager == null) return false;
// If the WiFi state is anything other than enabled, then wait.
if (oWiFiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLED) return true;
return false;
}
If you give your runnable a reference to the activity, and set a boolean to true in onResume() and false in onPause(), you should be able to reference the boolean from your runnable and then use a if statement to only run if the boolean is set to true.
I would like to know how to implement a thread in this class to make it safe from the problems of ANR (Application Not Responding)
public class myClass {
private static String LOG_TAG = Root.class.getName();
public boolean isDeviceRooted() throws IOException {
if (checkRootMethod1()){return true;}
if (checkRootMethod2()){return true;}
if (checkRootMethod3()){return true;}
return false;
}
public boolean checkRootMethod1(){
String buildTags = android.os.Build.TAGS;
if (buildTags != null && buildTags.contains("test-keys")) {
return true;
}
return false;
}
public boolean checkRootMethod2(){
try {
File file = new File("/system/app/Superuser.apk");
if (file.exists()) {
return true;
}
else {
return false;
}
} catch (Exception e) {
}
return false;
}
public boolean checkRootMethod3() {
if (new ExecShell().executeCommand(SHELL_CMD.check_su_binary) != null){
return true;
}else{
return false;
}
}
}
If for example this code is execute when i press a button, if i press many times this button, my app have an ANR.
You don't want to use a Thread, but an AsyncTask. Here's how:
Based on the following, figure out what you need for your app: AsyncTask<TypeOfVarArgParams, ProgressValue, ResultValue>
Some inspiration:
public class MyClass {
//Something
public MyClass() {
new BackgroundTask().execute("Hello World");
}
}
private class BackgroundTask extends AsyncTask<String, Void, String> {
#Override
protected void onPreExecute() {
// Prepare your background task. This will be executed before doInBackground
}
#Override
protected String doInBackground(String... params) {
// Your main code goes here
String iAmAString = "I have done something very heavy now...";
return iAmAString;
}
#Override
protected void onPostExecute(String result) {
// Whatever should happen after the background task has completed goes here
}
#Override
protected void onProgressUpdate(Void... values) {
// In here, you can send updates to you UI thread, for example if you're downloading a very large file.
}
}
Thread thread = new Thread()
{
#Override
public void run()
{
myClass rootChecker = new myClass();
isRooted = rootChecker.isDeviceRooted();
}
};