13

每次我声明并运行两个服务时,我都会遇到以下 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 on Button click 上启动我的第一个服务:

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);
        }

    }

第一服务.java

public class FirstService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

第二服务.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

但就我而言: 我从MainApplication绑定到SecondService ,并且两者都在同一个进程中运行(即默认应用程序进程)。我仍然在SecondService中面临 binderProxy 异常,而我的FirstService在单独的进程中运行,我什至没有绑定到该进程。

请帮助我解决这种情况,并建议我最好的方法,以便我可以在没有任何崩溃的情况下实现相同的场景。

4

4 回答 4

26

遇到这个问题(本地服务返回 BinderProxy),想发布我在尝试调试时找到此页面后发现的内容。简短版本作为运行语句:启动远程服务会在新进程中创建 Application 类的第二个实例,然后尝试绑定到由原始 Application 实例启动的本地服务,就好像它是本地服务一样,但是由于该服务在原始进程中运行,因此它跨进程绑定,您将获得一个 BinderProxy 而不是您预期的 Binder 类。

关于 Android 服务,有几点需要牢记。每个服务都有一个分配的进程,它将在其中运行。如果您没有在 Android Manifest 中分配进程,它将在默认进程(运行应用程序、活动等的进程)中运行。不提供进程名称并不意味着它将在您绑定/启动服务的同一进程中运行服务。

假设我有一个 MyApplication 类,它在启动时尝试绑定到两个服务:一个在默认进程中运行(我们称之为 LocalService),一个在单独的进程中运行(RemoteService)。

用户启动我的应用程序,该应用程序在默认进程中创建一个 MyApplication 实例。然后,此实例尝试绑定到 LocalService。Android 在默认进程中创建 LocalService 并将 LocalService 的 Binder 类返回给应用程序(mBinder = (LocalBinder) service;)。很好,我们已经成功绑定到 LocalService。

接下来,应用程序尝试绑定到 RemoteService。Android 使用您在 Android Manifest 中提供的名称创建一个新进程。但是,在它可以创建 RemoteService 之前,它需要创建一个应用程序来运行该服务。它在远程进程中创建一个新的 MyApplication 实例并启动它。

但是,在单独进程中运行的新 MyApplication 实例会在启动期间尝试绑定到 LocalService。因为 LocalService 在默认进程中运行,这是一个跨进程绑定,但 MyApplication 期望这是一个进程内绑定。Android 返回一个 BinderProxy,第二个 MyApplication 实例尝试将其转换为 LocalBinder 并崩溃。有趣的部分是它在不同的进程中崩溃,因此您的应用程序和活动实际上可以继续运行。您将永远无法绑定到远程服务。

如果您想使用应用程序上下文绑定到本地服务并使用远程服务,您需要处理这样一个事实:Android 将在启动远程服务时在远程进程中创建另一个应用程序。我没有费心去尝试这个(我只是让我的远程服务成为本地服务),但你可能会在应用程序创建期间检查进程名称,如果它不是默认进程,则不绑定。

于 2015-06-04T22:45:02.043 回答
8

在做了一些研究和调试后找到了答案,

如果我们创建任何服务并将其绑定到 MainApplication 类(然后服务将绑定到整个 ApplicationContext 或 BaseContext),并且如果同一个应用程序包含绑定到特定于 Activity 的上下文的其他服务,

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

在 OnServiceConnected() 中,我们将获得服务(在MainApplication中启动的 SecondService(向 BaseContext 注册将获得本地 binderObject)类和FirstService启动的 MainActivity(将获得android.os.binderProxyObject 从而导致 ClassCastException)的活页夹对象。

  • 因此,要解决此问题,必须从任何活动上下文启动所有应用程序服务,而不是使用任何全局应用程序上下文。此问题也与 流程无关

  • 因此,我将 SecondService 和 FirstService 都移到了 MainActivity Context 中,从而解决了这个问题。

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);

        }
    }
于 2015-02-16T15:44:03.420 回答
2

您不能直接调用远程服务(或强制转换)的任何方法,因为它们存在于不同的进程中,因此您无法获得对其实例的引用。但 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)您不会直接引用您的远程服务实例,而是创建一个具有send(message: Message)接口的 Messenger,以便您可以远程调用服务函数:

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

完整的指南可以在这个官方文档中找到

于 2019-09-20T13:31:39.253 回答
0

我尝试了所有解决方案,但没有一个有效。如果有人和我一样被困,请根据@Coeffect 答案尝试此操作。在我的场景中,服务客户端不属于我当前的应用程序(进程)

@Override
public void onServiceConnected(ComponentName className, IBinder service) {
       mBinder = LocalBinder.Stub.asInterface(service);
}
于 2018-11-08T05:14:25.090 回答