2

I've a util class in my android project and it looks something like this,

public class MyUtil {
    public static final String TAG = "tim";
    static IBinder mIBinder1 = new Binder();

    public static void printHelloWorld() {
        Log.i(TAG, "Just printing hello world from my util" + mIBinder1.toString());
    }
}

I've a service which runs under separate process (:one) within same application which looks something like this,

public class MyService extends Service {

    public static final String TAG = "tim";

    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
       return new IMyAidlInterface.Stub() {

           @Override
           public void printHelloWorld() throws RemoteException {
               Log.i(TAG, "This is just printing hello world");
               MyUtil.printHelloWorld();
           }
       };
    }
}

When I invoke printHelloWorld() within the util class from application (process p1) and within the service class (process p1:one) I could see the same value of mBinder1 object to get printed the same value across the processes.

In general we used to make IPC call when we communicate between process. Here how the util code is shared between the processes? How the code is shared between process when they are within same application?

I'm not able to understand. Some help in this regard would be really helpful.

Tom Taylor
  • 3,344
  • 2
  • 38
  • 63

1 Answers1

2

Binder instance is not being shared between processes.

Although Binder instance is static in MyUtil class, both app and service processes will have their own copies of MyUtil code.

So, app process creates 1 static object for its MyUtil class, and service process creates another static object for its MyUtil class.

Let's see it in action.

1. Running service in separate process

This is the AndroidManifest.xml.

<manifest
    package="com.lakindu.staticobjectbehavior">

    <application>

        <service
            android:name=".MyService"
            android:process=":myservice" />

    </application>
</manifest>

MyService.java

public class MyService extends Service {
    private static final String TAG = "mytest_MyService";

    private final IBinder mBinder = new IMyService.Stub() {

        @Override
        public void printHelloWorld() throws RemoteException {
            Log.i(TAG, "This is just printing hello world");
            MyUtil.printHelloWorld();
        }

    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

MyUtil.java

public class MyUtil {
    private static final String TAG = "mytest_MyUtil";

    private static final IBinder mMyBinder = new MyBinder();

    public static void printHelloWorld() {
        Log.i(TAG, "Just printing hello world from my util" + mMyBinder.toString());
    }
}

MyBinder is just a Binder, which logs a message every time it creates an object.

MyBinder.java

public class MyBinder extends Binder {
    private static final String TAG = "mytest_MyBinder";

    public MyBinder() {
        Log.d(TAG, "Creating MyBinder object");
    }
}

To test, I wrote this Android instrumented test.

app/src/androidTest/StaticObjectBehaviorTest.java

import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import androidx.test.platform.app.InstrumentationRegistry;

@RunWith(AndroidJUnit4.class)
public class StaticObjectBehaviorTest {

    @Rule
    public final ServiceTestRule mServiceRule = new ServiceTestRule();

    @Test
    public void objectCreation() throws TimeoutException, RemoteException {
        // Context of the app under test.
        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();

        // Calling MyUtil class in test app process.
        MyUtil.printHelloWorld();

        Intent intent = new Intent(appContext, MyService.class);
        IBinder binder = mServiceRule.bindService(intent);
        assertNotNull(binder);
        IMyService myService = IMyService.Stub.asInterface(binder);
        assertNotNull(myService);

        // Using service proxy to call MyUtil class in service process.
        myService.printHelloWorld();
    }

}

Examining logs.

$ adb logcat | grep "mytest_"
[PID]
13054 D mytest_MyBinder: Creating MyBinder object
13054 I mytest_MyUtil: Just printing hello world from my utilcom.lakindu.staticobjectbehavior.MyBinder@5c24fc2
13089 I mytest_MyService: This is just printing hello world
13089 D mytest_MyBinder: Creating MyBinder object
13089 I mytest_MyUtil: Just printing hello world from my utilcom.lakindu.staticobjectbehavior.MyBinder@2723e12

As you can see there are 2 Binder objects being created for each process.

2. Running service in the same process

Just change the AndroidManifest.xml.

<manifest
    package="com.lakindu.staticobjectbehavior">

    <application>

        <service
            android:name=".MyService" />

    </application>
</manifest>

No code changes.

Examining logs.

$ adb logcat | grep "mytest_"
[PID]
13202 D mytest_MyBinder: Creating MyBinder object
13202 I mytest_MyUtil: Just printing hello world from my utilcom.lakindu.staticobjectbehavior.MyBinder@5c24fc2
13202 I mytest_MyService: This is just printing hello world
13202 I mytest_MyUtil: Just printing hello world from my utilcom.lakindu.staticobjectbehavior.MyBinder@5c24fc2

When running in the same process, just one Binder instance is being created.

Lakindu
  • 1,010
  • 8
  • 14
  • 1
    But I've noticed the same with power manager too, `PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);` when I print `pm.toString` from any process it returns me the same value. – Tom Taylor May 11 '20 at 05:25
  • The reason I've read is binder allocates it's memory in binder driver (at kernel level) and the same memory is shared between processes (shared memory) and at java object level the `toString` method is overridden to provide the same hashCode value. – Tom Taylor May 11 '20 at 05:32
  • In my opinion, we are drifting away from the original topic of this discussion. Originally we were talking about how 2 different processes have 2 different copies of the same class in Android application level. But this question asks where the same Android framework resources are being shared among different processes, and how. If you create a new question for this `PowerManager` ask, the community will be able to see your question and provide you an satisfactory answer. I think your question is a good one, and I will have to research on it to provide you an answer. – Lakindu May 11 '20 at 07:51