I am implementing a service and have a wrapper class on top of this service wich locally bind to it. Now when I export the service as a jar and link to it from another application (this application instantiate the wrapper class).
When I run the application, I am getting the ClassCastException: android.os.BinderProxy cannot be cast to xxx$LocalBinder
caused by the local bind in the wrapper class:
service = ((LocalBinder) binder).getService();
The binder here is of type BinderProxy instead of localbinder and hence the crash.
The only way that this application works is when the package name of the app is the same as the service package name (I assume android thinks that the service is local).
private final IBinder localBinder = new LocalBinder();
public class LocalBinder extends Binder {
xxxService getService() {
return xxxService.this;
}
}
and
public IBinder onBind( Intent intent ) {
IBinder result = null;
result = localbinder;
return result;
}
Then in my wrapper class onServiceConnected:
public void onServiceConnected( ComponentName name, IBinder binder) {
xxxService = ((LocalBinder) binder).getService();
Finally my wrapper class constructor:
public xxxServiceManager( Context context ) throws Exception {
this.context = context;
xxxServiceManagerIntent = new Intent( "providerAPI" );
xxxServiceManagerIntent.setClassName( "com.yyy", "com.yyy.xxxService" );
context.startService( xxxServiceManagerIntent );
context.bindService( xxxServiceManagerIntent, serviceConnection, Context.BIND_AUTO_CREATE );
Then in the main application that uses this jar, if you set the package name
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yyy.provider" //the same as the service (which does not make sense)
everything work, but obviously I want to set another package name.
Is there any way how to redesign or make this work?
thanks!
see this
If your service is used only by the local application and does not need to work across processes, then you can implement your own Binder class that provides your client direct access to public methods in the service.
Note: This works only if the client and service are in the same
application and process, which is most common. For example, this would
work well for a music application that needs to bind an activity to
its own service that's playing music in the background.
Related
This question already has answers here:
Why is my program saying there is an error: "interface expected here"?
(4 answers)
Closed 1 year ago.
I'm trying to make my first service. The sample code I got has the
ServiceConnection created as a inner class in the activity that creates the service.
I would like ServiceConnection to be outside the activity class that creates the service so other actites can crat the service and use the same ServiceConnection class.
So i'm trying to sublclass it so it will be created in its own file.
public class CMYServiceConnection extends ServiceConnection {
I GET ERROR HERE
Messenger mService = null;
boolean mBound;
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the object we can use to
// interact with the service. We are communicating with the
// service using a Messenger, so here we get a client-side
// representation of that from the raw IBinder object.
mService = new Messenger(service);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mBound = false;
}
}
ServiceConnection is an interface, and classes can't extend interfaces.
Use implements instead
I would agree with Zoe on that one, you're trying to extends not an abstract class but an interface. The syntactically right thing would be to implements it. I did the same, simple fix! What a bummer! :D
Since ServiceConnection is an Interface you should use implements instead of extends.
You can only use extends for classes.
I have a service which has a BeaconNotificationsManager, I want to access this BeaconNotificationsManager in my Activity. Currently my BeaconNotificationsManager is static:
public class MyService extends Service {
public static BeaconNotificationsManager bnm;
}
And I am accessing this in my Activity like this:
if(MyService.bnm != null){
// do stuff
}
Although Android is telling me this is bad. What is the correct way to do this?
About Static issue: let just say you are referencing your service bnm from another class and your service has been destroyed by the OS but the static object(bnm) is still in use by some activity so this will hold on the service context from garbage collection unless you set your bnm reference inside your activity to null and this will leak all the application's resources
Solution :
The optimal option is use BindService in this way you will get the more control over your service through the object of service , in service use IBinder
class MyService..{
public BeaconNotificationsManager bnm;
public class LocalBinder extends Binder {
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
// inside service class
public boolean getStatus(){
return bnm==null;
}
}
So when you bind a service , you will get the binder object which can further give you the service object and use your function to check nullity
1.) Create a ServiceConnection object
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
bnmNull= mService.getStatus(); // bnm status
}
2.) Bind a Service using ServiceConnection object created in first step
Intent intent = new Intent(this, MyService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
,so then simply have a function in your class 'getStatus' and call it with the object retrieved through the binder check out the link for code example
I am encountering following binder.proxy exception every time i declare and run two services. One service runs in different Process(Private to app) and another service runs in same process as My Application is running in(Default App Process) with a Binder Implementation.
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.service.check"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" />
<application
android:name="com.service.check.MainApplication"
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="com.service.check.SecondService"
android:exported="false"/>
<service
android:name="com.service.check.FirstService"
android:process=":newProcess" >
</service>
</application>
</manifest>
I am launching my first service in MainActivity on Button click as:
MainActivity.java
public class MainActivity extends ActionBarActivity implements OnClickListener {
private Button mLanchServiceBtn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLanchServiceBtn=(Button) findViewById(R.id.launch_btn);
mLanchServiceBtn.setOnClickListener(this);
}
#Override
public void onClick(View v) {
//Starting first service
Intent launch=new Intent(this,FirstService.class);
startService(launch);
}
}
And second service in MainApplication class as.
MainApplication.java
public class MainApplication extends Application {
private SecondService.LocalBinder mBinder;
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
mBinder = (LocalBinder) service;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
}
};
#Override
public void onCreate() {
super.onCreate();
//starting second service
Intent launch=new Intent(this,SecondService.class);
startService(launch);
//Binding to it
bindService(launch, mConnection, BIND_AUTO_CREATE);
}
}
FirstService.java
public class FirstService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
SecondService.java
public class SecondService extends Service{
//Service Containing Local Binder
private LocalBinder mBinder=new LocalBinder();
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
class LocalBinder extends Binder{
public LocalBinder() {
}
}
}
StackTrace:
02-05 10:32:25.035: E/AndroidRuntime(1424): Process:
com.service.check:newProcess, PID: 1424 02-05 10:32:25.035:
E/AndroidRuntime(1424): java.lang.ClassCastException:
android.os.BinderProxy cannot be cast to
com.service.check.SecondService$LocalBinder 02-05 10:32:25.035:
E/AndroidRuntime(1424): at
com.service.check.MainApplication$1.onServiceConnected(MainApplication.java:23)
02-05 10:32:25.035: E/AndroidRuntime(1424): at
android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1101)
I have referred the following links to sort out the issue which says,
if my activity and service are in separate processes then we should not bind the way I have done.
Android service android.os.BinderProxy error
java.lang.ClassCastException: android.os.BinderProxy cannot be cast to LocalBinder
But in my case:
I am binding to SecondService from MainApplication and both are running in same Process(i.e Default Application Process). Still I am facing binderProxy exception in SecondService , And my FirstService runs in separate process which I am not even binding to.
Please help me out with this situation and, Suggest me a best possible way so that I can implement same scenario without any crash.
Ran into this issue (local service returning a BinderProxy), wanted to post what I'd found since I found this page while trying to debug. The short version as a run on sentence: starting a remote service creates a second instance of your Application class in a new process which then tries to bind to the local service that was started by the original Application instance as if it was a local service but since the service is running in the original process it's binding across processes and you get a BinderProxy instead of your expected Binder class.
There's a few things to keep in mind about Android services. Every service has an assigned process it will run in. If you don't assign a process in your Android Manifest it will run in the default process (the process where the Application, Activities, etc are run). Not giving a process name doesn't mean that it will run the service in the same process that you're binding to/starting the service from.
Let's say I have a MyApplication class which attempts to bind to two services on start up: one service running in the default process (we'll call this the LocalService), one running in a separate process (the RemoteService).
The user launches my app which creates a MyApplication instance in the default process. This instance then tries to bind to the LocalService. Android creates the LocalService in the default process and returns the LocalService's Binder class to the app (mBinder = (LocalBinder) service;). That's all good, we've successfully bound to the LocalService.
Next the app tries to bind to the RemoteService. Android creates a new process with the name you've supplied in the Android Manifest. However, before it can create the RemoteService it needs to create an Application for the service to run in. It creates a new MyApplication instance in the remote process and starts that up.
However, that new MyApplication instance running in a separate process tries to bind to the LocalService during start up. Because the LocalService is running in the default process this is a cross process binding but MyApplication expects this to be an in process binding. Android returns a BinderProxy, the second MyApplication instance tries to cast it to a LocalBinder and crashes. The fun part is that it crashes in a different process so your app and activity can actually continue running. You'll just never be able to bind to the remote service.
If you want to bind to a local service with an Application context and also use a remote service you'll need to handle the fact that Android will create another Application in the remote process when starting the remote service. I haven't bothered to try this (I just made my remote service a local service), but you could probably check the process name during the application's on create and not bind if it's not the default process.
Found an answer after doing some research and debugging,
If we create and bind any service to a MainApplication class(then service gets binded to whole ApplicationContext or BaseContext) and if same application contains other services which are binded to Activity specific Context(s),
//Declared in MainApplication
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
mBinder = (LocalBinder) service;
}
In OnServiceConnected() We will get binder object for both the Services( SecondService Started in MainApplication(registered with BaseContext will get local binderObject) class and FirstService started MainActivity(will get android.os.binderProxyObject hence causing ClassCastException).
So, to fix this issue one has to start all the application
services from any Activity Context rather than using any Global
Application Context. Also this issue is independent of the
Processes
Hence, I moved both SecondService and FirstService into MainActivity
Context which fixed the issue.
MainActivity.java
private Button mLanchServiceBtn;
private SecondService.LocalBinder mBinder;
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
mBinder = (LocalBinder) service;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLanchServiceBtn=(Button) findViewById(R.id.launch_btn);
mLanchServiceBtn.setOnClickListener(this);
//starting second service in activity
Intent launch=new Intent(this,SecondService.class);
startService(launch);
//Binding to it
bindService(launch, mConnection, BIND_AUTO_CREATE);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onClick(View v) {
//Starting FirstService also from MainActivity
Intent launch=new Intent(this,FirstService.class);
startService(launch);
}
}
You can't call directly any methods of your remote services (or cast) because they live in different process so you can't get a reference to it's instance. But Android has specific interfaces to handle this interprocess communications (IPC). The easiest way is using android.os.Messenger (another is AIDL, more complex).
On your Service, your implementation of Service#onBind() will be a little bit different:
override fun onBind(intent: Intent): IBinder? {
mMessenger = Messenger(YourServiceHandler())
return mMessenger.binder
}
And on your Activity implementation of ServiceConnection#onServiceConnected(serviceBinder: IBinder) you will not get a directly reference to your remote service instance, but instead create a Messenger that have a send(message: Message) interface so you can remotelly call the service functions:
override fun onServiceConnected(className: ComponentName, service: IBinder) {
mServiceMessenger = Messenger(service)
}
override fun onCreate(){
doStuff1Button.setOnClickListener{
val msg = Message.obtain(null, YourRemoteService.MESSAGE_DO_STUFF_1, 0, 0)
mServiceMessenger.send(msg)
}
doStuff1Button.setOnClickListener{
val msg = Message.obtain(null, YourRemoteService.MESSAGE_DO_STUFF_2, 0, 0)
mServiceMessenger.send(msg)
}
}
Note that in the message is going a argument do stuff 1 or 2. You will get this back on your service handler Handler#onHandleMessage(message: Message) with the attribute what:
override fun handleMessage(message: Message) {
when (message.what) {
MESSAGE_DO_STUFF_1 -> doStuff1()
MESSAGE_DO_STUFF_2 -> doStuff2()
}
}
Complete guide can be found in this official documentation
I tried above all solutions but none of those worked. If anyone was stuck same as myself try this based on #Coeffect answer. In my scenario service clients doesn't belong to my current application(process)
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
mBinder = LocalBinder.Stub.asInterface(service);
}
I managed to get my headset buttons get recognized by my app when pressed, but one of the buttons needs to call a method that's in MyCustomActivity. The problem is onReceive's 1st parameter is a Context that cannot be cast to Activity and using a MyCustomActivity's inner class won't work in Android 4.1 unless it is static (which has the same problem of inability to access MyCustomActivity's method.
So the only option left for me (in order to support both 2.x and 4.1) is to pass the activity as a parameter to RemoteControlReceiver.
But how do I do that, when the only way to instantiate it is via:
private ComponentName mRemoteControlReceiver = new ComponentName(this, RemoteControlReceiver.class);
Which doesn't accept any additional parameters?
Any idea how to work around this limitation?
Note: If I try to define RemoteControlReceiver as having a constructor with a parameter, I receive the following exception:
E/AndroidRuntime(2836): java.lang.RuntimeException: Unable to instantiate receiver com.example.RemoteControlReceiver: java.lang.InstantiationException: can't instantiate class com.example.RemoteControlReceiver; no empty constructor
Caused by:
E/AndroidRuntime(2836): Caused by: java.lang.InstantiationException: can't instantiate class com.example.RemoteControlReceiver; no empty constructor
E/AndroidRuntime(2836): at java.lang.Class.newInstanceImpl(Native Method)
E/AndroidRuntime(2836): at java.lang.Class.newInstance(Class.java:1319)
E/AndroidRuntime(2836): at android.app.ActivityThread.handleReceiver(ActivityThread.java:2205)
So it is clear that this new registerMediaButtonEventReceiver requirement (introduced in Android 4.1) expects an empty constructor.
Is there no way to work around this?
For example, is there a way to get a reference to the actual RemoteControlReceiver object (instantiated indirectly via mAudioManager.registerMediaButtonEventReceiver())? So that I can use an accessor to set a data-member of RemoteControlReceiver after it has been instantiated?
registerMediaButtonEventReceiver requires the BroadcastReceiver to be declared in the application manifest. This means that the receiver must be a standalone class, meaning it knows nothing about your current activity or service.
In order to get this message to your activity or service, you have a number of options:
Use a static global for the activity or service so the receiver can forward the message to it. This is generally not a good idea as it leads to leaks and isn't very adaptable when you want to change the code later. Statics are generally to be avoided.
Re-broadcast the message to a specific class, which happens to be an inner class of the activity or service you want to invoke. E.g. in the BroadcastReceiver for registerMediaButtonEventReceiver:
// Standalone class, declared in the manifest
public class ButtonReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
Intent intent = new Intent();
intent.setAction("com.foo.ACTION");
// Rebroadcasts to your own receiver.
// This receiver is not exported; it'll only be received if the receiver is currently registered.
context.sendBroadcast(intent);
}
}
And in your activity:
class MyActivity extends Activity {
private BroadcastReceiver myReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
MyActivity.this.onMessageReceived();
}
}
#Override
protected void onResume() {
registerReceiver(myReceiver, new IntentFilter("com.foo.ACTION"));
}
#Override
protected void onPause() {
unregisterReceiver(myReceiver);
}
private void onMessageReceived() {
}
}
Similar to the above method, it doesn't necessarily have to be a broadcast, it could be an Intent passed to the activity, depending on your use case. To do this instead of using sendBroadcast, you'd use startActivity (or startService if you're using a service).
I've created a class that extends Application.
This class starts a service when the application is loaded.
This service is running a thread in a loop, and need to update the application global variable throw the getter\setter method in this class:
public class AppLoader extends Application {
private boolean isInternetOn, isGpsOn, isThereActivityRunning;
private String results;
public String getResults() {
return results;
}
public void setResults(String results) { }
public boolean getIsInternetOn() {
return isInternetOn;
}
public void setIsInternetOn(boolean state) {
this.isInternetOn = state;
}
public boolean getIsGpsOn() {
return isGpsOn;
}
public void setIsGpsOn(boolean state) {
this.isGpsOn = state;
}
public void onCreate() {
super.onCreate();
final Intent intent = new Intent(Intent.ACTION_SYNC, null, this, ServerConnection.class);
startService(intent);
Log.d("ServiceStart", "ServerConnection.java Service has been started");
}
}
I want to use the getter setter methods inside the thread and I couldn't understand how to do it.
Help will be appreciated.
You need to have a Handler that you can send messages to. This is a very easy way to communicate across threads.
You are doing things very strangely.
As a rule of thumb: don't use an Application class unless you understand the framework very well, and know exactly what you're doing and why you need to be doing it.
Instead of starting your service in your Application class, you should be starting it in one of you Activities. Along with that, as #CaseyB points out, you should be using some other mechanism to communicate back and forth between your Service and the rest of your app. Instead of touching variables, you can pass Messages, implement an AIDL interface, etc...
Suppose you specify your own customized Application implementation in AndroidManifest.xml properly:
<?xml version="1.0" encoding="utf-8"?>
<manifest ... ...>
<application android:name="com.example.AppLoader" ... ...>
... ...
If you are within a android.content.Context (Activity, Service and etc.), simply get/cast an instance of your customized android.app.Application then call whatever your implement as public method on this object:
boolean gpsOn = ((AppLoader) getApplication()).getIsGpsOn();
... ...
((AppLoader) getApplication()).setIsGpsOn("true");
If you are calling setter in thread, you need implement synchronized block properly.