Android 8.1后,Binder调度有个优化,就是当前进程的主线程在进行跨进程调用且在等待返回时,可能被调度去执行其他进程对本进程的远程调用操作。
下面是其中必然发生的一种情况:
流程图
所以如果认为AppA中的远程回调一定执行在Binder线程,可能就会有隐患。如果在AppA中的远程回调中wait(),而需要依靠主线程来notify(),就会可能无法得到notify而卡死主线程。
验证Demo
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="personal.jayhou.mydemos">
<uses-permission android:name="jayhou.permission.test"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="com.jayhou.intent.action.MY_DEMO_MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<service android:name=".RemoteService"
android:process=":remote" />
</application>
</manifest>
MainActivity.java
package personal.jayhou.mydemos;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
private TestRemoteService mTestRemoteService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTestRemoteService = new TestRemoteService();
mTestRemoteService.testRemoteService(getApplicationContext());
}
}
TestRemoteService.java
package personal.jayhou.mydemos;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import personal.jayhou.mydemos.aidl.ICallback;
import personal.jayhou.mydemos.aidl.IRemoteService;
public class TestRemoteService {
ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
final IRemoteService service = IRemoteService.Stub.asInterface(iBinder);
if(REMOTE_CALL_EXECUTE_IN_MAIN_THREAD) {
Log.d(TAG, "called in main thread make main thread wait for binder driver return, but the main thread in idle state.");
registerCallbackToRemote(service);
} else {
new Thread() {
@Override
public void run() {
Log.d(TAG, "called in other thread.");
registerCallbackToRemote(service);
}
}.start();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
public void testRemoteService(Context context) {
Intent remoteService = new Intent(context,RemoteService.class);
if(context.bindService(remoteService,mServiceConnection, Context.BIND_AUTO_CREATE)) {
} else {
context.unbindService(mServiceConnection);
}
}
private static final String TAG = "RemoteCallbackTest-Activity";
private static final boolean REMOTE_CALL_EXECUTE_IN_MAIN_THREAD = true;
ICallback mCallback = new ICallback.Stub() {
@Override
public void callback() throws RemoteException {
Log.d(TAG, "called in " + (REMOTE_CALL_EXECUTE_IN_MAIN_THREAD?" main thread right?":" in binder thread right?"));
}
};
private void registerCallbackToRemote(IRemoteService service) {
try {
service.callbackTrans(mCallback);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
RemoteService.java
package personal.jayhou.mydemos;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import personal.jayhou.mydemos.aidl.ICallback;
import personal.jayhou.mydemos.aidl.IRemoteService;
public class RemoteService extends Service {
private static final String TAG = "RemoteCallbackTest-Service";
@Override
public IBinder onBind(Intent intent) {
return new BinderService();
}
public static class BinderService extends IRemoteService.Stub {
@Override
public void callbackTrans(ICallback callback) throws RemoteException {
if(callback != null) {
Log.d(TAG, "call remote callback immediately.");
callback.callback();
}
}
}
}
ICallback.aidl
// ICallback.aidl
package personal.jayhou.mydemos.aidl;
interface ICallback {
void callback();
}
IRemoteService.aidl
// IRemoteService.aidl
package personal.jayhou.mydemos.aidl;
import personal.jayhou.mydemos.aidl.ICallback;
interface IRemoteService {
void callbackTrans(ICallback callback);
}
当下面变量设为true的时候,可以看到callback是在主线程中执行。
private static final boolean REMOTE_CALL_EXECUTE_IN_MAIN_THREAD = true;
02-02 16:47:54.344 10702 10702 D RemoteCallbackTest-Activity: called in main thread make main thread wait for binder driver return, but the main thread in idle state.
02-02 16:47:54.345 10743 10761 D RemoteCallbackTest-Service: call remote callback immediately.
02-02 16:47:54.346 10702 10702 D RemoteCallbackTest-Activity: called in main thread right?
当变量设置为false的时候,callback其实也没有在binder线程中执行,而是在11464这个new出来的线程中执行的。
02-02 17:23:45.907 11407 11464 D RemoteCallbackTest-Activity: called in other thread.
02-02 17:23:45.909 11433 11450 D RemoteCallbackTest-Service: call remote callback immediately.
02-02 17:23:45.912 11407 11464 D RemoteCallbackTest-Activity: called in in binder thread right?
网友评论