Android学习之路(21) 进程间通信-AIDL与Servce基本使用

news2024/10/6 10:37:35

Service 与 Thread 和 进程 之间的关系

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

Service的生命周期图

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

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

<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(Application Not Responding)

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"/>

跨进程通信

跨进程通信的场景有两种:

  • 本地service跨进程通信:是和当前程序内的service进行通信
  • 远程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还未创建),参数可以是 0 (不自动创建),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基本类型,String,List,Map,CharSequence,除此之外的其他类型都 需要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>

是 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,并不能防止用户手动停止掉

| |
|–|–|
| | |

回调方法详解
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)。


Intent的显式与隐式启动
一、显示启动service
Intent intent = new Intent(this, MyService.class);
startService(intent);
二、隐式启动service
首先自定义意图过滤器:标签 intent-filter、Action


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

</intent-filter>

三、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,还需要添加权限:

跨进程通信 跨进程通信的场景有两种:

本地service跨进程通信:是和当前程序内的service进行通信
远程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还未创建),参数可以是 0 (不自动创建),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,否则连接不成立。

  1. 本地 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 属性另开进程


客户端:

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);
}

}

  1. 远程 Service 跨进程通信
  2. AIDL跨进程通信
    编写 AIDL 的注意事项:

接口名词需要与aidl文件名相同。
接口和方法前面不要加访问权限修饰符:public ,private,protected等,也不能用static final。
AIDL默认支持的类型包括Java基本类型,String,List,Map,CharSequence,除此之外的其他类型都 需要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 要定义 意图过滤器

是 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);
}

}

  1. 不用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);
}

}

  1. 使用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组件

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1121707.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ip报头和ip报文切片组装问题

在tcp层将数据打包封装向下传递后&#xff0c;网络层将其整个看为一个数据&#xff0c;然后对其数据加网络报头操作&#xff0c;在网络层最具有代表的协议就是ip协议。在这里我们探究ipv4的报头。 ip报头 4位版本&#xff1a;指定ip的版本号&#xff0c;对于ipv4来说就是4。 …

粤嵌实训医疗项目day02(Vue + SpringBoot)

目录 一、创建vue项目并运行 二、vue-cli中的路由使用 三、element-ui框架、实现页面布局以及vue-路由 四、前端登录页面 五、user登录后端接口完善【后端】 六、user登录前端-请求工具-请求发起【前端】 七、请求的跨域-访问策略 八、完善项目的页面布局、导航菜单以及…

反射的作用(可以使用反射保存所有对象的具体信息)

1、绕过 编译阶段 为集合添加数据 反射是作用在运行时的技术&#xff0c;此时集合的泛型将不能产生约束了&#xff0c;此时是可以 为集合存入其他任意类型的元素的 。泛型只是在编译阶段可以约束集合只能操作某种数据类型&#xff0c;在 编译成Class文件进入 运行阶段 的时候&a…

存储优化知识复习一详细版解析

存储优化 知识复习一 一、 选择题 1、1948 年&#xff0c;____提出了“信息熵”(shāng) 的概念&#xff0c;解决了对信息的量化度量问题。 A、薛定谔 B、香农 C、克劳修斯 D、纳什 【参考答案】B2、 RAID2.0技术下&#xff0c;LUN是建立在____上。 A、硬盘 B、条带 C、Chun…

RESTful 分享

RESTful 分享 什么是RESTful 理解RESTful RESTful的使用 1.什么是RESTful REST全称是Representational State Transfer&#xff0c;中文译文就是“表述性状态转移”。 在2000年&#xff0c;由Roy Fielding&#xff08;HTTP规范的主要编写者之一&#xff09;在博士论文中提…

鲁迅为什么打周树人?今天的昨天是明天的什么?chatgpt4.0告诉你

GPT-4架构&#xff0c;是OpenAI开发的一个大型语言模型。虽然和GPT-3.5都是基于GPT-3的进一步发展&#xff0c;但是GPT-4在模型大小&#xff0c;知识更新&#xff0c;知识更新等方面都比GPT-3更上一个层次。现在国内很多平台号称可以使用GPT4&#xff0c;而实际上是能申请到GPT…

2023年09月 C/C++(八级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C编程&#xff08;1~8级&#xff09;全部真题・点这里 第1题&#xff1a;最短路径问题 平面上有n个点&#xff08;n<100&#xff09;&#xff0c;每个点的坐标均在-10000~10000之间。其中的一些点之间有连线。 若有连线&#xff0c;则表示可从一个点到达另一个点&#xff…

看我为了水作业速通C++!

和java不太一样的一样的标题打个*&#xff0c;方便对比 基本架构* #include<iostream> using namespace std; int main() { system("pause"); return 0; } 打印* cout << "需要打印的内容" <<endl endl 是一个特殊的输出流控…

还在为 Dubbo 服务写 Controller?因为未使用 ShenYu 网关

Dubbo 是一款高性能、轻量级的开源 Java RPC 框架&#xff0c;它可以帮助开发人员快速构建分布式服务。在 Dubbo 应用中&#xff0c;我们经常需要提供 HTTP 调用&#xff0c;如供 H5、外部系统等调用。一般的做法是为需要提供 HTTP 调用的服务编写 Controller&#xff0c;但这并…

高校教务系统登录页面JS分析——西安交通大学

高校教务系统密码加密逻辑及JS逆向 本文将介绍高校教务系统的密码加密逻辑以及使用JavaScript进行逆向分析的过程。通过本文&#xff0c;你将了解到密码加密的基本概念、常用加密算法以及如何通过逆向分析来破解密码。 本文仅供交流学习&#xff0c;勿用于非法用途。 一、密码加…

CUDA学习笔记(八)Branch Divergence and Unrolling Loop

Avoiding Branch Divergence 有时&#xff0c;控制流依赖于thread索引。同一个warp中&#xff0c;一个条件分支可能导致很差的性能。通过重新组织数据获取模式可以减少或避免warp divergence&#xff08;该问题的解释请查看warp解析篇&#xff09;。 The Parallel Reduction …

Mybatis-Plus 0基础光速入门代码

目录 1.创建springboot项目 2.引入依赖 3.找到application.properties&#xff0c;把后缀改成yml&#xff08;这种格式的文件有更多优点&#xff09;&#xff0c;在application.yml里面加上下面的配置代码 4.写实体类 5.创建接口继承BaseMapper 6.在启动类中加上注解Mappe…

头脑风暴之约瑟夫环问题

一 问题的引入 约瑟夫问题的源头完全可以命名为“自杀游戏”。本着和谐友爱和追求本质的目的&#xff0c;可以把问题描述如下&#xff1a; 现有n个人围成一桌坐下&#xff0c;编号从1到n&#xff0c;从编号为1的人开始报数。报数也从1开始&#xff0c;报到m人离席&#xff0c…

阶段七-Day01-Spring01

一、Spring框架介绍 1. 介绍 Spring Framework是由Spring团队研发的模块化、轻量级开源框架。其主要目的是为了简化项目开发。 Spring Framework前身是interface21&#xff0c;由Rod Johnson于2002年研发&#xff0c;主要是为了不使用EJB下依然能够构建高质量Java EE项目。E…

Ubuntu下 u2net tensorrt模型部署

TensorRT系列之 Windows10下yolov8 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov8 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov7 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov6 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov5 tensorrt模型加速…

浅析人脸活体检测技术的功能及几种分类

在日常生活工作中&#xff0c;出现了人脸验证、人脸支付、人脸乘梯、人脸门禁等等常见的应用场景。这说明人脸识别技术已经在门禁安防、金融行业、教育医疗等领域被广泛地应用&#xff0c;人脸识别技术的高速发展与应用同时也出现不少质疑。其中之一就是人脸识别很容易被照片、…

DDOS攻击的有效防护方式有哪些?

DDoS攻击简介&#xff1a; DDoS攻击&#xff0c;即分布式拒绝服务攻击&#xff08;Distributed Denial of Service&#xff09;&#xff0c;是一种网络攻击&#xff0c;旨在通过向目标服务器发送大量恶意请求&#xff0c;使服务器资源耗尽&#xff0c;无法满足合法用户的需求&a…

KubeSphere安装KubeEdge

1. kubesphere安装请参考博客 2. 配置master节点 控制台->平台管理->集群管理->自定义CRD&#xff0c;搜索​​clusterconfiguration​​&#xff0c;查看详情&#xff0c;在资源列表中&#xff0c;点击 ​​ks-installer​​ 右侧的图标&#xff0c;然后选择编辑配…

一文带你彻底弄懂ZGC

1 推荐的文章 1.1 必看 干掉1ms以内的Java垃圾收集器ZGC到底是个什么东西&#xff1f; 1.2 选看 ZGC有什么缺点? 2 疑问【皆来自上面两篇文章】 2.1 什么使得用户线程工作的同时&#xff0c;让垃圾收集器可以回收垃圾-读写屏障 ZGC (Z Garbage Collector) 和读写屏障: …

【瑞吉外卖部分功能补充】

瑞吉外卖部分功能补充 菜品的启售和停售 在浏览器控制台点击对应功能后可以看到前端发送的请求是&#xff1a;http://localhost:9999/dish/status/1?ids1413342036832100354&#xff0c;请求方式为POST。 接收到前端参数后&#xff0c;进行controller层代码补全&#xff0c…