Получение java.lang.ClassCastException: android.os.BinderProxy каждый раз, когда я объявляю и запускаю две службы

Я сталкиваюсь со следующим исключением binder.proxy каждый раз, когда я объявляю и запускаю две службы. Одна служба работает в другом процессе (частном для приложения), а другая служба работает в том же процессе, что и мое приложение (процесс приложения по умолчанию) с реализацией Binder.

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 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() {
        }
    }
}

Отслеживание стека:

 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

java.lang.ClassCastException: android.os. BinderProxy нельзя преобразовать в LocalBinder

Но в моем случае: я привязываюсь к SecondService из MainApplication, и оба выполняются в одном и том же процессе (т. е. в процессе приложения по умолчанию). Тем не менее я сталкиваюсь с исключением binderProxy в SecondService , и мой FirstService работает в отдельном процессе, к которому я даже не привязываюсь.

Пожалуйста, помогите мне в этой ситуации и предложите мне наилучший способ, чтобы я мог реализовать тот же сценарий без каких-либо сбоев.


person Prathmesh Swaroop    schedule 06.02.2015    source источник
comment
Вы убедились, что приложение и служба работают в одном и том же процессе?   -  person pskink    schedule 06.02.2015
comment
Я не проверял это явно в мониторе устройства Android, но по журналам я вижу, что у меня происходит сбой во второй службе, которая должна работать в том же процессе, что и приложение, поскольку я не назначил ему новый процесс в манифесте .   -  person Prathmesh Swaroop    schedule 07.02.2015
comment
обычно, если IBinder является BinderProxy, это означает, что другая сторона работает в другом процессе, поэтому...   -  person pskink    schedule 07.02.2015
comment
Хорошо, я проверю вторую службу, напечатав имя процесса во время выполнения в журналах или в ddms. Кроме того, если каким-то образом Android запускает мою вторую службу внутри другого процесса, я хотел бы знать, есть ли способ заставить эту службу не запускаться в отдельном процессе.   -  person Prathmesh Swaroop    schedule 07.02.2015
comment
и почему вы хотите, чтобы первый запускался в отдельном процессе?   -  person pskink    schedule 07.02.2015
comment
У меня есть требование, когда мне нужно импортировать библиотеку, и библиотека содержит 6-7 служб, работающих в разных процессах, тогда как мой проект содержит другую службу (из другой библиотеки), работающую в процессах приложения.   -  person Prathmesh Swaroop    schedule 08.02.2015
comment
Я не понимаю: если эти службы уже установлены на вашем устройстве, вам вообще ничего не нужно импортировать, если они не установлены, то зачем другой процесс?   -  person pskink    schedule 08.02.2015
comment
Попробуйте привязать службу, используя yourActivity.this.service = (ConnectionService) binder.getService(). В данном случае это должна быть MainActivity.   -  person Abhishek Singh    schedule 16.02.2015


Ответы (4)


Столкнулся с этой проблемой (локальная служба возвращает BinderProxy), хотел опубликовать то, что нашел, так как нашел эту страницу при попытке отладки. Короткая версия в виде предложения: запуск удаленной службы создает второй экземпляр вашего класса приложения в новом процессе, который затем пытается привязаться к локальной службе, которая была запущена исходным экземпляром приложения, как если бы это была локальная служба, но поскольку служба работает в исходном процессе, она связывается между процессами, и вы получаете BinderProxy вместо ожидаемого класса Binder.

Есть несколько вещей, о которых следует помнить в отношении сервисов Android. У каждой службы есть назначенный процесс, в котором она будет работать. Если вы не назначите процесс в своем манифесте Android, он будет работать в процессе по умолчанию (процесс, в котором запускаются приложение, действия и т. д.). Отсутствие имени процесса не означает, что он будет запускать службу в том же процессе, к которому вы привязываетесь/запускаете службу.

Допустим, у меня есть класс MyApplication, который пытается привязаться к двум службам при запуске: одна служба работает в процессе по умолчанию (назовем ее LocalService), а другая работает в отдельном процессе (RemoteService).

Пользователь запускает мое приложение, которое создает экземпляр MyApplication в процессе по умолчанию. Затем этот экземпляр пытается выполнить привязку к LocalService. Android создает LocalService в процессе по умолчанию и возвращает класс Binder LocalService приложению (mBinder = (LocalBinder) service;). Все хорошо, мы успешно привязались к LocalService.

Затем приложение пытается выполнить привязку к RemoteService. Android создает новый процесс с именем, которое вы указали в манифесте Android. Однако, прежде чем он сможет создать RemoteService, ему необходимо создать приложение для запуска службы. Он создает новый экземпляр MyApplication в удаленном процессе и запускает его.

Однако этот новый экземпляр MyApplication, работающий в отдельном процессе, пытается привязаться к LocalService во время запуска. Поскольку LocalService выполняется в процессе по умолчанию, это межпроцессная привязка, но MyApplication ожидает, что это будет внутрипроцессная привязка. Android возвращает BinderProxy, второй экземпляр MyApplication пытается преобразовать его в LocalBinder и аварийно завершает работу. Самое интересное, что он падает в другом процессе, поэтому ваше приложение и активность могут продолжать работать. Вы просто никогда не сможете привязаться к удаленной службе.

Если вы хотите привязаться к локальной службе с помощью контекста приложения, а также использовать удаленную службу, вам необходимо учесть тот факт, что Android создаст другое приложение в удаленном процессе при запуске удаленной службы. Я не удосужился попробовать это (я только что сделал свою удаленную службу локальной службой), но вы, вероятно, могли бы проверить имя процесса во время создания приложения и не привязываться, если это не процесс по умолчанию.

person Coeffect    schedule 04.06.2015
comment
Отсутствие имени процесса не означает, что он будет запускать службу в том же процессе, к которому вы привязываетесь/запускаете службу. это где-то задокументировано? Я считаю, что если вы его опустите, они обязательно будут работать в одном и том же процессе. - person frankish; 03.09.2019

Нашел ответ после некоторых исследований и отладки,

Если мы создадим и привяжем какую-либо службу к классу MainApplication (тогда служба будет привязана ко всему ApplicationContext или BaseContext), и если одно и то же приложение содержит другие службы, которые привязаны к конкретному контексту (контекстам) действия,

//Declared in MainApplication
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
                mBinder = (LocalBinder) service;
     }

В OnServiceConnected() мы получим объект связывания для класса Services (SecondService, запущенный в MainApplication (зарегистрированный в BaseContext, получит локальный объект binderObject), и FirstService, запущенный MainActivity (получит < strong>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);

        }
    }
person Prathmesh Swaroop    schedule 16.02.2015
comment
чтобы устранить эту проблему, необходимо запускать все службы приложений из любого контекста действия, а не из любого глобального контекста приложения. Можете ли вы объяснить, почему это так? Пока контексты приложения и действия находятся в одном процессе, не имеет значения, какой из них привязан к службе. - person tir38; 21.02.2018

Вы не можете напрямую вызывать какие-либо методы ваших удаленных служб (или приведения), потому что они находятся в другом процессе, поэтому вы не можете получить ссылку на его экземпляр. Но у 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)
    }
}

Обратите внимание, что в сообщении идет аргумент do stuff 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()
    }
}

Полное руководство можно найти в этой официальной документации.

person Allan Veloso    schedule 20.09.2019
comment
Хотя это более сложное решение, оно основано не на чем-то, что, вероятно, не работает, а на рекомендуемых практиках, в отличие от принятого ответа, и действительно работает, особенно в тех случаях, когда вы не можете привязать службу к действию. - person NoHarmDan; 08.04.2021

Я пробовал прежде всего решения, но ни одно из них не сработало. Если кто-то застрял так же, как я, попробуйте это на основе ответа @Coeffect. В моем сценарии клиенты службы не принадлежат моему текущему приложению (процессу)

@Override
public void onServiceConnected(ComponentName className, IBinder service) {
       mBinder = LocalBinder.Stub.asInterface(service);
}
person 0x52616A657368    schedule 08.11.2018
comment
что такое Stub.asInterface? - person Lior Iluz; 19.05.2020