你所不知道的Service

service常见的有2种方式,本地service以及remote service。
这2种的生命周期,同activity的通信方式等,都不相同。
关于这2种service如何使用,这里不做介绍,只是介绍一些被遗漏的地方

1.远程Service(AIDL方式)

ServiceConActivity:

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(getApplicationContext(),ServiceSamples.class);//        intent.setAction("com.joyfulmath.service.samples");        intent.putExtra("cookie","origin");        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();    }}

ServiceSamples

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<>();    private String tag = "";    @Nullable    @Override    public IBinder onBind(Intent intent) {        tag = intent.getStringExtra("cookie");        TraceLog.i(tag);        return samplesBinder;    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        tag = intent.getStringExtra("cookie");        TraceLog.i(tag);        return super.onStartCommand(intent, flags, startId);    }    @Override    public boolean onUnbind(Intent intent) {        TraceLog.i(tag);        return super.onUnbind(intent);    }    @Override    public void onDestroy() {        super.onDestroy();        TraceLog.i(tag);        mCallbacks.kill();    }    @Override    public void onCreate() {        super.onCreate();        samplesBinder = new SamplesBinder();        TraceLog.i(tag);    }    public class SamplesBinder extends ISamplesAidlInterface.Stub{        @Override        public int doBackground(String action) throws RemoteException {            TraceLog.i(tag);            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(tag+":"+e.getMessage());        }    }}

这是简单的service & activity交互的代码。
在看关键的AIDL代码:

// ISamplesAidlInterface.aidlpackage com.joyfulmath.samples.basecontrol;import com.joyfulmath.samples.basecontrol.PersonCall;import com.joyfulmath.samples.basecontrol.ICallBack;// Declare any non-default types here with import statementsinterface ISamplesAidlInterface { int doBackground(in String action); void findPerson(in PersonCall p); void registerCallBack(ICallBack cb); void unRegisterCallBack(ICallBack cb);}

// PersonCall.aidlpackage com.joyfulmath.samples.basecontrol;parcelable PersonCall;

// ICallBack.aidlpackage com.joyfulmath.samples.basecontrol;// Declare any non-default types here with import statementsinterface ICallBack { void onServiceStateChanged(int s);}

这里有3个问题,我们从头往下看,就能明白。
1)为什么在其他APK调用该service的时候,aidl的文件包必须一致
2)为什么要自定义PersonCall.aidl

  1. 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,得到它的远程对象,进行通信。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容