Получение java.lang.ClassCastException: android.os.BinderProxy каждый раз, когда я объявляю и запускаю две службы
Я встречаю следующее исключение binder.proxy каждый раз, когда я объявляю и запускаю две службы. Одна служба выполняется в разных процессах (частное приложение), а другая служба выполняется в том же процессе, что и в моем приложении, в (процесс приложений по умолчанию) с внедрением связующего.
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>
Я запускаю свою первую услугу в MainActivity при нажатии кнопки:
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);
}
}
И вторая услуга в классе MainApplication как.
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)
Я сослался на следующие ссылки, чтобы разобраться в проблеме, которая гласит:
если моя деятельность и служба находятся в отдельных процессах, тогда мы не должны связывать то, как я это сделал.
Android-версия android.os.BinderProxy для Android
java.lang.ClassCastException: android.os.BinderProxy нельзя отнести к LocalBinder
Но в моем случае:
Я привязываюсь к SecondService из MainApplication, и оба они работают в одном Процессе (например, в приложении по умолчанию). Тем не менее я столкнулся с проблемой binderProxy в SecondService, а моя FirstService работает в отдельном процессе, с которым я даже не привязан.
Пожалуйста, помогите мне в этой ситуации и предложите мне наилучший способ, чтобы я мог реализовать тот же сценарий без какого-либо сбоя.
Ответы
Ответ 1
Нашел ответ после проведения некоторых исследований и отладки,
Если мы создаем и привязываем любую службу к классу MainApplication (тогда служба получает привязку к целому ApplicationContext или BaseContext), и если одно и то же приложение содержит другие службы, привязанные к Контексту (-ам),
//Declared in MainApplication
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
mBinder = (LocalBinder) service;
}
В OnServiceConnected() Мы получим объект связующего для обеих служб (SecondService, запущенный в MainApplication (зарегистрированный в BaseContext получит локальный объект binderObject) и FirstService, запущенный MainActivity ( получит android.os.binderProxyObject, поэтому вызывает ClassCastException).
-
Итак, чтобы исправить эту проблему, нужно запустить все приложение
услуг из любого контекста активности, а не с использованием любого глобального
Контекст приложения. Также этот вопрос является независимым
Процессы
-
Следовательно, я переместил SecondService и FirstService в MainActivity
Контекст, который исправил проблему.
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);
}
}
Ответ 2
В эту проблему (локальная служба, возвращающая BinderProxy), захотела опубликовать то, что я нашел, так как нашел эту страницу при попытке отладки. Краткая версия как запуск по предложению: запуск удаленной службы создает второй экземпляр класса Application в новом процессе, который затем пытается привязать к локальной службе, которая была запущена исходным экземпляром приложения, как если бы это была локальная служба, но поскольку служба запускается в исходном процессе, привязывая ее к процессам, и вы получаете BinderProxy вместо ожидаемого класса Binder.
Есть несколько вещей, которые нужно помнить об услугах Android. Каждый сервис имеет назначенный процесс, в котором он будет работать. Если вы не назначаете процесс в вашем манифесте Android, он будет работать в процессе по умолчанию (процесс, в котором запускаются приложение, действия и т.д.). Не указывать имя процесса не означает, что он запустит службу в том же процессе, с которым вы связываете/запускаете службу.
Скажем, у меня есть класс MyApplication, который пытается связать две службы при запуске: одна служба работает в процессе по умолчанию (мы будем называть это LocalService), один из которых выполняется в отдельном процессе (RemoteService).
Пользователь запускает мое приложение, которое создает экземпляр MyApplication в процессе по умолчанию. Затем этот экземпляр пытается привязать к LocalService. Android создает LocalService в процессе по умолчанию и возвращает класс LocalService Binder в приложение (mBinder = (LocalBinder) service;
). Что все хорошо, мы успешно привязаны к LocalService.
Затем приложение пытается связать с RemoteService. Android создает новый процесс с именем, которое вы указали в манифесте Android. Однако, прежде чем он сможет создать RemoteService, ему необходимо создать приложение для запуска службы. Он создает новый экземпляр MyApplication в удаленном процессе и запускает его.
Однако этот новый экземпляр MyApplication, запущенный в отдельном процессе, пытается связать с LocalService во время запуска. Поскольку LocalService работает в процессе по умолчанию, это привязка к перекрестному процессу, но MyApplication ожидает, что это будет привязка к процессу. Android возвращает BinderProxy, второй экземпляр MyApplication пытается передать его в LocalBinder и сбой. Самое интересное, что он падает в другом процессе, поэтому ваше приложение и активность могут продолжать работать. Вы никогда не сможете привязываться к удаленной службе.
Если вы хотите привязываться к локальной службе с контекстом приложения, а также использовать удаленную службу, вам нужно будет обработать тот факт, что Android будет создавать другое приложение в удаленном процессе при запуске удаленной службы. Я не потрудился попробовать это (я просто сделал мой удаленный сервис локальной службой), но вы, вероятно, могли бы проверить имя процесса во время приложения при создании и не связывать, если это не процесс по умолчанию.
Ответ 3
Я пробовал прежде всего решения, но ни одно из них не сработало. Если кто-то застрял так же, как я, попробуйте это на основе ответа @Coeffect. В моем сценарии служба клиентов не принадлежит моему текущему приложению (процессу)
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
mBinder = LocalBinder.Stub.asInterface(service);
}
Ответ 4
Вы не можете напрямую вызывать какие-либо методы своих удаленных служб (или приведений), потому что они живут в другом процессе, поэтому вы не можете получить ссылку на этот экземпляр. Но у Android есть специальные интерфейсы для обработки этой межпроцессной связи (IPC). Самый простой способ - использовать android.os.Messenger (другой - AIDL, более сложный).
В вашем Сервисе ваша реализация Service#onBind()
будет немного отличаться:
override fun onBind(intent: Intent): IBinder? {
mMessenger = Messenger(YourServiceHandler())
return mMessenger.binder
}
А в реализации Activity ServiceConnection#onServiceConnected(serviceBinder: IBinder)
вы не получите прямую ссылку на экземпляр удаленной службы, а вместо этого создадите Messenger с интерфейсом send(message: Message)
, чтобы вы могли удаленно вызывать функции службы:
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)
}
}
Обратите внимание, что в сообщении идет аргумент, выполните вещи 1 или 2. Вы получите это обратно в свой обработчик службы Handler#onHandleMessage(message: Message)
с атрибутом what
:
override fun handleMessage(message: Message) {
when (message.what) {
MESSAGE_DO_STUFF_1 -> doStuff1()
MESSAGE_DO_STUFF_2 -> doStuff2()
}
}
Полное руководство можно найти в этой официальной документации