




已阅读5页,还剩26页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
前言开篇名义,这篇博客介绍一下Android下使用绑定服务进行时数据交互的几种方法。关于Android下Service的内容,前面两篇博客已经介绍了,不清楚的可以移步过去先看看:Android-Service之基础、Android-Service之提高。在前面的博客中已经介绍到了,对于Service组件而言,它只有在绑定模式下才可以与客户端进行时交互,这里讲解几个方法进行绑定服务与客户端间的交互方法:1. 使用IBinder接口 2. 使用Messenger类 3. 使用AIDL 虽然根据官方文档给出了三个方法,其中AIDL涉及的内容超出本博客内容范围,以后有机会在另外介绍,本篇博客只介绍1、2两种方式的数据交互。使用IBinder接口如果看了之前关于Service博客的人,应该对IBinder接口有所了解,这里简单介绍一下IBinder。IBinder是远程对象的基本接口,是为高性能而设计的轻量级远程调用机制的核心部分。但它不仅用于远程调用,也可以用于进程内调用。这个接口定义了与远程对象交互的协议,一般不直接实现这个接口,而是从它的实现类Binder中继承。通过IBinder进行服务的交互一般有两种方式,一种方式是使用IBinder.transact()方法向远端的IBinder对象发送一个发出调用,会回调远端的Binder.onTransact()方法,这个方法传递的数据是Parcel。Parcel是一种缓冲区,除了数据外还有有一些描述它内容的元素,如果查看源码的话会发现,Parcel本质上是一个Serialize,只是它在内存中完成了序列化和反序列化,利用的是连续的内存空间,因此效率会更高,并且AIDL的数据也是通过Parcel来交互的。另外一种方法就是抛弃IBinder中原生的方法,使用自定义的接口方法进行数据交互,这也是Android官方推荐绑定服务的一种数据交互方式。当然,不管是使用transact()给远程服务交互,还是使用自定义的接口交互,都是同步执行的,直到远程服务执行完并返回结果才会继续向下执行。其他关于适应IBinder服务的内容,在博客Android-Service之基础中已经讲解过了,这里不再累述。下面使用一个例子来演示一下使用自定义接口与服务进行交互的例子。服务:IBinderSer.java 1 package cn.bgxt.servicebinddatedemo; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.os.Binder; 6 import android.os.IBinder; 7 import android.os.Parcel; 8 import android.os.RemoteException; 9 import android.util.Log;10 11 public class IBinderSer extends Service 12 private final String TAG=main;13 private final int MULTIPLE=1024; 14 public final IBinder mBinder=new LocalBinder();15 16 public class LocalBinder extends Binder17 / 在Binder中定义一个自定义的接口用于数据交互18 / 这里直接把当前的服务传回给宿主19 public IBinderSer getService()20 return IBinderSer.this;21 22 23 24 Override25 public IBinder onBind(Intent intent) 26 Log.i(TAG, The service is binding!);27 / 绑定服务,把当前服务的IBinder对象的引用传递给宿主28 return mBinder;29 30 31 public int getMultipleNum(int num)32 / 定义一个方法 用于数据交互33 return MULTIPLE*num;34 35 调用服务的Activity:IBinderActivity.java 1 package cn.bgxt.servicebinddatedemo; 2 3 import android.app.Activity; 4 import android.app.Service; 5 import android.content.ComponentName; 6 import android.content.Intent; 7 import android.content.ServiceConnection; 8 import android.os.Bundle; 9 import android.os.IBinder;10 import android.os.Messenger;11 import android.os.Parcel;12 import android.os.RemoteException;13 import android.view.View;14 import android.widget.Button;15 import android.widget.Toast;16 17 public class IBinderActivity extends Activity 18 private Button btnStart, btnInvoke, btnStop;19 IBinderSer mService=null;20 private ServiceConnection mConnection = new ServiceConnection() 21 22 Override23 public void onServiceDisconnected(ComponentName name) 24 mService = null;25 26 27 Override28 public void onServiceConnected(ComponentName name, IBinder service) 29 / 获取服务上的IBinder对象,调用IBinder对象中定义的自定义方法,获取Service对象30 IBinderSer.LocalBinder binder=(IBinderSer.LocalBinder)service;31 mService=binder.getService();32 33 ;34 35 Override36 protected void onCreate(Bundle savedInstanceState) 37 super.onCreate(savedInstanceState);38 setContentView(R.layout.layout_service);39 btnStart = (Button) findViewById(R.id.btnStartSer);40 btnInvoke = (Button) findViewById(R.id.btnInvokeMethod);41 btnStop = (Button) findViewById(R.id.btnStopSer);42 43 btnStart.setOnClickListener(onclick);44 btnInvoke.setOnClickListener(onclick);45 btnStop.setOnClickListener(onclick);46 47 48 View.OnClickListener onclick = new View.OnClickListener() 49 50 Override51 public void onClick(View v) 52 switch (v.getId() 53 case R.id.btnStartSer:54 Toast.makeText(getApplicationContext(), 绑定服务成功, Toast.LENGTH_SHORT).show();55 bindService(new Intent(IBinderActivity.this,IBinderSer.class),mConnection,Service.BIND_AUTO_CREATE); 56 break;57 case R.id.btnInvokeMethod:58 if(mService=null)59 Toast.makeText(getApplicationContext(), 请先绑定服务, Toast.LENGTH_SHORT).show();60 return;61 62 / 调用绑定服务上的方法,进行数据交互63 int iResult=mService.getMultipleNum(10);64 Toast.makeText(getApplicationContext(), 服务计算结果为:+iResult, Toast.LENGTH_SHORT).show();65 break;66 case R.id.btnStopSer:67 Toast.makeText(getApplicationContext(), 服务解除绑定, Toast.LENGTH_SHORT).show();68 unbindService(mConnection);69 mService=null;70 break;71 default:72 break;73 74 75 ;76 执行结果:使用Messenger类除了使用IBinder之外,还可以使用Messenger,那么先来聊聊什么是Messenger。Messenger引用了一个Handler独享,可以使用Messenger.send(Message msg)方法跨进程向服务发送消息,只需要在服务中使用Handler创建一个Messenger,宿主持有这个Messenger就可以与服务进行通信。之前介绍的handler+Message的通信方式不同,那都是在同一个进程中的,从工作线程持有一个主线程的Handler对象,从而向主线程发送消息,这里不了解的可以看看之前的博客:Android-多线程之Handler。而上面介绍过了,Android可以使用IBinder实现跨进程通信,并且也将Handler与IBinder结合起来实现跨进程发送消息。当然这里提一下,Messenger管理的是一个消息队列,它会依据消息进入的先后次序予以执行,所以也不需要把服务设计为线程安全是。实现Messenger实现进程通信,主要有以下几点注意:1. 在服务中实现一个Handler类,并实例化它,在handleMessage()方法中接收客户端的请求。 2. 在服务中使用这个Handler对象创建一个Messenger对象。 3. 使用Messenger对象的getBinder()方法返回一个IBinder对象作为onBind()的返回值返回给客户端。 4. 在客户端使用IBinder实例化一个Messenger对象,并使用它向服务端发送信息。 下面通过一个简单的例子来演示一下利用Messenger在服务与客户端进行的通信。服务:MessengerSer.java 1 package cn.bgxt.servicebinddatedemo; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.os.Handler; 6 import android.os.IBinder; 7 import android.os.Message; 8 import android.os.Messenger; 9 import android.util.Log;10 import android.widget.Toast;11 12 public class MessengerSer extends Service 13 private final String TAG=main;14 static final int MSG_SAY_HELLO = 1;15 16 public class IncomingHandler extends Handler 17 Override18 public void handleMessage(Message msg) 19 switch (msg.what) 20 case MSG_SAY_HELLO:21 Toast.makeText(getApplicationContext(), Service say hello!,22 Toast.LENGTH_SHORT).show();23 Log.i(TAG, Service say hello!);24 break;25 default:26 super.handleMessage(msg);27 28 29 30 31 IncomingHandler incomingHandler=new IncomingHandler();32 final Messenger mMessenger=new Messenger(new IncomingHandler();33 34 Override35 public IBinder onBind(Intent arg0) 36 return mMessenger.getBinder();37 38 39 服务绑定的Activity:MessengerActivity.java 1 package cn.bgxt.servicebinddatedemo; 2 3 4 import android.app.Activity; 5 import android.app.Service; 6 import android.content.ComponentName; 7 import android.content.Intent; 8 import android.content.ServiceConnection; 9 import android.os.Bundle;10 import android.os.IBinder;11 import android.os.Message;12 import android.os.Messenger;13 import android.os.RemoteException;14 import android.view.View;15 import android.widget.Button;16 import android.widget.Toast;17 18 public class MessengerActivity extends Activity 19 private Button btnStart, btnInvoke, btnStop;20 private Messenger mService = null;21 22 private ServiceConnection mConnection = new ServiceConnection() 23 24 Override25 public void onServiceDisconnected(ComponentName name) 26 mService = null;27 28 29 Override30 public void onServiceConnected(ComponentName name, IBinder service) 31 / 使用服务端的IBinder对象实例化一个Messenger对象32 mService = new Messenger(service);33 34 ;35 Override36 protected void onCreate(Bundle savedInstanceState) 37 / TODO Auto-generated method stub38 super.onCreate(savedInstanceState);39 setContentView(R.layout.layout_service);40 btnStart = (Button) findViewById(R.id.btnStartSer);41 btnInvoke = (Button) findViewById(R.id.btnInvokeMethod);42 btnStop = (Button) findViewById(R.id.btnStopSer);43 44 btnStart.setOnClickListener(onclick);45 btnInvoke.setOnClickListener(onclick);46 btnStop.setOnClickListener(onclick);47 48 49 View.OnClickListener onclick = new View.OnClickListener() 50 51 Override52 public void onClick(View v) 53 switch (v.getId() 54 case R.id.btnStartSer:55 Toast.makeText(getApplicationContext(), 绑定服务成功, Toast.LENGTH_SHORT).show();56 bindService(new Intent(getApplicationContext(),MessengerSer.class), mConnection, Service.BIND_AUTO_CREATE);57 break;58 case R.id.btnInvokeMethod:59 if(mService=null)60 Toast.makeText(getApplicationContext(), 请先绑定服务,Toast.LENGTH_SHORT).show();61 return ;62 63 / 实例化一个Message对象64 Message msg=Message.obtain(null, MessengerSer.MSG_SAY_HELLO, 0, 0);65 try66 / 把Message独享传递给服务端处理67 mService.send(msg);68 69 catch(RemoteException e)70 e.printStackTrace();71 72 break;73 case R.id.btnStopSer:74 Toast.makeText(getApplicationContext(), 服务解除绑定, Toast.LENGTH_SHORT).show();75 unbindService(mConnection);76 mService=null;77 break;78 default:79 break;80 81 82 83 ;84 执行结果:使用AIDLAIDL(Android Interface Definition Language),它可以实现跨进程间的通信。之前讲到的Messenger实现跨进程通信,其实也是基于AIDL作为底层结构。但是正如上面提到的,Messenger创建的一个消息队列是在一个单独的线程中,所以服务一次仅处理一个请求,然而,如果想要服务同时处理多个请求,就需要使用到AIDL,但是这种情况下就要考虑多线程和线程安全的问题了。这个不在本篇博客的范畴内,以后有机会在细细讲解。Android-Service之提高 前言上一篇博客讲解了一下Android下Service组件的基本使用,对Service组件还不了解的朋友可以先去看看另外一篇Service基础的博客:Android-Service之基础。这篇博客讲解一下Service组件的一些需要注意的地方以及高级的应用,并用几个例子讲解一下本文中提到的功能,最后依然会提供示例源码下载。既然是深入讲解Service,本片博客涉及的内容有点杂乱,这里列个导航方便查看。1. IntentService的使用 2. Service与Thread的区别 3. Service生命周期详解 4. 前台服务 5. 服务资源被系统意外回收处理办法 6. 不被销毁的服务 IntentService对于Service而言,它依然是运行在主线程之上,所以一些无法在主线程上完成的功能,依然需要另外开启工作线程来完成,并且一些耗时操作,如果直接放在Service的主线程中完成的话,会影响设备的运行流畅度。对于这样的问题,有两种解决方案,一种是在Service主线程中额外开启一条工作线程,如何开启工作线程的方法在以前的博客中已经介绍过了,这里不再重复介绍;另外一个方法就是使用IntentService这个父类来实现Service业务类,这里着重讲解这个IntentService。IntentService是一个服务基类,直接继承于Service,在需要的时候通过异步调用的方式处理请求。要使用IntentService启动一个服务进行异步调用,需要实现它的一个抽象方法:onHandleIntent(Intent intent),在这个方法里,可以获得访问这传来的Intent对象,并在其中进行异步操作的实现。对于IntentService而言,因为其继承自Service类,所以其他的Service的声明周期方法在此类中也适用,访问者可以通过调用Context.startService(Intent intent)方法来启动这个服务。使用IntentService作为服务基类,在其内部其实也是重新开启了一条线程来完成操作,只是这里使用IntentService进行了封装,并且它自己管理服务的结束。使用IntentService的服务类,在执行结束后执行结束,无需人为的结束它,比较适用于一些无需管理,但是又比较耗时的操作!IntentService-Demo下面通过一个小示例演示IntentService的操作,在这个示例中,下载一张网络上的图片,保存在设备的本地目录下。其中涉及到的本地存储和访问网络下载图片的内容,如果你不了解的朋友,可以查看另外两篇博客:Android-数据持久化、Android-HTTPClient。IntentSer.java: 1 package cn.bgxt.servicedemohigh; 2 3 import java.io.File; 4 import java.io.FileOutputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import .MalformedURLException; 8 import .URL; 9 10 import android.app.IntentService;11 import android.content.Intent;12 import android.util.Log;13 import android.widget.Toast;14 15 public class IntentSer extends IntentService 16 private final static String TAG = main;17 private String url_path=/bmiddle/9dc6852bjw1e8gk397jt9j20c8085dg6.jpg;18 public IntentSer() 19 super(IntentSer);20 21 Override22 public void onCreate() 23 Log.i(TAG, Service is Created);24 super.onCreate();25 26 27 Override28 public int onStartCommand(Intent intent, int flags, int startId) 29 Log.i(TAG, Service is started);30 return super.onStartCommand(intent, flags, startId);31 32 33 Override34 public void onDestroy() 35 Log.i(TAG, Service is Destroyed);36 super.onDestroy();37 38 Override39 protected void onHandleIntent(Intent intent) 40 Log.i(TAG, HandleIntent is execute);41 try 42 / 在设备应用目录下创建一个文件43 File file=new File(this.getFilesDir(), weibo.jpg);44 FileOutputStream fos=new FileOutputStream(file); 45 / 获取网络图片的输入流46 InputStream inputStream = new URL(url_path).openStream();47 / 把网络图片输入流写入文件的输出流中48 byte date=new byte1024;49 int len=-1;50 while(len=inputStream.read(date)!=-1)51 fos.write(date, 0, len);52 53 54 fos.close();55 inputStream.close(); 56 Log.i(TAG, The file download is complete);57 catch (MalformedURLException e) 58 e.printStackTrace();59 catch (IOException e) 60 e.printStackTrace();61 62 63 编写好服务类,仅需要适用一个Android组件即可启动服务,这里使用一个Activity中来启动服务,下面是主要代码。 1 btnDownload=(Button)findViewById(R.id.btnDownload); 2 btnDownload.setOnClickListener(new View.OnClickListener() 3 4 Override 5 public void onClick(View v) 6 / TODO Auto-generated method stub 7 Intent service=new Intent(MainActivity.this,IntentSer.class); 8 startService(service); 9 10 );执行结果均写入日志中,可以通过LoaCat查看。下载完成后,可以在应用的安装目录下/files下看到下载的weibo.jpg文件。Service与Thread的区别对Service了解后,会发现它实现的大部分功能使用Thread也可以解决,并且Thread使用起来比Service方便的多,那么为什么还需要使用Service呢,下面来详细解释一下。首先,Thread是程序执行的最小单元,它是分配系统资源的基本单位,主要用于执行一些异步的操作。而Service是Android的一种机制,当它使用bindService()被绑定的时候,是运行在宿主主进程的主线程上的,当使用startService()启动服务的时候,是独立运行在独立进程的主线程上的,因此它们的核心没有任何关系。其次,对于Thread而言,它是独立于启动它的组件的,如使用一个Activity启动了一个Thread,当这个Activity被销毁前,没有主动停止Thread或者Thread的run()方法没有执行完毕的话,Thread也会一直执行下去,这样就很容易导致一些问题,当这个Activity被销毁之后,将不再持有这个Thread的引用,也就是说,无法再在另外一个Activity中对同一个Thread进行控制。而Service不同,在Android系统中,无论启动或绑定几次,只会创建一个对应的Service实例,所以只要这个Service在运行,就可以在能获取到Context对象的地方控制它,这些特点是Thread无法做到的。Service生命周期详解上一篇博客简单讲解了一下Service的生命周期,但是讲解的不够详细,这里另外再解释一下,下图是官方API中给定的关于Service生命周期的图示:从上图可以看出,对于启动服务和绑定服务存在不同的生命周期,但是大部分调用的生命周期方法是一样的,如onCreate()、onDestroy()等,并且无论是启动或是绑定多次同一个服务,onCreate()、onDestroy()等这些共用的生命周期方法也仅被调用一次。它们唯一的区别就是当使用startService()启动服务的时候,回调的是onStatrCommand()方法,而使用bindService()绑定服务的时候,回调的是onBind()方法,并且解除绑定的时候还会回调onUnbind()方法。下面详细解说一下启动服务和绑定服务的生命周期: 启动服务:如果一个Service被Android组件调用startService()方法启动,那么不管这个Service对象是否使用bindService()方法被访问者绑定过,该Service都会在后台运行。因为Android系统只会为一个Service服务创建一个实例,所以无论启动几次,onCreate()方法仅执行一次,但是每次启动都会执行onStartCommand()方法,一旦服务启动,不管访问者是否被销毁,服务将一直执行下去,除非被调用stopService()方法或者服务自身的stopSelf()方法,当然系统资源不足的时候,也有可能回收结束服务回收资源。 绑定服务:如果一个Service被某个Android组件调用bindService()方法绑定服务,同样因为一个Service只会被创建一个实例,所以不管bindService()被不同的组件调用几次,onCreate()方法都只被回调一次,转而还会执行onBind()方法进行Binder对象的绑定。在绑定连接被建立后,Service将一直运行下去,除非宿主调用unbindService()方法断开连接或者之前的宿主被销毁了,这个时候系统会检测这个Service是否存在宿主绑定,当宿主绑定的数量为0的时候,系统将会自动停止服务,对应的onDestroy()将被回调。 正如上面提到的,Android系统仅会为一个Service创建一个实例,所以不管是使用启动服务或是绑定服务,都操作的是同一个Service实例。但是如果两种服务运行方式均被调用,那么绑定服务将会转为启动服务运行,这时就算之前绑定的宿主被销毁了,也不会影响服务的运行,而启动服务并不会因为有宿主调用了bindService()方法而把原本的启动服务转为绑定服务,但是还是会与宿主产生绑定,但这时即使宿主解除绑定后,服务依然按启动服务的生命周期在后台运行,直到有Context调用了stopService()或是服务本身调用了stopSelf()方法才会真正销毁服务。这样理解感觉启动服务的优先级要比绑定服务高,当然不管哪种情况,被系统回收的资源不在此讨论的范围内。所以综上所述,对于一个既使用startService()启动又使用bindService()绑定的服务,除非这个服务的两条生命周期均完结,否则不会被销毁。也就是说,在不考虑系统在资源不足的时候,主动回收资源销毁服务的情况下,使用startService()启动的服务,必须使用stopService()或是服务本身的stopSelf()停止服务,使用bindService()绑定的服务,必须使用unbindService()或是销毁宿主来解除绑定,否则服务一直运行。在实际项目中,经常会使用开始服务于绑定服务混合使用,这样既保证了一个有效的服务在后台长期运行,又可以在需要的时候通过bindService()绑定服务,从而与服务进行交互。前台服务一个Service不管是被启动或是被绑定,默认是运行在后台的,但是有一种特殊的服务叫后台服务,它是一种能被用户意识到它存在的服务,因此系统默认是不会自动销毁它的,但是必须提供一个状态栏通知,Notification,在通知栏放置一个持续的标题,所以这个通知是不能被忽略的,除非服务被停止或从前台删除。关于通知栏的内容,可以查看另外一篇博客:Android-通知之Notification。这类服务主要用于一些需要用户能意识到它在后台运行,并且随时可以操作的业务,如音乐播放器,设置为前台服务,使用一个Notification显示在通知栏,可以试用户切歌或是暂停之类的。前台服务与普通服务的定义规则是一样的,也是需要继承Service,这里没有区别,唯一的区别是在服务里需要使用Service.startFroeground()方法设置当前服务为一个前台服务,并为其制定Notification。下面是startForeground()的完整签名。public final void startForeground(int id,Notification notification)其中的参数id是一个唯一标识通知的整数,但是这里注意这个整数一定不能为0,notification为前台服务的通知,并且这个notification对象只需要使用startForeground()方法设置即可,无需像普通通知一样使用NotificationManager对象操作通知。前台服务可以通过调用stopService(boolean bool)来使当前服务退出前台,但是并不会停止服务,传递false即可。有一点需要声明一下,startForeground()需要在Android2.0之后的版本才生效,在这之前的版本使用setForeground()来设置前台服务,并且需要NotificationManager对象来管理通知,但是现在市面上的设备基本上已经很少有2.0或一下的设备了,所以也不用太在意。前台服务-示例下面通过一个示例来演示一个简单的前台服务,这个前台服务展示为一个通知,并且点击通知的时候会开启一个新的ActivityForegroundService.java 1 package cn.bgxt.servicedemohigh; 2 3 import android.app.Notification; 4
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025云南机电职业技术学院选聘云南省产业导师(12人)考试模拟试题及答案解析
- 2025四川绵阳三台县教体系统考调事业单位工作人员6人备考考试试题及答案解析
- 2025浙江农业商贸职业学院劳务派遣人员招聘1人考试模拟试题及答案解析
- 2025湖南怀化市靖州苗族侗族自治县政务服务中心见习人员招聘2人备考考试题库附答案解析
- 2025中山大学附属第一医院广西医院神经外科等科室人才招聘6人考试模拟试题及答案解析
- 2025湖南郴州市宜航人力资源有限责任公司招聘项目现场管理人员、现场技术人员2人考试参考题库及答案解析
- 2025年淮北濉溪职业技术学校公开招聘代课教师33名考试模拟试题及答案解析
- 2025年财会类初级银行从业人员-法律法规与综合能力参考题库含答案解析(5卷)
- 2025年下半年新疆兵团招聘事业单位工作人员(2398人)备考考试题库附答案解析
- 2025年赣州市南康区初中学校公开选调区内教师备考考试题库附答案解析
- 采购进口生蚝合同协议书
- 鼓号队培训课件内容
- 液体外渗的预防与处理 2
- 2025山西吕梁文水县公办幼儿园幼儿业务辅助人员招聘120人笔试参考题库附答案解析
- 2024年云南航空产业投资集团招聘考试真题
- 柚子树栽培技术
- 蓝莓种植加工一体化发展项目可行性研究报告写作模板-申批备案
- 2025年秋季新学期教学工作会议校长讲话:一心一意抓质量一点一滴见成效一步一脚印做教学
- 上腔静脉综合征护理查房
- 安徽省2025年公需科目培训测验答案(科目一)
- 2025年新退休返聘人员协议书
评论
0/150
提交评论