I'm learning android and I'm stuck here:
The app which I'm writing scans for Wifi signals in background periodically. I am using android intent service for the same. The problem is, application never executes onReceive() method of BroadCastReceiver
Intent Code:
public class BackgroundIntent extends IntentService {
// Default Constructor
public BackgroundIntent() {
super("BackgroundIntent");
// TODO Auto-generated constructor stub
}
WifiManager mainWifi;
BroadcastReceiver receiverWifi;
private final Handler handler = new Handler();
#TargetApi(Build.VERSION_CODES.GINGERBREAD)
#Override
protected void onHandleIntent(Intent intent) {
// TODO Auto-generated method stub
// Get mainWifi
mainWifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
if (mainWifi.isWifiEnabled() == false) {
mainWifi.setWifiEnabled(true);
}
receiverWifi = new WifiReceiver();
doInback();
}
// Basically a thread which calls itself after 5000milli sec
public void doInback() {
handler.postDelayed(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
mainWifi.startScan();
Log.i("Inside ", "doInBack");
// Call itself CODE GOES HERE :D
doInback();
}
}, 5000);
}
public class WifiReceiver extends BroadcastReceiver {
#TargetApi(Build.VERSION_CODES.GINGERBREAD)
public void onReceive(Context c, Intent intent) {
// CODE NEVER GOES HERE :(
List<ScanResult> wifiList;
wifiList = mainWifi.getScanResults();
Log.i("Inside receiver", "yes");
}
}
}
MainActivity code which invokes android intent service
public class MainActivity extends Activity {
TextView texty;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Intent is called here
Intent myIntent = new Intent(MainActivity.this, BackgroundIntent.class);
startService(myIntent);
texty = (TextView) findViewById(R.id.textView1);
}
public boolean onMenuItemSelected(int featureId, MenuItem item) {
return super.onMenuItemSelected(featureId, item);
}
}
Any idea what may be the cause? Is this wrong way to implement background wifi signal scan.
The same when I implement in Main Activity runs fine, so I'm guessing AndroidManifest.xml is correctly written..
Edit: I might've misinterpreted your goal here, If your just wondering why your onReceive() isn't firing, your activity has to register the receiver. Something like the following:
receiverWifi = new WifiReceiver();
registerReceiver(receiverWifi, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
doInback();
Check out this link
So the issue was with the functions which I was using.
I was using onHandleIntent(intent) which is the wrong function. Right one is onStartCommand(intent, flags, startId) for service.
My bad sorry..
Related
My question here is about playing the music after the user closes the app (the app tab is not showing on the list of currently open apps).
I have created a class to handle the service that targets the media player to play the ring-tone music:
public class RingService extends Service {
private MediaPlayer player;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
player = MediaPlayer.create(this,
Settings.System.DEFAULT_RINGTONE_URI);
player.setLooping(true);
player.start();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
player.stop();
}
}
in my MainActivity I use a button to start playing music. And it works fine till I close the app.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onStart() {
super.onStart();
}
public void onRing(View view) {
Intent intent = new Intent(this, RingService.class);
startService(intent);
}
public void onStop(View view) {
Intent intent = new Intent(this, RingService.class);
stopService(intent);
}
}
Is there anyone who could tell me, what I'm doing wrong? I would be extremely grateful.
And just not to start another topic for this app, I will ask it here - is it possible to relaunch the activity in 10 seconds after it has been closed by the user?
I think you missed the onCreate to implement
My app is doing good with this code:
public class BackgroundMusicService extends Service {
MediaPlayer musicPlayer;
public IBinder onBind(Intent arg) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
musicPlayer = MediaPlayer.create(this, R.raw.your_music_file);
musicPlayer.setLooping(true); // Set looping
musicPlayer.setVolume(100,100);
}
public int onStartCommand(Intent intent, int flags, int startId) {
musicPlayer.start();
return 1;
}
#Override
public void onDestroy() {
musicPlayer.stop();
musicPlayer.release();
}
and in you manifest:
<service android:enabled="true" android:name=".BackgroundSoundService" />
So after closing your app you want to play some ringtone using RingService but when user close the app the onStop() method is called in your MainActivity with this code Intent intent = new Intent(this, RingService.class);
stopService(intent);. How service can play your music if you stopping your service at same time.
Try to delete this code from onStop() (not sure it will solve the problem but it worth trying).
Additionally I am recommending using Logcat for testing your code.
https://developer.android.com/studio/command-line/logcat
You need to put your service in foreground with startForeground(id, notification), check the official docs for reference.
When you have a service in foreground, the chances of your process being killed are reduced drastically because your process won't be considered as in background. The downside is that you must show a permanent notification, which in your case might even be desirable because you might need controls for the player.
I am working on an Android application which requires constant listener of Internet connectivity. I am using Broadcast listener and successfully applied it. But my code only shows the Toast message.
I want to stop the current activity and show a default XML file which says "No Internet Connection". and whenever it connect the Internet, previous activity resumes.
ExampleBradcastReceiver.java
public class ExampleBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
boolean noConnectivity = intent.getBooleanExtra(
ConnectivityManager.EXTRA_NO_CONNECTIVITY, false
);
if (noConnectivity) {
Toast.makeText(context, "Disconnected", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "Connected", Toast.LENGTH_SHORT).show();
}
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
ExampleBroadcastReceiver exampleBroadcastReceiver = new ExampleBroadcastReceiver();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(exampleBroadcastReceiver, filter);
}
#Override
protected void onStop() {
super.onStop();
unregisterReceiver(exampleBroadcastReceiver);
}
}
In the place of Toast Message, I want to show a default XML file whenever disconnected and resume activity whenever connected.
You can move ExampleBroadcastReceiver to MainActivity as an inner class. And since in Java inner classes have access to their parent classes' methods and fields, you can in onReceive method consider showing/hiding the Internet disconnected view.
public class MainActivity extends AppCompatActivity {
ExampleBroadcastReceiver exampleBroadcastReceiver = new ExampleBroadcastReceiver();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(exampleBroadcastReceiver, filter);
}
#Override
protected void onStop() {
super.onStop();
unregisterReceiver(exampleBroadcastReceiver);
}
private void showInternetDisconnectedView(boolean disconnected){
// show or hide based on 'disconnected'
}
private class ExampleBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
boolean noConnectivity = intent.getBooleanExtra(
ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
showInternetDisconnectedView(noConnectivity);
}
}
}
}
You need to move Broadcast receiver code into Activity and on receiving internet connection events you can stop current in progress activity and make internet failure layout visible there only as it is part of Activity class. If it is required through out the Application, then create Base activity and handle this there to avoid duplicating code on every screen.
I'm making an Android whack a mole game. I have the main activity which is basically the launcher, when you press the Play button the game activity starts. This works fine as it shows the background image and all molehills but I don't know how to call the method to start the game.
I've tried to call it from inside onCreate() but this ends up "playing the game" itself.
I've tried to call it right after the startActivity(intent) but the app crashes. And also I've tried to create an instance of the game class and call the play() method after the start activity but it doesn't work aswell. I don't know how to start the game method once the game activity is loaded.
I hope I explained well, thank you.
public class MainActivity extends AppCompatActivity {
ImageButton btnStart;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Hide TitleBar
try { this.getSupportActionBar().hide();}
catch (NullPointerException e){}
setContentView(R.layout.activity_main);
btnStart = (ImageButton)findViewById(R.id.btnStart);
btnStart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), GameView.class);
startActivity(intent);
}
});
}
And this is the code for the game_activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Hide TitleBar
try { this.getSupportActionBar().hide();}
catch (NullPointerException e){}
setContentView(R.layout.activity_game_view);
game();
}
The game() method is a typical game loop.
public void game() {
Random random = new Random();
int index;
/*
* Casting array to store all ImageView on the game
*/
imgViewArray[0] = (ImageView)findViewById(R.id.img1);
imgViewArray[1] = (ImageView)findViewById(R.id.img2);
imgViewArray[2] = (ImageView)findViewById(R.id.img3);
imgViewArray[3] = (ImageView)findViewById(R.id.img4);
imgViewArray[4] = (ImageView)findViewById(R.id.img5);
imgViewArray[5] = (ImageView)findViewById(R.id.img6);
imgViewArray[6] = (ImageView)findViewById(R.id.img7);
imgViewArray[7] = (ImageView)findViewById(R.id.img8);
imgViewArray[8] = (ImageView)findViewById(R.id.img9);
imgViewArray[9] = (ImageView)findViewById(R.id.img10);
int j=0;
while (j < 10) {
// Get a random image to animate
index = random.nextInt(10);
switch(index) {
case 0: imgViewArray[0].setImageResource(images[6]);
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
imgViewArray[0].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
imgViewArray[0].setImageResource(images[0]);
}
});
}
},
300 // The code executes after 300ms
);
break;
I think you should put the game() call inside onResume().
There are many ways to solve the problem:
Using EventBus
Send the start game Event from Main Activity and register for the Event in the Game activity.
This is my favorite way to handle the problem. It's because the simplicity and prevent us from tightly coupled code. The major problem with using EventBus is we will lost in the sea of Event if there are too much Event in the the app.
How to do:
First, create the Event. This is just a simple class:
public class StartGameEvent {
}
Second, register for the event in the game activity:
public class GameActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
EventBus.getDefault().register(this);
}
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
Third, subscribe for the event:
public class GameActivity extends Activity {
...
#Subscribe
public void onMessageEvent(StartGameEvent event) {
game();
}
}
Last, send the event from Main activity:
EventBus.getDefault().post(new StartGameEvent());
Using LocalBroadcastManager
You need to create the message and broadcast it in from your Main activity:
Intent intent = new Intent("playEvent");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
Then, in the game activity, you need to register as receiver:
#Override
public void onCreate(Bundle savedInstanceState) {
// register for the event
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver,
new IntentFilter("playEvent"));
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
game();
}
};
#Override
protected void onDestroy() {
// Unregister here
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(mReceiver);
super.onDestroy();
}
I slightly modifying the code from How to use LocalBroadcastManager? for your case.
Using a static method in Game activity
This is the simplest way but highly discouraged. Because we can't ensure the state of the activity. Do not use this in production code. This is for learning sake only.
You can make the game() method as a static method like this:
public class GameActivity extends Activity {
...
public static void game() {
// game logic.
}
}
Then call the method when you want with:
GameActivity.game();
I'm trying to create a service where I want to detect something about user, let's say when user lays the device on a table, the thing is that I have that action detected but I have it on a MainActivty and I want it to put on Service.
The thing is that on my MainActivity() I had my registerAction() and on my onResume() were called and in onPause() I call the unregisterListener() from my sensor, as well I have a HandlerThread where I start it on my onCreate() how do I change it to Service? Would be a problem? I see that there aren't the same methods...
I've created my Service and I've got :
public class MyService extends Service {
public MyService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
super.onCreate();
Log.d("CREATE","ONCREATE");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("DESTROY","ONDESTROY");
}
}
Also my MainActivity I have put implements SensorEventListener.
A skeleton of my class is :
public class MainActivity extends Activity implements SensorEventListener {
private HandlerThread mSensorThread;
private SensorManager mSensorManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensorThread = new HandlerThread("sensor_thread");
mSensorThread.start();
}
private void registerSensorListener() {
mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST, new Handler(mSensorThread.getLooper()));
}
#Override
public void onSensorChanged(SensorEvent event) {
//DO stuff
if (isLayed()) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.d("LAY","LAYLAY");
}
});
mSensorManager.unregisterListener(this);
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
private boolean isLayed() {
return stuff;
}
#Override
protected void onResume() {
super.onResume();
registerSensorListener();
}
#Override
protected void onPause() {
super.onPause();
mSensorManager.unregisterListener(this);
}
}
EDIT
I'm using szamani20 code, but I'm having problems with runOnUiThread because I can not call from my Service also, I'm having this issue
java.lang.RuntimeException: Unable to start service com.example.developer.qwe.MyService#d8c613b with null: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Intent.getAction()' on a null object reference
First of all you need to decide whether you want the user to be aware of your running service or not. Take a review on Background Execution Limits in android Oreo:
To improve the user experience, Android 8.0 (API level 26) imposes limitations on what apps can do while running in the background.
So considering your case where it seems there are lots of work to do in many situations, it would be a better approach to use a foreground service. As android document says about foreground services:
A foreground service is a service that the user is actively aware of and is not a candidate for the system to kill when low on memory. A foreground service must provide a notification for the status bar, which is placed under the Ongoing heading. This means that the notification cannot be dismissed unless the service is either stopped or removed from the foreground.
Since you mentioned that you have the action detected I won't enter that part of your code. So you need to create a subclass of Service as you did and use the startService method to get it's onCreate called. One thing you need to notice is that the onCreate method of service is called once you call startService on that service for the first time, no matter how many times you call startService again the onCreate method won't get called and only the onStartCommand get called. We use that fact alongside that you could provide a string action within your intent to properly register and unregister your listener.
In MainActivity.java:
String action = "start"; // Or to unregister listener "stop"!
final Intent intent = new Intent(this, MyService.class);
intent.setAction(action);
startService(intent);
and then in MyService.java:
#Override
public void onCreate() {
super.onCreate();
// Do initialization or whatever here (executed once per service lifecycle)
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals("start")) {
// Register your listener or whatever
showForegroundNotification();
}
if (intent.getAction().equals("stop")) {
// Unregister your listener or whatever
stopForeground(true);
stopSelf();
}
return START_STICKY;
}
private void showForegroundNotification() {
Intent myServiceNotificationIntent = new Intent(this, MainActivity.class);
myServiceNotificationIntent.setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent
.getActivity(this, MY_SERVICE_REQUEST_CODE,
myServiceNotificationIntent, MY_SERVICE_FLAG);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle(MY_SERVICE_NOTIFICATION_CONTENT_TITLE)
.setTicker(MY_SERVICE_NOTIFICATION_TICKER)
.setContentText(MY_SERVICE_NOTIFICATION_CONTENT_TEXT)
.setSmallIcon(R.drawable.ic_whatever)
.setContentIntent(pendingIntent)
.setOngoing(true)
.build();
startForeground(MY_SERVICE_NOTIFICATION_ID, notification);
}
Finally don't forget to unregister your listener in onDestroy in case of android kill your service (which is very rare):
#Override
public void onDestroy() {
super.onDestroy();
// Unregister your listener
}
You can register SensorManager inside service in OnStartCommand.Also try using startForeground as android os will kill your service when app is killed
Main Purpose:
Reading an access point's properties freshly, and use it.
I tried to add a custom BroadcastReceiver class to my activity.
If it is true; I learned that; onReceive runs after intent action,
(here: WifiManager.SCAN_RESULTS_AVAILABLE_ACTION).
But i think i must wait until onReceive finishes running to get a fresh r11.
When i debug the method "useResults"; "use r11, modify r11" lines runs first, after a while onReceive starts to run.
public class MainActivity extends ActionBarActivity implements OnTouchListener{
int r11=0;
public void useResults(){
mainWifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
receiverWifi = new WifiReceiver();
registerReceiver(receiverWifi, new IntentFilter( WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
mainWifi.startScan();
//use r11
//modify r11
//do something with r11
}
#Override
protected void onPause() {
unregisterReceiver(receiverWifi);
super.onPause();
}
#Override
protected void onResume() {
registerReceiver(receiverWifi, new IntentFilter(
WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
super.onResume();
}
class WifiReceiver extends BroadcastReceiver {
public void onReceive(Context c, Intent intent) {
wifiList = mainWifi.getScanResults();
for (ScanResult result0:wifiList) {
String ssid0 = result0.SSID;
if(ssid0.compareTo("anID")==0){
r11=result0.level;
}
}
}
}
}
The BroadcastReceiver is an inner class, there's nothing wrong or dirty in using it to call methods on the class. That's what inner classes are for.
Just create a separate method
void doR11Results(){
//use r11
//modify r11
//do something with r11
}
... and call it from onReceive. Super simple!