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那篇里说明了。
4.binder机制
关于这块之前以及有相关博文,接下来打算再详细分析下。binder机制是android最重要的基石。
server 会在通过servermanger注册它,然后提供远程调用的句柄,通过binder机制
client获取servermanger不需要通过binder,应为servermanger是默认的句柄为0,可以直接获取到。
所以说,servermanager是在等待client端发送请求,然后它去寻找以及注册的server,得到它的远程对象,进行通信。
Android 四大组件之再论service的更多相关文章
- Android 四大组件之再论BroadCast
BroadCast 是android提供的跨进程通讯的有一利器. 1.异步执行onReceiver @Nullable public abstract Intent registerReceiver( ...
- 【Android 界面效果16】关于android四大组件的总结
Android四大组件:Activity.Service.Broadcast receiver.Content provider 在Android中,一个应用程序可以使用其它应用程序的组件,这是And ...
- Android四大组件简介:Android 基础知识,开发教程
Android 四大组件: Activity.Service.Broadcast Receiver.Content Provider. http://developer.android.com/int ...
- Android四大组件之Service
Android四大组件之Service Android支持服务的概念,服务是在后台运行的组件,没有用户界面,Android服务可用有与活动独立的生命周期.Android支持两种类型的服务: 本地服务: ...
- Android 四大组件之service与Broadcast
Android 四大组件之一:service: Service有五个生命周期:onCreat,onStartCommand, onBind,onUnbind, onDestroy 主要有绑定和非绑定两 ...
- Android四大组件——Service
Service相关链接 Service初涉 Service进阶 Service精通 Service是Android系统中的一种组件,它跟Activity的级别差不多,但是它不能自己运行,只能后台运行, ...
- Android四大组件之一 -- Service详解
相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的Android程序员如果连Service都没听说过的话,那确实也太逊了.Service作为Android四大组件之一,在每一个应用程序 ...
- 【Android开发日记】之入门篇(五)——Android四大组件之Service
这几天忙着驾校考试,连电脑都碰不到了,今天总算告一段落了~~Service作为Android的服务组件,默默地在后台为整个程序服务,辅助应用与系统中的其他组件或系统服务进行沟通.它跟Activity的 ...
- Android四大组件:Service
前言 Service作为Android四大组件之一,应用非常广泛 本文将介绍对Service进行全面介绍(基础认识.生命周期.使用和应用场景) 目录 目录 1. 基础知识 定义:服务,属于Androi ...
随机推荐
- PHP代码的执行
先看下PHP的结构图:
- 重温JSP学习笔记--三大指令九大内置对象
最近在温习javaweb的相关基础知识,鉴于我弄丢了记满了整整一本的笔记,决定以后把笔记和一些学习上的心得以及碰到的一些问题统统都放在网上,今天看了一下jsp的相关基础,以下是笔记: JSP三大指令: ...
- Cesium原理篇:3最长的一帧之地形(4:重采样)
地形部分的原理介绍的差不多了,但之前还有一个刻意忽略的地方,就是地形的重采样.通俗的讲,如果当前Tile没有地形数据的话,则会从他父类的地形数据中取它所对应的四分之一的地形数据.打个比方 ...
- (十)WebGIS中地理坐标与屏幕坐标间的转换原理
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.前言 地图本身是拥有坐标的,一般可以大致分为平面坐标和经纬度坐标, ...
- 3.Code-First 约定(EF Code-First系列)
前面,我们已经了解了Code-First利用领域类,怎么为我们创建数据库的简单示例.现在我们来学习一下Code-First约定吧. 什么是约定 约定说白了,就是基于一套规矩办事,这里就是基于你定义好的 ...
- CSS基础之居中显示
这些天忙完了一些项目后,终于有时间来总结一下了.自己就把做项目过程中的体会和理解到的一些东西和大家分享一下.有错请指正!! 在css中,元素居中显示,是基础也是一个小难点.我们经常不知为何总是不能把元 ...
- Hibernate插入数据后获得ID
很多表的主键都是自增型的,新增的记录使用save()方法保存以后,要获得ID,直接使用getId()就可以了,因为此时记录已经保存进数据库,已经有了ID. 另一种方法是使用MySQL的SELECT L ...
- 使用SwipeListView实现滑动效果
QQ的滑动删除效果很不错,要实现这种效果,可以使用SwipeListView.1. 下载com.fortysevendeg.swipelistview这个项目(以前GitHub上有,现在GitHub上 ...
- 如何寻找“真爱”型合伙人
曾与朋友笑侃,现在找人结婚,跟合伙开公司差不多,各自条件一一对比,细细斟酌,最后双方达成一致,才得凑成一对冤家.谁说不是呢?两种关系都实为"伙伴",开公司重" ...
- GJM : 数据结构学习笔记
--------------------------数据结构 --------------------数据结构分 线性数据结构给非线性数据结构 数据和结合 线性表(顺序存储方式)特点:有且仅有一个开始 ...