获取 java.lang.ClassCastException: android.os.BinderProxy 每次我声明和 运行 两项服务

Getting java.lang.ClassCastException: android.os.BinderProxy every time i declare and run two services

我每次声明和 运行 两项服务时都会遇到以下 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 中的第二个服务 class 作为。

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.onServiceConnected(MainApplication.java:23)
 02-05 10:32:25.035: E/AndroidRuntime(1424):    at
 android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1101)

我参考了以下链接来解决问题,其中说: 如果我的 activity 和服务在不同的进程中,那么我们不应该像我那样绑定。

Android service android.os.BinderProxy error

java.lang.ClassCastException: android.os.BinderProxy cannot be cast to LocalBinder

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

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

经过一些研究和调试后找到了答案,

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

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

在 OnServiceConnected() 中,我们将为服务(SecondService 在 MainApplication 中启动(向 BaseContext 注册将获得本地 binderObject)class 和 FirstService 启动 MainActivity(将得到 android.os.binderProxyObject 因此导致 ClassCastException)。

  • 所以,要解决这个问题,必须启动所有应用程序 来自任何 Activity 上下文的服务,而不是使用任何全局 应用程序上下文。此问题也是 independent进程

  • 因此,我将 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);

        }
    }

运行 进入此问题(本地服务返回 BinderProxy),想要 post 我在尝试调试时找到此页面后发现的内容。 运行 on 句子的简短版本:启动远程服务会在新进程中创建应用程序的第二个实例 class,然后尝试绑定到由原始应用程序实例启动的本地服务就好像它是一个本地服务,但由于该服务在原始进程中 运行ning 它跨进程绑定,你得到一个 BinderProxy 而不是你预期的 Binder class.

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

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

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

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

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

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

我尝试了以上所有解决方案,但其中 none 行得通。如果有人和我一样被困在@Coeffect 答案的基础上试试这个。在我的场景中,服务客户端不属于我当前的应用程序(进程)

@Override
public void onServiceConnected(ComponentName className, IBinder service) {
       mBinder = LocalBinder.Stub.asInterface(service);
}

您不能直接调用远程服务(或强制转换)的任何方法,因为它们位于不同的进程中,因此您无法获得对其实例的引用。但是 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。您将通过属性 what:

在您的服务处理程序 Handler#onHandleMessage(message: Message) 上返回它
override fun handleMessage(message: Message) {
    when (message.what) {
         MESSAGE_DO_STUFF_1 -> doStuff1()
         MESSAGE_DO_STUFF_2 -> doStuff2()
    }
}

完整的指南可以在official documentation

中找到