跳至主要內容

AndroidService 相关知识

JI,XIAOYONG...大约 6 分钟

启动一个 Service

  • MyServices.java

    必须继承自 Service,或者如 IntentService 本身就是等其子类

    public class MyServices extends Service {
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            Log.d("TAG","onBind");
            return null;
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.d("TAG","onCreate");
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.d("TAG", "onDestroy: ");
        }
    }
    
  • AndroidManifest.xml

    注册 MyServices

    <application>
    		<service android:name=".MyServices"
                android:exported="true">
                <intent-filter>
                    <action android:name="cf.android666.myservices" />
                </intent-filter>
            </service>
    </application>
    
  • MainActivity.java

    在 java 中调用 Service,需要ServiceConnection

    ServiceConnection mConnection = new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    Log.d(TAG, "onServiceConnected: 服务绑定");
                }
    
                @Override
                public void onServiceDisconnected(ComponentName name) {
                    Log.d(TAG, "onServiceDisconnected: 服务解绑");
                }
            };
    Intent intent = new Intent(context, MyServices.class);
    bindService(intent, mConnection, Service.BIND_AUTO_CREATE);//绑定 Service
    //startService(intent); 启动 service
    unbindService(mConnection);//解绑 Service
    

    bindService()startService()的区别在于:

    ** bindService()将 service 和当前的 activity 绑定在一起,activity 销毁时,service 也会被销毁;

    ** startService()则只是“启动”service,在此后 service 的活动和 activity 无关,并一直存活。

Service 具体分析

Service 在 AndroidManifest.xml 中的属性:

android:name=".MyService"//必须被指定
android:exported=true/false //是否能被其他应用隐式调用
//有 intent-filter 则默认为 true,否则默认 false;若手动指定为 false 则即使有 intent-filter 也无法隐式调用
android:process="remote"/":remote"//前者在共有的进程中进行,后者在名字为{packageName}:remote 的私有进程中进行,其他进行不可访问;如果不设置该属性,则 service 在应用自己的进程里面运行

Service 默认运行在创建他的线程中,要是进行耗时操作,最好在 service 中单独创建一个线程,这样子可以在子线程工作,在主线程中更新工作进度。

Service 中的方法:

//在初次创建服务时调用,并且直至服务死亡,只会被调用一次
void onCreate()

//在绑定服务是才会被调用,必须实现该方法
IBinder onBind(Intent intent)

//每一次通过 startService() 方法启动 Service 的时候都会被调用
int onStartCommand(Intent intent, int flags, int startId)
  //1.intent 启动时,启动组件传递过来的 Intent
  //2.flags 表示启动请求时是否有额外数据,可以是:
  //  0:无
  //  START_FLAG_REDELIVERY:表示该方法返回值为 START_REDELIVER_INTENT,在上个服务被杀死之前调用 stopSelf() 停止服务
  //  START_FLAG_RETRY:在 onStartCommand() 被调用后一直无返回值时,会尝试重新调用 onStartCommand()
  //3.当前服务 id

其中onStartCommand()方法的返回值意义如下:

START_STICKY :service 在内存不足被杀死后,内存空闲时系统会重新创建 service,一旦成功创建会回调onStartCommand()方法,此时 intent 是 null,除非是挂起的 intent 如 pendingintent,无限期运行

START_NOT_STICKY:service 因内存不足被杀死,内存再次空闲系统也不会再重新创建服务,最安全

START_REDELIVER_INTENT:service 因内存不足被杀死,会重建服务并传递给最后一个 intent(最后一次调用startService() 时的 intent),用于连续作业,如下载等

Service 绑定服务的三种方式

1.拓展 Binder 类

要求客户端和服务在同一应用的同一进程内。客户端通过其访问 service 中的公共方法。

步骤如下:

  1. 创建 BindService 服务端,在类中创建一个实现了 IBinder 接口的实力对象并提供公共方法给客户端使用
  2. 在 onBind() 回调方法返回此 Binder 实例
  3. 在客户端的 onServiceConnected() 方法接收 Binder,使用提供的方法绑定服务
//service 服务端
public class LocalService extends Service{
  LocalService mService;
  private LocalBinder binder = new LocalBinder();
  ...
  public IBinder onBind(Intent intent){
    return binder;
  }

  public void doSomeThing(){
    //服务中公共方法,可以被客户端通过 IBInder 获取实例调用
  }

  public class LocalBinder extends Binder{
    LocalService getService(){
      return LocalService.this;
    }
  }
}
//客户端
public class BindActivity extends Activity{
  protected void onCreate(...){
    ServiceConnection conn = new ServiceConnection(){
      //绑定服务时被调用,实现客户端和服务端交互(IBinder)
      public void onServiceConnected(ComponentName name, IBinder service){
        LocalService.LocalBinder binder = (LocalService.LocalBinder)service;//获取服务端 IBinder
        mService = binder.getService();//获取服务实例,以调用服务的公共方法
      }
      //取消绑定时回调,多数时候是 service 被意外销毁,如内存不足
      //当客户端取消绑定时,系统“绝对不会”调用该方法。
      public void onServiceDisconnected(ComponentName name){
        mService = null;
      }
    };
    //创建绑定对象
    Intent intent = new Intent(this,LocalService.class);
    //绑定服务
    //参数 3 flags 则是指定绑定时是否自动创建 Service。0 代表不自动创建、BIND_AUTO_CREATE 则代表自动创建
    bindService(intent,conn,Service.BIND_AUTO_CREATE);
    //调用服务中的方法,最好先判断是否为 null
    mService.doSomeThing();
    //解除绑定
    unbindService(conn);
  }
}

2.Messenger

service 与不同进程通信(IPC)

步骤如下:

  1. Service 实现一个 Handler,接收客户端每个调用的回调
  2. 用 Handler 创建 Messenger 对象
  3. 用 Messenger 创建 IBinder 对象,并通过 onBind() 返回客户端
  4. 客户端使用 IBinder 实例化 Messenger,用其将 Message 对象发送给 Service
  5. Service 在 Handler 接收并处理 Message
//Service
public class MessageService extends Service{
  public final static int MSG_WHAT = 1;
  //创建 Handler 接收、处理客户端 msg
  class IncomingHanler extends Handler{
    public void handleMessage(Message msg){
      //do sth with msg...
    }
  }
  Messenger messenger = new Messenger(new IncomingHanlder());
  public IBinder onBind(Intent intent){
    return messenger.getBinder();
  }
}

//客户端
//onCreate() 方法中:
mConnection = new ServiceConnection(){
  public void onServiceConnected(ComponentName className, IBinder service){
    Messenger mService = new Messenger(service);
  }
};
//给服务发消息
Message msg = Message.obtain(null,MessengerService.MSG_WHAT,0,0);
mService.send(msg);

注意 service 要在不同的进程中:

AndroidMinafast.xml
<service android:name=".messenger.MessengerService"
         android:process=":remote"
        />

服务与客户端双向通信

服务端,修改 IncomingHandler,回复客户端消息

class IncomingHandler extends Handler{
  public void handleMessage(Message msg){
    //回复消息
    Messenger client = msg.replyTo;
    Message replyMsg = Message.obtain(null,MessengerService.MSG_WHAT);
    Bundle bundle = new Bundle();
    bundle.putString("key","value");
    replyMsg.setData(bundle);
    try{
      client.send(replyMsg);
    }catch(){}
  }
}

客户端,增加 Messenger 和 Handler 处理服务端回复

private static class RecyclerReplyMsgHandler extends Hanlder{
  public void handleMessage(Message msg){
    //接收服务端返回的 msg
  //do sth ...
  }
}
private Messenger mRecevierReplyMsg = new Messenger(new RecyclerReplyMsgHandler());

此外,在发送消息是需要将接收服务端回复的 Messenger 通过 Message 的 replyTo 传递给服务端

//create msg...
msg.replyTo = mRecevierReplyMsg;
//send msg...

3.AIDL

一般不会使用,具体使用可以参考这篇文章open in new window

绑定服务时的注意事项

  • 多个客户端可连接一个服务端,只有第一个客户端绑定时才会调用服务onBind()方法来检索 IBinder,此后无需调用就可将同一个 IBinder 传递给其他客户端
  • bindService() 绑定服务是异步进行的
  • 一般在 activity 可见生命周期内绑定 - 取消服务,不要在onResume()onPause()期间执行绑定/解绑

Service 绑定和启动转换

顺序结果
先绑定后启动 service启动 service
先启动后绑定 service会绑定宿主,但是宿主死后仍按照启动 service 方式存活

前台服务和通知

  • startForeground(int id, Notification notification)
    该方法的作用是把当前服务设置为前台服务,其中 id 参数代表唯一标识通知的整型数,需要注意的是提供给 startForeground() 的整型 ID 不得为 0,而 notification 是一个状态栏的通知。
  • stopForeground(boolean removeNotification)
    该方法是用来从前台删除服务,此方法传入一个布尔值,指示是否也删除状态栏通知,true 为删除。注意该方法并不会停止服务。但是,如果在服务正在前台运行时将其停止,则通知也会被删除。

文章参考:

关于 Android Service 真正的完全详解,你需要知道的一切 - CSDN 博客open in new window

文章标题:《AndroidService 相关知识》
本文作者: JI,XIAOYONG
发布时间: 2018/01/14 01:10:01 UTC+8
更新时间: 2023/12/30 16:17:02 UTC+8
written by human, not by AI
本文地址: https://jixiaoyong.github.io/blog/posts/ad4c562c.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 许可协议。转载请注明出处!
你认为这篇文章怎么样?
  • 0
  • 0
  • 0
  • 0
  • 0
  • 0
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8