【Android】跨进程通信——AIDL、之Service基本细节使用之:精通

目录

Service 与 Thread 和 进程 之间的关系

Service的生命周期图

回调方法详解

三种服务的启动方式

1. startService()启动Service

2. bindService()启动Service

3. startService()启动Service后,调用bindService()绑定Service

清单文件声明 Service 组件及属性

Intent的显式与隐式启动

一、显示启动service

二、隐式启动service

三、5.0后隐式启动 service

前台Service

系统8.0、8.1的后台 Service 必须切换成前台服务

跨进程通信

1.1 bindService(Intent service, ServiceConnection conn, int flags)

1.2 ServiceConnection

1.3 返回的 Binder 对象注意

2. 本地 Service 跨进程通信

3. 远程 Service 跨进程通信

1. AIDL跨进程通信

2. 不用AIDL方式,使用 Binder 的 onTransact 方法

3. 使用Messenger跨进程通信

双进程守护


Service 与 Thread 和 进程 之间的关系

  • 进程:应用程序在内存中分配的空间。(正在运行中的程序)
  • 线程:负责程序执行的单元,也称为执行路径。(需要线程来执行代码)。一个进程至少包含一条线程,如果一个进程中出现了条线程,此程序就为多线程程序。
  • Service是一个组件:默认运行在Main线程(进程中)。和Thread没关系。如果Service在清单文件中用 android:process 属性另开进程运行此Service组件,就算Service被销毁进程也不会停止。

Service的生命周期图

回调方法详解

  • onCreate(),创建Service时调用,整个生命周期中只执行一次。
  • onBind(),必须实现的方法,客户端调用bindService()时回调该方法,返回一个IBind对象,通过此对象与被绑定Service通信。此后如果再次使用bindService绑定Service,系统不会再调用onBind()方法,只会直接把IBinder对象传递给其他后来增加的客户端!
  • onStartCommand(),早期版本是onStart()。客户端通过startService()时调用。可多次调用startService(),但不会创建新的Service对象,继续复用已创建的Service,会继续回调onStartCommand()方法。
  • onUnbind(),当该Service上绑定的所有客户端都断开时会回调该方法!
  • onDestroy(),服务被关闭前调用此方法。如调用stopService(),或者调用unbindService()。

三种服务的启动方式

1. startService()启动Service

  • 方法回调onCreate() ---> onStartCommand() ---> 进入运行状态。
  • 联系绑定与调用者无联系,就算调用者结束了生命,只要不调用stopService()方法,Service还会继续运行。
  • 结束方法stopService()

2. bindService()启动Service

  • 方法回调onCreate() ---> onBind() ---> 进入运行状态。
  • 联系绑定服务与调用者进行联系绑定。1. 如果调用者没有手动调用unbindService()关闭服务,当调用者销毁后,服务也会被销毁,并回调 onUnbind() ---> onDestroy()。  2. 如果是多个客户端绑定同一个Service的话,当所有的客户端都和service解除绑定或销毁后,系统才会销毁service。
  • 结束方法unbindService()
  • 注意如果中途中通过startService() 方法启动服务,则服务不会和全部调用者进行联系绑定。

3. startService()启动Service后,调用bindService()绑定Service

  • 方法回调onCreate() ---> onStartCommand() ---> onBind() ---> 进入运行状态。
  • 方法回调2如果两条语句在一个方法内:onCreate() ---> onBind() ---> onStartCommand() ---> 进入运行状态。
  • 联系绑定与调用者无联系,因为服务是通过startService()启动的。
  • 结束方法先unbindService()解绑,再调用stopService()关闭Service

不管哪种方法启动服务,都可以调用 stopService() 关闭服务。上面是按照规范关闭服务,当然你有需求的话。

清单文件声明 Service 组件及属性

android:exported表示是否允许除了当前程序之外的其他程序访问这个服务
android:enabled表示是否启用这个服务
android:permission是权限声明
android:process是否需要在单独的进程中运行,当设置为android:process=”:remote”时,代表Service在单独的进程中运行。注意“:”很重要,它的意思是指要在当前进程名称前面附加上当前的包名,所以“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote。
android:isolatedProcess 设置 true 意味着,服务会在一个特殊的进程下运行,这个进程与系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务的API(bind and start)。

<service android:name=".myService"
    android:enabled="true"
    android:exported="true"
    android:icon="@drawable/ic_launcher"
    android:label="string"
    android:process=":string"
    android:permission="string">
</service>

Intent的显式与隐式启动

一、显示启动service

Intent intent = new Intent(this, MyService.class);
startService(intent);

二、隐式启动service

首先自定义意图过滤器:标签 intent-filterAction

<service android:name=".MyService" 
    android:enabled="true" >
    <intent-filter android:priority="1000" >

        <action android:name="com.bin.action.MyService" />

    </intent-filter>
</service>

<!-- android:priority="1000" 服务优先级 -->

三、5.0后隐式启动 service

5.0 之前的隐式启动service

Intent intent = new Intent("com.bin.action.MyService");
startService(intent);

5.0 之后的隐式启动service

Intent intent = new Intent("com.bin.action.MyService");
intent.setPackage(getPackageName()); //如果是另一个程序的service则指定它的包名
startService(intent);

前台Service

原理:在服务里通过 startForeground() 方法启动一个通知 Notification,也可以使用兼容包里的 通知对象。

public void onCreate()
{
    super.onCreate();
    Notification.Builder localBuilder = new Notification.Builder(this);
    localBuilder.setSmallIcon(R.drawable.ic_launcher);
    localBuilder.setContentTitle("Service标题");
    localBuilder.setContentText("正在运行...");
    startForeground(1, localBuilder.getNotification());
}

系统8.0、8.1的后台 Service 必须切换成前台服务

注意:调用startForegroundService()后5秒内没有调用startForeground(),会有ANR

public class MyService extends Service {

    @Override
    public void onCreate()
    {
        super.onCreate();
        // 调用startForegroundService()后5秒内没有调用startForeground(),会有ANR
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODE.O) {
            Notification.Builder localBuilder = new Notification.Builder(this);
            localBuilder.setSmallIcon(R.drawable.ic_launcher);
            localBuilder.setContentTitle("Service标题");
            localBuilder.setContentText("正在运行...");
            startForeground(1, localBuilder.getNotification());
        }
    }

	@Override
	public void onDestroy() {
		stopForeground(true);
		super.onDestroy();
	}
}

版本兼容判断 启动Service(8.0、8.1启动Service方法用startForegroundService())

if (Build.VERSION.SDK_INT >= Build.VERSION_CODE.O) {
	startForegroundService(intent); 
} else {
	startService(intent);
}

使用 startForegroundService() 方法启动service,还需要添加权限:

 <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

跨进程通信

跨进程通信的场景有两种

  1. 本地service跨进程通信:是和当前程序内的service进行通信
  2. 远程Service跨进程通信:是和其它程序内的service进行通信,已知方式:

                 1. AIDL 接口描述文件方式

                 2. 不用AIDL方式,使用 Binder 的 onTransact 方法

                 3. 使用Messenger:Handler的方式

先来了解一下 bindService()、ServiceConnection、Binder

1.1 bindService(Intent service, ServiceConnection conn, int flags)

  • service : 通过该intent指定要启动的Service
  • conn : ServiceConnection对象,用户监听访问者与Service间的连接情况,连接成功回调该对象中的onServiceConnected(ComponentName,IBinder)方法;如果Service所在的宿主由于异常终止或者其他原因终止,导致Service与访问者间断开,连接时调用onServiceDisconnected(CompanentName)方法,主动通过unbindService() 方法断开并不会调用上述方法!
  • flags 指定绑定时是否自动创建Service(如果Service还未创建),参数可以是 (不自动创建),BIND_AUTO_CREATE(自动创建)

1.2 ServiceConnection

private IBinder myService;

private ServiceConnection con = new ServiceConnection(){

		@Override
		public void onServiceConnected(ComponentName package, IBinder service)
		{
                        myService = service;
			Toast.makeText(MyService.this, "连接成功", Toast.LENGTH_LONG).show();
		}

		@Override
		public void onServiceDisconnected(ComponentName package)
		{
                        myService = null;
			Toast.makeText(MyService.this, "连接断开", Toast.LENGTH_LONG).show();
		}
};

1.3 返回的 Binder 对象注意

返回的对象不能为null,否则连接不成立。

2. 本地 Service 跨进程通信

服务端

服务端需要写一个继承自 Binder 的类与服务通信。而这个类是你自定义的。

public class MyService extends Service
{
    private MyBinder binder;

    @Override
    public IBinder onBinder(Intent intent)
    {
        if (binder == null) {
            binder = new MyBinder();
        }
        return binder;
    }

    private class MyBinder extends Binder
    {
        // ...code
        // 返回此对象与Service进行通信
        public void toast(Context context, String str)
        {
            Toast.mackText(context, str, Toast.LENGTH_LONG).show();
        }
    }

}

然后在清单文件声明服务组件,并用 android:process 属性另开进程

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

客户端

public class MainActivity extends Activity 
{

    private IBinder myService;

    private ServiceConnection con = new ServiceConnection() {

		@Override
		public void onServiceConnected(ComponentName package, IBinder service)
		{
                        myService = service;
			Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_LONG).show();
                        try
			{
                            if (myService != null) {
                                Toast.makeText(MainActivity.this, myService.toast(this, "myService 弹出我吧!"), Toast.LENGTH_LONG).show();
                            } else {
                                Toast.makeText(MainActivity.this, "服务端未启动,或异常关闭", Toast.LENGTH_LONG).show();
                            }
			}
			catch (RemoteException e)
			{}

		}

		@Override
		public void onServiceDisconnected(ComponentName package)
		{
                        myService = null;
			Toast.makeText(MainActivity.this, "连接断开", Toast.LENGTH_LONG).show();
		}
    };
	
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
		
	bindService(new Intent(this, MyService.class), con, BIND_AUTO_CREATE);
    }
	
}

3. 远程 Service 跨进程通信

1. AIDL跨进程通信

编写 AIDL 的注意事项

  • 接口名词需要与aidl文件名相同。
  • 接口和方法前面不要加访问权限修饰符:public ,private,protected等,也不能用static final。
  • AIDL默认支持的类型包括Java基本类型StringListMapCharSequence,除此之外的其他类型都 需要import声明,对于使用自定义类型作为参数或者返回值,自定义类型需要实现Parcelable接口。
  • 自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义 的包在同一个包中。
  • 常见坑:两个应用程序的AIDL描述文件所在的包,必须一样。

服务端创建 AIDL 文件接口 :IPerson.aidl

package com.bin.aidl;

interface IPerson {
    String getContent(int postion);
}

保存后会在 gen目录的包路径下 自动生成 IPerson.java (图片来源网络)

自定义Service类:

注意:这时候我们返回的 Binder 对象是继承自 AIDL接口的 内部类Stub,IPerson.java下的 Stub类

public static abstract class Stub extends android.os.Binder implements com.bin.aidl.IPerson
public class AIDLService extends Service {

    private IBinder binder;

    @Override
    public IBinder onBind(Intent intent) {
        if (binder == null) {
            binder = new AIDLBinder();
        }
        return binder;
    }

    private final class AIDLBinder extends Stub {
        @Override
        public String getContent(int postion) throws RemoteException {
            String result = null;
            switch (postion) {
                case 0:
                    result = "我是0";
                    break;
                case 1:
                    result = "我是1";
                    break;
                case 2:
                    result = "我是2";
                    break;
                default:
                    result = "我是默认";
                    break;
            }
            return result;
        }
    }
}

在AndroidManifest.xml文件中注册Service

注意:从一个进程启动另一个进程的 Service 要定义 意图过滤器

<service android:name=".AIDLService">
    <intent-filter>
        <action android:name="android.intent.action.AIDLService" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>

<category android:name="android.intent.category.DEFAULT" /> 是 Intent默认添加的附加属性,所以加上。

服务端编写完毕

客户端

首先把 服务端的AIDL文件复制过来,不能修改AIDL文件里的任何东西,包括 所在 package ...

注意:AIDL存放的包路径必须与服务端一致

与服务端连接成功后,返回的Binder对象要强转为 AIDL 接口对象才能使用自定义方法:

// public static com.bin.aidl.IPerson asInterface(android.os.IBinder obj)

IPerson.Stub.asInterface(service); 
public class MainActivity extends Activity 
{
	private IPerson iPerson;
	private ServiceConnection con = new ServiceConnection(){

		@Override
		public void onServiceConnected(ComponentName package, IBinder service)
		{
                        Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_LONG).show();
			iPerson = IPC.Stub.asInterface(service);
			try
			{
				Toast.makeText(MainActivity.this, iPerson.getContent(0), Toast.LENGTH_LONG).show();
			}
			catch (RemoteException e)
			{}
		}

		@Override
		public void onServiceDisconnected(ComponentName package)
		{
			Toast.makeText(MainActivity.this, "断开连接", Toast.LENGTH_LONG).show();
			iPerson = null;
		}
	};
	
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
	
        Intent service = new Intent("android.intent.action.AIDLService");
        service.setPackage("com.bin.aidl");
        bindService(service, con, BIND_AUTO_CREATE);
    }
	
}

2. 不用AIDL方式,使用 Binder 的 onTransact 方法

服务端

public class IPCService extends Service {

    private static final String DESCRIPTOR = "IPCService";
    private IBinder binder;

    @Override
    public IBinder onBind(Intent intent) {
        if (binder == null) {
            binder = new IPCBinder();
        }
        return binder;
    }

    private final class IPCBinder extends Binder {

        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            switch (code){
                case 0x001: {
                    //读取发送来的消息
                    data.enforceInterface(DESCRIPTOR);
                    int postion = data.readInt();
                    //处理
                    String result = null;
                    switch (postion) {
                        case 0:
                            result = "我是0";
                            break;
                        case 1:
                            result = "我是1";
                            break;
                        case 2:
                            result = "我是2";
                            break;
                        default:
                            result = "我是默认";
                            break;
                    }
                    //返回数据
                    reply.writeNoException();
                    reply.writeString(result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

    }
}

客户端

public class MainActivity extends Activity 
{
	private IBinder iBinder;
	private ServiceConnection con = new ServiceConnection(){

		@Override
		public void onServiceConnected(ComponentName package, IBinder service)
		{
                        Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_LONG).show();
			iBinder = service;

			android.os.Parcel data = android.os.Parcel.obtain();
                        android.os.Parcel reply = android.os.Parcel.obtain();
                        String result = null;
                        int postion = 1;
                        try {
                            //写入信息
                            data.writeInterfaceToken("IPCService");
                            data.writeInt(postion);
                            //发送消息
                            mIBinder.transact(0x001, data, reply, 0);
                            //读取返回信息
                            reply.readException();
                            result = reply.readString();
                        }catch (RemoteException e) {
                            e.printStackTrace();
                        } finally { 
                            //释放资源
                            reply.recycle();
                            data.recycle();
                        }
                        Toast.makeText(MainActivity.this, result, Toast.LENGTH_LONG).show();
		}

		@Override
		public void onServiceDisconnected(ComponentName package)
		{
			Toast.makeText(MainActivity.this, "断开连接", Toast.LENGTH_LONG).show();
			iBinder = null;
		}
	};
	
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
	
        Intent service = new Intent("android.intent.action.IPCService");
        service.setPackage("com.bin.ipcservice");
        bindService(service, con, BIND_AUTO_CREATE);
    }
	
}

3. 使用Messenger跨进程通信

  • 服务端实现一个Handler,由其接受来自客户端的每个调用的回调
  • 使用实现的Handler创建Messenger对象
  • 通过Messenger得到一个IBinder对象,并将其通过onBind()返回给客户端
  • 客户端使用 IBinder 将 Messenger(引用服务的 Handler)实例化,然后使用后者将 Message 对象发送给服务
  • 服务端在其 Handler 中(具体地讲,是在 handleMessage() 方法中)接收每个 Message

服务端

public class MessengerService extends Service {

    private final Messenger mMessenger = new Messenger(new ServiceHandler());

    class ServiceHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            String result = null;
            switch (msg.what) {
                case 0:
                    result = "我是0";
                    break;
                case 1:
                    result = "我是1";
                    break;
                case 2:
                    result = "我是2";
                    break;
                default:
                    result = "我是默认";
                    break;
            }
            Toast.makeText(MessengerService.this, result, Toast.LENGTH_LONG).show();
            super.handleMessage(msg);
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //返回给客户端一个IBinder实例
        return mMessenger.getBinder();
    }
}

客户端

public class MainActivity extends Activity 
{
	private Messenger mService;
	private ServiceConnection con = new ServiceConnection(){

		@Override
		public void onServiceConnected(ComponentName package, IBinder service)
		{
                        Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_LONG).show();
			//接收onBind()传回来的IBinder,并用它构造Messenger
                        mService = new Messenger(service);

                        //构造一个消息对象
                        Message msg = Message.obtain(null, 2, 0, 0);
                        try {
                            //把这个信息发送给服务端
                            mService.send(msg);
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
		}

		@Override
		public void onServiceDisconnected(ComponentName package)
		{
			Toast.makeText(MainActivity.this, "断开连接", Toast.LENGTH_LONG).show();
			mService = null;
		}
	};
	
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
	
        Intent service = new Intent("android.intent.action.MessengerService");
        service.setPackage("com.bin.messengerservice");
        bindService(service, con, BIND_AUTO_CREATE);
    }
	
}

双进程守护

原理:两个服务之间互相绑定,监听服务之间的 ServiceConnection 对象,重写 onServiceDisconnected() 方法 在对方异常断开连接时重启对方。

服务1

public class MyService1 extends Service
{
    private MyBinder binder;
    private IBinder mService;
    private Intent intent;

    private ServiceConnection con = new ServiceConnection(){

		@Override
		public void onServiceConnected(ComponentName package, IBinder service)
		{
			Toast.makeText(MyService1.this, "连接成功", Toast.LENGTH_LONG).show();
                        mService = service;
		}

		@Override
		public void onServiceDisconnected(ComponentName package)
		{
			Toast.makeText(MyService1.this, "连接断开", Toast.LENGTH_LONG).show();
                        mService = null;
                        startService(intent);
                        bindService(intent, con, 0);
		}
    };
    

    @Override
    public void onCreate()
    {
        intent = new Intent(this, MyService2.class);
        startService(intent);
        bindService(intent, con, 0);
        super.onCreate();
    }

    @Override
    public IBinder onBinder(Intent intent)
    {
        if (binder == null) {
            binder = new MyBinder();
        }
        return binder;
    }

    private class MyBinder extends Binder
    {
        // TODO
    }

}

服务2

public class MyService2 extends Service
{
    private MyBinder binder;
    private IBinder mService;
    private Intent intent;

    private ServiceConnection con = new ServiceConnection(){

		@Override
		public void onServiceConnected(ComponentName package, IBinder service)
		{
			Toast.makeText(MyService2.this, "连接成功", Toast.LENGTH_LONG).show();
                        mService = service;
		}

		@Override
		public void onServiceDisconnected(ComponentName package)
		{
			Toast.makeText(MyService2.this, "连接断开", Toast.LENGTH_LONG).show();
                        mService = null;
                        startService(intent);
                        bindService(intent, con, 0);
		}
    };
    

    @Override
    public void onCreate()
    {
        intent = new Intent(this, MyService1.class);
        startService(intent);
        bindService(intent, con, 0);
        super.onCreate();
    }

    @Override
    public IBinder onBinder(Intent intent)
    {
        if (binder == null) {
            binder = new MyBinder();
        }
        return binder;
    }

    private class MyBinder extends Binder
    {
        // TODO
    }

}

启动服务的方式可以在 Activity 中,也可以用广播接收器,我这里就用活动来启动服务

public class MainActivity extends Activity 
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
	
        Intent service = new Intent(this, MyService1.class);
        startService(service);
    }
	
}

记得在清单文件声明Service组件

<service android:name=".MyService1" />
<service android:name=".MyService2"
    android:process=":MyService2"/>
    

注意:双进程守护只能防止第三方应用kill,并不能防止用户手动停止掉

  • 7
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
千里马8年Android系统及应用开发经验,曾担任过美国unokiwi公司移动端技术总监兼架构师,对系统开发,性能优化,应用高级开发有深入的研究,Android开源定制ROM Lineage的贡献者之一,国内首家线下开辟培训Android Framework课程,拥有2年的Android系统培训经验。成为腾讯课堂专业负责android framework课程分享第一人,致力于提高国内android Framework水平Android Framework领域内是国内各大手机终端科技公司需要的人才,应用开发者都对Android系统充满着好奇,其中的binder是重中之重,都说无binder无Android,binde是Android系统的任督二脉。课程水平循序渐进,由中级再到高级,满足各个层次水平的android开发者。1、灵活使用binder进程通信,在app端对它的任何api方法等使用自如2、可以单独分析android系统源码中任何binder部分,分析再也没有难度3、掌握binder驱动本质原理,及对应binder驱动怎么进行进程通信,及内存等拷贝方式数据等4、对binder从上层的java app端一直到最底层的内核binder驱动,都可以顺利理通5、针对系统开发过程中遇到的binder报错等分析方法,及binder bug案例学习6、针对面试官任何的binder问题都可以对答自如7、socket这种进程通信实战使用8、针对android源码中使用的socket源码轻松掌握9、android系统源码中最常见的socketpair中双向进程通信10、使用socket实现一个可以让app执行shell命令的程序

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

虚妄狼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值