I am trying attach and instantiate a variable that is a Service, before start the first Activity.
I am trying this:
Application.java
public class ApplicationBase extends Application {
public ServiceDoSport mServiceDoSport;
#Override
public void onCreate() {
startService(new Intent(this, ServiceDoSport.class));
super.onCreate();
}
}
ServiceDoSport.java:
public class ServiceDoSport extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
// attach service to main application
ApplicationBase mApplication = (ApplicationBase) getApplication();
mApplication.mServiceDoSport = this;
}
}
But the onCreate method of the first Activity is called before than Service has been attached and instantiated to ApplicationBase.
How I can do this?
What I doing wrong?
I am trying attach and instantiate a variable that is a Service, before start the first Activity
You cannot guarantee that will happen. startService() is asynchronous, and you have no way to guarantee that it will start before an activity does.
Related
I need to call certain non static methods in an activity when the user interacts with buttons in notification, I tried to do it in a standalone class the extends BroadcastReceiver it works, but I don't know how call a method on the activity mentioned earlier, I tried to make an inner class, non static, edited the manifest file it was first :
<receiver android:name=".Activity$NotificationBroadcast" >
<intent-filter>
.
.
.
</intent-filter>
</receiver>
and that gives me an error saying :
FATAL EXCEPTION: main
Process: com.example.app, PID: 3189
java.lang.RuntimeException: Unable to instantiate receiver
com.example.app.Activity$NotificationBroadcast:
java.lang.InstantiationException:
the class is :
public class NotificationBroadcast extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent){
if (intent.getAction().equals(Activity.ACTION1)){
// do things
} else if (intent.getAction().equals(Activity.ACTION1)){
// do things
}else if (intent.getAction().equals(Activity.ACTION2)){
// do things
}else if (intent.getAction().equals(Activity.ACTION3)){
// do things
}else if (intent.getAction().equals(Activity.ACTION4)){
// do things
}
}
}
I'm afraid you can't make the receiver as an inner class, because when the receiver is statically instantiated, the "holder" class has to be instantiated as well. The activity instance is only created when it is meant to be active, and that's why you got the exception.
If you want the receiver to interact with the activity (by calling non-static methods in the activity class), I suggest that you should make the receiver a non-static one. This means that you need to register an instance of the receiver in OnCreate() and unregister it in OnDestroy().
For a better design, the activity instance should be passed into the receiver via its constructor as an interface, so that the receiver does not get access to the whole activity object, but is only able to access the functionality.
Manifest should have:
<receiver android:name=".NotificationBroadcast" android:enabled="false" />
An interface for the interaction (IWorker.java for example):
public interface IWorker {
public void doThis();
public void doThat();
}
The receiver (a class on its own) takes the IWorker and does something when a broadcast is received:
public class NotificationReceiver extends BroadcastReceiver {
public static final string ACTION1 = "com.yourpackage.action1";
public static final string ACTION2 = "com.yourpackage.action2";
private IWorker worker;
public NotificationReceiver() {
}
public NotificationReceiver(IWorker worker) {
this.worker = worker;
}
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION1)) {
worker.doThis();
} else if (intent.getAction().equals(ACTION2)) {
worker.doThat();
}
}
}
And the activity takes care of the receiver on its life cycle:
public class MyActivity extends Activity implements IWorker {
private NotificationReceiver receiver;
#override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// create the receiver
receiver = new NotificationReceiver(this);
IntentFilter filter = new IntentFilter();
filter.addAction(NotificationReceiver.ACTION1);
filter.addAction(NotificationReceiver.ACTION2);
// register it
registerReceiver(receiver, filter);
}
#override
protected void onDestroy() {
super.onDestroy();
if (receiver != null) {
unregisterReceiver(receiver);
receiver = null;
}
}
#override
public void doThis() {
System.out.println("Doing this...");
}
#override
public void doThat() {
System.out.println("Doing that...");
}
}
P.S. The above codes are for reference only, they're not tested and may not compile.
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
How can I use findViewById() in a non activity class. Below is my code snippet. I get the error message: "can't resolve method findViewById" if used directly. And if i try to use the class constructor (Where the imageView is available) i get this error "cannot resolve symbol context"
public class MyBroadcastReceiver extends FirstBroadcastReceiver {
Activity activity;
public MyBroadcastReceiver(Context context, Activity activity){
this.context=context; // error here(cannot resolve symbol context)
this.activity=activity;
}
#Override
protected void (Context context) {
// content
}
#Override
public void onButton(Context context, boolean isClick) {
if(isClick) {
ImageView blueImage = (ImageView)activity.findViewById(R.id.imageView);
blueImage.setColorFilter(0xff000000);
}
}
.......
....
// and so on
And below is my MainActivity with MybroadcastReceiver class instance.Is it correct?
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// and so on
}
}
MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver(MainActivity.this,this);
#Override
public void onActivityResult() {
// some code
}
#Override
public void onInitialized(MyManager manager){
// some code
}
A BroacastReceiver runs entirely in the background, listening for Intents sent either by the OS or other apps. It is not responsible for any UI interactions, and cannot access any views. Therefore, findViewById cannot be used within a BroadcastReceiver.
See also - What is BroadcastReceiver and when we use it?
You have to pass View to the non activity class, before using findViewByid
and
try using
view.findViewByid(R.id.view_id);
Because context is null in Broadcast class. use Broadcast class constructor to pass parent_activity(Where the imageView is available) context in Broadcast to access the context:
public class Broadcast extends BroadCastReceiver {
Activity activity;
public Broadcast(Context context,Activity activity){
this.context=context;
this.activity=activity;
}
.......
....... //so on
and in parent_activity create Broadcast class instance by passing parent_activity context as:
Broadcast broadcast = new Broadcast(parent_activity.this,this);
Use activity instance as:
#Override
public void onButton(Context context, boolean isClick) {
if(isClick) {
ImageView blueImage = (ImageView) activity.findViewById(R.id.imageView); //<--- here
}
}
.........
......... //so on
There are two Activities..
1. Open SecondActivity from MainActivity
2. When event comes into MainActivity, call testMethod of SecondActivity
But how to do call this testMethod?
public class MainActivity extends Activity implements someListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Launch SecondActivity here!!
Intent intent = new Intent();
intent.setClass(MainActivity.this, SecondActivity.class);
startActivityForResult(intent, ID_PlayerActivity);
}
//trigger by JNI, it's in the other thread, not main thread.
void onEventCome() {
//How to call testMethod() in SecondActivity?
}
}
public class SecondActivity extends Activity {
void testMethod() {
//execute something...
}
}
If you open the SecondActivity, your MainActivity becomes inactive. I don't believe it is a good idea to call some activity method from other inactive/stopped activity.
I suggest to use observer pattern. Create a global long-lived object like EventProducer and register all activities as observer. So your EventProducer can inform all Activities about new event.
Example:
public class SecondActivity extends Activity implements MyEventListener {
#Override
public void onResume(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventProducer.instance().register(this);
}
#Override
public void onPause(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventProducer.instance().unregister(this);
}
void testMethod(){
//just doit
}
#Override
void onMyEventCome() {
testMethod();
}
}
First you need an event aware listener that will capture such an event happening. Your class seems ill equipped to do so.
Since you have a valid question, here goes:
void onEventCome() {
SecondActivity secondActivity = new SecondActivity();
secondActivity.testMethod();
}
There are many ways.
For eg:
Create the method as static and use class name and call it.
public static void onEventCome() {
}
In MainActivity:
MainActivity.onEventCome();
This is one method. Another method is create an object for MainActivity.
public void onEventCome() {
}
MainActivity main;
main = new MainActivity();
main.onEventCome();
You don't have a content view for your second activity. If you don't need to see the operation happen, you could remove your
Intent intent = new Intent();
intent.setClass(MainActivity.this, SecondActivity.class);
startActivityForResult(intent, ID_PlayerActivity);
remove extends Activity in SecondActivity and add a constructor public SecondActivity(Context context) and invoke the test method from your first activity like #Dragan example:
void onEventCome() {
SecondActivity secondActivity = new SecondActivity(MainActivity.this);
secondActivity.testMethod();
}
I know, that there are tons of questions regarding leaking Android services and how to "maintain" a service in an app, but it's a bit like seeing two doctors with one problem: you get three different opinions...
In other words: I am completely unsure, how to deal with my service in a proper way. Hence this question.
I have the following: Several Activities and a BoundService, which is started on app startup and should be kept alive until the app closes.
Android.delevoper proposes to use a bound service to ease communication between Service and Activity. So I bind my service in each activitie's onStart() and unbind it in every onStop() to keep it from leaking.
In addition, I read in several threads, that one should use startService() to keep the service from being destroyed when no Activity is binding to it anymore. So I use an explicit startService() in my first Activity and a stopService() on every exit point of my app.
So basically I have this as my Service:
public class NetworkService extends Service {
private final IBinder binder = new ServiceBinder();
#Override
public void onCreate() {
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
public class ServiceBinder extends Binder {
public NetworkService getService() {
return NetworkService.this;
}
}
}
And then a starting Activity like this:
public class ActFirstActivity extends Activity {
private NetworkService networkService;
private boolean networkServiceIsBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_1);
}
#Override
protected void onStart() {
super.onStart();
Intent serviceIntent = new Intent(this, NetworkService.class);
startService(serviceIntent);
bindService(serviceIntent, networkServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
unbindService(networkServiceConnection);
}
private ServiceConnection networkServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
ServiceBinder binder = (ServiceBinder) service;
networkService = binder.getService();
networkServiceIsBound = true;
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
networkServiceIsBound = false;
}
};
}
Every other Activity would have the same bind/unbind functionality and the same ServiceConnection. As far as I can see, this keeps my app from leaking a service connection. But I have the impression that I introduced a LOT of overhead here. How to do this
a) secure (no leaking),
b) efficient (minimal overhead), and
c) easy to maintain (code-wise) ?
Thanks to everyone who takes the time to answer :)