Android 四大组件之再论service

service常见的有2种方式,本地service以及remote service。

这2种的生命周期,同activity的通信方式等,都不相同。

关于这2种service如何使用,这里不做介绍,只是介绍一些被遗漏的地方

1.远程Service(AIDL方式)

package com.joyfulmath.samples.basecontrol;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;

import com.joyfulmath.samples.R;
import com.joyfulmath.samples.TraceLog;

import org.androidannotations.annotations.Click;
import org.androidannotations.annotations.EActivity;

/**
 * Created by Administrator on 2016/10/11 0011.
 * service connect activity samples
 */
@EActivity(R.layout.activity_connect_service)
public class ServiceConActivity extends Activity {
    private ISamplesAidlInterface binder;
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            TraceLog.i();
            binder = ISamplesAidlInterface.Stub.asInterface(service);
            if(binder!=null)
            {
                try {
                    binder.registerCallBack(mCallBack);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            TraceLog.i();
            binder = null;
        }
    };

    private ICallBack.Stub mCallBack = new ICallBack.Stub() {
        @Override
        public void onServiceStateChanged(int s) throws RemoteException {
            TraceLog.i(String.valueOf(s));
        }
    };

    public void bindSamplesService()
    {
        TraceLog.i();
        Intent intent = new Intent(this,ServiceSamples.class);
//        intent.setAction("com.joyfulmath.service.samples");
        bindService(intent,connection,BIND_AUTO_CREATE);
    }

    public void unBindSamplesService()
    {
        TraceLog.i();
        if(binder!=null)
        {
            try {
                binder.unRegisterCallBack(mCallBack);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(connection);
    }

    @Click(R.id.btn_connect)
    void connectClick()
    {
        TraceLog.i();
        bindSamplesService();
    }

    @Click(R.id.btn_unconnect)
    void unConnectClick()
    {
        TraceLog.i();
        unBindSamplesService();
    }

    @Click(R.id.btn_do)
    void doAction()
    {
        if(binder!=null)
        {
            try {
                int r = binder.doBackground("action");
                TraceLog.i(String.valueOf(r));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        TraceLog.i();
        unBindSamplesService();
    }
}

ServiceConActivity

package com.joyfulmath.samples.basecontrol;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;

import com.joyfulmath.samples.TraceLog;

/**
 * Created by Administrator on 2016/10/11 0011.
 */
public class ServiceSamples extends Service {

    private SamplesBinder samplesBinder = null;
    private RemoteCallbackList<ICallBack> mCallbacks = new RemoteCallbackList<>();

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        TraceLog.i();
        return samplesBinder;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        TraceLog.i();
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public boolean onUnbind(Intent intent) {
        TraceLog.i();
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        TraceLog.i();
        mCallbacks.kill();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        samplesBinder = new SamplesBinder();
        TraceLog.i();
    }

    public class SamplesBinder extends ISamplesAidlInterface.Stub{

        @Override
        public int doBackground(String action) throws RemoteException {
            TraceLog.i();
            return -1;
        }

        @Override
        public void findPerson(PersonCall p) throws RemoteException {
            notifyFindPerson();
        }

        @Override
        public void registerCallBack(ICallBack cb) throws RemoteException {
            mCallbacks.register(cb);
        }

        @Override
        public void unRegisterCallBack(ICallBack cb) throws RemoteException {
            mCallbacks.unregister(cb);
        }
    }

    private void notifyFindPerson() throws RemoteException {
        try{
            synchronized (this){
                int n = mCallbacks.beginBroadcast();
                for(int i=0;i<n;i++){
                    mCallbacks.getBroadcastItem(i).onServiceStateChanged(0x11);
                }
                mCallbacks.finishBroadcast();
            }
        }catch (RemoteException e)
        {
            TraceLog.i(e.getMessage());
        }

    }
}

ServiceSamples

这是简单的service & activity交互的代码。

在看关键的AIDL代码:

// ISamplesAidlInterface.aidl
package com.joyfulmath.samples.basecontrol;
import com.joyfulmath.samples.basecontrol.PersonCall;
import com.joyfulmath.samples.basecontrol.ICallBack;
// Declare any non-default types here with import statements

interface ISamplesAidlInterface {

    int doBackground(in String action);
    void findPerson(in PersonCall p);
    void registerCallBack(ICallBack cb);
    void unRegisterCallBack(ICallBack cb);
}
// PersonCall.aidl
package com.joyfulmath.samples.basecontrol;
parcelable PersonCall;
// ICallBack.aidl
package com.joyfulmath.samples.basecontrol;

// Declare any non-default types here with import statements

interface ICallBack {
    void onServiceStateChanged(int s);
}

这里有3个问题,我们从头往下看,就能明白。

1)为什么在其他APK调用该service的时候,aidl的文件包必须一致

2)为什么要自定义PersonCall.aidl

3) ICallBack是什么玩意。

4)多个APK连接同一个service,该service会产生多个实例吗。怎么保证不冲突呢?

其实1) & 2)的问题是一样的,都是基于java的classloader原理。

同一个类,必须在同一个包内,并且由同一个classloader加载,才能表示他们是同一个类。

所以AIDL在拷贝的时候,必须保证是同一个包名(AIDL在打包的时候会生成java文件。)

并且自定义的参数class,必须有AIDL定义,才能让其他APK可以理解该类。当然为了传输,需要继承自pracacle

3)关于service回调的工作,是由RemoteCallbackList 专门用来回调通知client端。

首先在client端定义的listener,远端是没有实体对象的,所以在作为参数传入到远端的时候,会复制一份,并且与binder绑定。

下面来看看真正的干货,第4个问题:

我们分成几个小问题来解答。

I,如果service和activity不在同一个app,那么activity可以通过startservice or bindservice的方式启动该service吗?如果不行,怎么启动该service。

经测试,可以通过bindservice的方式启动。

II,如果2个client同时对同一个service做bind操作,会有什么结果?

binderservice都会返回成功操作,并且前一个client,没有收到disconnect的通知。

此时service的操作,会返回对后面一个client传递的参数的操作,也就是只有一份service实例,会同时binder2个client,but只会处理后面一个client的行为。

所以此时,service应该阻止由其他client端输入的请求,并且可以提供接口给到client,由他决定是否关闭这个binder。

下面是bindservice的flag参数说明:

常量名 含义
BIND_ABOVE_CLIENT 8 如果当绑定服务期间遇到OOM需要杀死进程,客户进程会先于服务进程被杀死。
BIND_ADJUST_WITH_ACTIVITY 128 允许客户进程提升被绑定服务进程的优先级
BIND_ALLOW_OOM_MANAGEMENT 16 如果绑定服务期间遇到OOM需要杀死进程,被绑定的服务进程会被OOM列入猎杀对象中。
BIND_AUTO_CREATE 1 若绑定服务时服务未启动,则会自动启动服务。 注意,这种情况下服务的onStartCommand仍然未被调用(它只会在显式调用startService时才会被调用)。
BIND_DEBUG_UNBIND 2 使用此标志绑定服务之后的unBindService方法会无效。 这种方法会引起内存泄露,只能在调试时使用。
BIND_IMPORTANT 64 被绑定的服务进程优先级会被提到FOREGROUND级别
BIND_NOT_FOREGROUND 4 被绑定的服务进程优先级不允许被提到FOREGROUND级别
BIND_WAIVE_PRIORITY 32 被绑定的服务进程不会被OOM列入猎杀对象中。

可以看到,他们是可以组合使用的。

如果在第三方APP 使用service

第一步:在java同级目录下,创建aidl文件夹

第二步:把AIDL文件copy该目录下,注意保持包名一致。

第三步:把自定义的class,copy到java目录下,包名一致。

第四步:启动service需要用显示的定义(android5.0开始):

    public void bindSamplesService()
    {
        TraceLog.i();
//        ComponentName name = new ComponentName("com.joyfulmath.samples","ServiceSamples");
        Intent intent = new Intent();
//        intent.setComponent(name);
        intent.setAction("com.joyfulmath.service.samples");
        intent.setPackage("com.joyfulmath.samples");
        intent.putExtra("cookie","third");
        bindService(intent,connection,BIND_AUTO_CREATE|BIND_ADJUST_WITH_ACTIVITY);
    }

android 5.1上,亲测,该方式可行,使用componentName不行,需进一步研究。

2.startservice

startservice可以跨进程调用,也就是调用其他app的service。

    public void bindSamplesService()
    {
        TraceLog.i();
        Intent intent = new Intent();
        intent.setAction("com.joyfulmath.service.samples");
        intent.setPackage("com.joyfulmath.samples");
        intent.putExtra("cookie","third");
//        bindService(intent,connection,BIND_AUTO_CREATE|BIND_ADJUST_WITH_ACTIVITY);
        startService(intent);
    }

关于startservice,你所不知道的内容如下:

public abstract ComponentName startService(Intent service);

该方法还会返回一个ComponentName ,这个name就是表示package+name,因为classname会重复。

    * @return If the service is being started or is already running, the
     * {@link ComponentName} of the actual service that was started is
     * returned; else if the service does not exist null is returned.

注释说的很清楚。

    * <p>This function will throw {@link SecurityException} if you do not
     * have permission to start the given service.

没有权限,就会报安全异常。

跨进程启动service的流程:

如果考虑到进程,那么我们就应该暂时撇开四大组件的概念。

从操作系统,进程线程的本质来考虑问题。

Activity是生存在一个ActivityThread。它就是一个app(一般对应一个进程)的主线程。

那么service在哪里,也在主线程中。可以通过tracelong来认证这个结论。

所以说,service虽然是有独立生命周期的一大组件,但是它默认还是在主线程中。所以也会ANR。

既然要跨进程,必然也需要binder机制,可能我们看不到而已。

大致流程如下:

从主进程调用到AMS进程(SystemServer进程),创建新的进程。这个过程需要用到binder通信。

从新进程回调AMS,获取新进程的一些信息。关键是这些信息是从源进程传递过来+manifest注册的。

从AMS回到新进程,直到新进程启动(同时包括service启动)

这三步都是跨进程启动service的过程,都需要binder机制来通信。

具体详细流程,后续会继续分析。

3.process lifecycle

关于service对应的lifecycle已经在activity那篇里说明了。

Android 四大组件之Activity(续2)

4.binder机制

关于这块之前以及有相关博文,接下来打算再详细分析下。binder机制是android最重要的基石。

server 会在通过servermanger注册它,然后提供远程调用的句柄,通过binder机制

client获取servermanger不需要通过binder,应为servermanger是默认的句柄为0,可以直接获取到。

所以说,servermanager是在等待client端发送请求,然后它去寻找以及注册的server,得到它的远程对象,进行通信。

时间: 2024-12-27 14:07:06

Android 四大组件之再论service的相关文章

Android四大组件及其应用--&gt;

android四大组件分别为:activity/service/content provider/broadcast receiver; 1.activity--> (1)一个Activity通常就是一个单独的屏幕(窗口). (2)Activity之间通过Intent进行通信. (3)android应用中每一个Activity都必须要在AndroidManifest.xml配置文件中声明,否则系统将不识别也不执行该Activity. 2.service--> (1)service用于在后台完成

Android四大组件——Service

Service相关链接 Service初涉 Service进阶 Service精通 Service是Android系统中的一种组件,它跟Activity的级别差不多,但是它不能自己运行,只能后台运行,并且可以和其他组件进行交互.Service是没有界面的长生命周期的代码.Service是一种程序,它可以运行很长时间,但是它却没有用户界面.这么说有点枯燥,来看个例子,打开一个音乐播放器的程序,这个时候若想上网了,那么,我们打开Android浏览器,这个时候虽然我们已经进入了浏览器程序,但是,歌曲播

Android 四大组件之Service(上)

1.Service简介 Service是Android四大组件中最与Activity相似的组件,他们都代表可执行的程序.Service一直运行于后台,不会与用户交互,可用来处理一些耗时的任务(比如:后台播放音乐,I/O操作等).它的创建.配置与Activity基本相似,下面将详细介绍Android Service的开发. 2.创建.配置Service 2.1 定义一个继承Service类的子类 2.2 在AndroidManifest.xml中配置该Service 需要注意的是 Service和

Android 四大组件之service与Broadcast

Android 四大组件之一:service: Service有五个生命周期:onCreat,onStartCommand, onBind,onUnbind, onDestroy 主要有绑定和非绑定两种方式 首相在Activity中设置四个Button,用于测试绑定和非绑定两种方式,按下Button播放音乐,停止音乐,非绑定用StopService停止,绑定方式用Bind Service启动,解绑用unbindService停止. 非绑定:Intent intent=new Intent(Mai

Android 四大组件之Service详解

                   Android四大组件之Service详解    来这实习已经10多天了,今天整理整理学习时的Android笔记.正所谓好记性不如烂笔头,今天来说说service组件. service可以在和多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务嘛,总是藏在后头的. Service是在一段不定的时间运行在后台,不和用户交互应用组件.每个

【Android的从零单排开发日记】之入门篇(五)——Android四大组件之Service

这几天忙着驾校考试,连电脑都碰不到了,今天总算告一段落了~~Service作为Android的服务组件,默默地在后台为整个程序服务,辅助应用与系统中的其他组件或系统服务进行沟通.它跟Activity的级别差不多,但不能自己运行只能后台运行.service可以在很多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等, 总之服务总是藏在后台的. ps:Service运行在主线程中的,所

Android 四大组件 (二) Service 使用

一. Service 介绍 Service属于android四大组件之一,在很多地方经常被用到.开启Service有两种不同的方式:startService和bindService.不同的开启方式,Service执行的生命周期方法也不同. 分 显示/隐示调用 ,但是官网推荐用显式的方式启动Service.下面 service使用 用的就是显示调用:注意事项用的就是隐示调用,在5.0系统上隐示调用会报错.所以这里只介绍使用显示调用. 不能再service里做耗时操作,否则ANR:需要开辟子线程进行

Android深入四大组件(三)Service的绑定过程

相关文章 Android深入理解四大组件系列 前言 我们可以通过调用Context的startService来启动Service,也可以通过Context的bindService来绑定Service,建议阅读此篇文章前请阅读Android深入四大组件(二)Service的启动过程这篇文章,知识点重叠的部分,本篇文章将不再赘述. 1.ContextImpl到ActivityManageService的调用过程 我们可以用bindService方法来绑定Service,它的实现在ContextWra

Android四大组件之一Service介绍-android学习之旅(十二)

基本概念: service是android四大组件之一,运行在后台执行耗时操作,并不提供用户界面.其他组件如acticity可以通过startService启动该组件,也可以通过bindService启动并把绑定该组件进行通信. 使用场景 后台下载文件,以及播放音乐等 注意 service运行在主线程中,他不会创建属于自己的线程,也不是运行在独立的线程中,所以在使用的时候,需要自己创建线程,而不应该直接使用,这样会造成ANR错误. service的两种形式 started service 其他组