使用Messenger实现IPC

2018-02-06  本文已影响0人  _Youngjim

在写此篇前,笔者已经对消息机制有个大概的了解。但是此篇中又出现Messenger,Message,Binder,handle,bundle的一大堆错综复杂的关系,所以暂不对他们关系进行解释,只是简单得描述步骤。

至于Messenger,大家可以理解成轻量级的AIDL的通信手段。

要进行IPC,我们这可以分成两种情况

服务和客户端(界面)在同一个应用不同进程

如何让两者同在一个应用不同进程,可以参考Service的xml属性解析,指定process属性即可。

服务的代码编写

指定属性

   <service android:name=".MyService"
            android:process=":myremote"></service>
public class MyService extends Service {


    private static final String TAG = "yjm";

    private static class abchandle extends Handler{
        @Override
        public void handleMessage(Message msg) {
            if(msg.what==1){
                Log.e(TAG,msg.getData().getString("msg"));
            }
            super.handleMessage(msg);
        }
    }


    private Messenger messenger=new Messenger(new abchandle());
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }

}
客户端的代码编写
public class MainActivity extends AppCompatActivity {

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

            Messenger messenger = new Messenger(iBinder);
            Message message = Message.obtain(null, 1);
            Bundle bundle = new Bundle();
            bundle.putString("msg","msgisme");
            message.setData(bundle);
            try {
                messenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(MainActivity.this, MyService.class);
        bindService(intent,mConnection,BIND_AUTO_CREATE);
        
    }

    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }
}

服务和客户端(界面)不在同一个应用

服务的代码编写

(要进行IPC前提,让服务可以被找到,设置下面属性)

<service android:name=".MyService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.kt.abc"></action>
                <category android:name="android.intent.category.DEFAULT"></category>
            </intent-filter>
        </service>
  public class MyService extends Service {

    public MyService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
    
    }

    private static class Abchandle extends Handler{
        @Override
        public void handleMessage(Message msg) {
            if(msg.what==1){
                Bundle bundles = msg.getData();
                //1.这句话至关重要,如果你传的是基础类型那不提了,如果是自定义对象,就要序列化
                //还要加上这句话,不然会显示类找不到的错误,,至于为什么 ,看最后抄来的
                //2.自定义类的包名,要和服务端的类高度一致,特别是包名!不然即使加上下面的话也没用
                bundles.setClassLoader(Thread.currentThread().getContextClassLoader());
                Book msg1 =  bundles.getParcelable("msg");
                Log.e("yjm", msg1.toString());
            }
            super.handleMessage(msg);
        }
    }
    private Messenger messenger=new Messenger(new Abchandle());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }
}
客户端(界面)的代码编写
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //这里的绑定方式稍微有点不同
        Intent intent = new Intent();
        intent.setPackage("com.kt.ktservice");
        intent.setAction("com.kt.abc");
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }


    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Messenger messenger = new Messenger(service);
            Message msg = Message.obtain(null, 1);
            Bundle bundle = new Bundle();
            bundle.putParcelable("msg",new Book("haha2"));
            msg.setData(bundle);
            try {
                messenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };


}

前面的示例都是客户端想服务端发送消息,如果服务端想客户端发送消息,则做法如下(这里以同一个应用不同进程举例)。客服端在向服务端发送消息时,顺便发送一个Messenger过去,到时候服务端就拿这个发送消息。

客户端的代码编写
public class MainActivity extends AppCompatActivity {

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

            Messenger messenger = new Messenger(iBinder);
            Message message = Message.obtain(null, 1);
            Bundle bundle = new Bundle();
            bundle.putString("msg","msgisme");
            message.setData(bundle);
            //顺便发送一个Messenger过去,但是服务端就拿这个发送
            message.replyTo=reMessenger;
            try {
                messenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
private  static class recevierHandle  extends Handler{
        @Override
        public void handleMessage(Message msg) {
            if(msg.what==2){
                Log.e("yjm",msg.getData().getString("re"));
            }
            super.handleMessage(msg);
        }
    }


    private Messenger reMessenger=new Messenger(new recevierHandle());

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(MainActivity.this, MyService.class);
        bindService(intent,mConnection,BIND_AUTO_CREATE);
        
    }

    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }
}
服务的代码编写
public class MyService extends Service {


    private static final String TAG = "yjm";

    private static class abchandle extends Handler{
        @Override
        public void handleMessage(Message msg) {
            if(msg.what==1){
                Log.e(TAG,msg.getData().getString("msg"));
                //使用客户端发送过来的Messenger发送消息
                Messenger replyTo = msg.replyTo;
                Message remessage = Message.obtain(null, 2);
                Bundle bundle = new Bundle();
                bundle.putString("re","reply");
                remessage.setData(bundle);
                try {
                    replyTo.send(remessage);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

            }
            super.handleMessage(msg);
        }
    }


    private Messenger messenger=new Messenger(new abchandle());
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }

}

最后

笔者自己标注:使用AIDL进行IPC的那一篇文章只试过不同应用的通信,还没验证过同个应用不同进程,但是应该一样,主要就是改下绑定方式,服务属性,而自定义类和AIDL接口文件在同一个应用中就都不用改了

bundles.setClassLoader(Thread.currentThread().getContextClassLoader());
这段话用于获取客户端发送过来的Book信息,难点在于bundles为什么要设置当前环境的类的加载器,前面说过,Messenger远程通信,归根到底是Parcel在起作用,因此传递的对象必须是实现了Parcelable接口的对象,而ClassLoader类并没有实现,因此在传递过程中会丢失,所以需要重新加载当前环境的类的加载器去寻找User类别进行实例化。如果不设置,会报ClassNotFoundException错误。如果我们把上面的Log语句的注释去掉,系统会报空指针异常,很好的证明了ClassLoader类的消息并不能跨进程传输。

上一篇 下一篇

猜你喜欢

热点阅读