service生命周期

Service主要包含本地类和远程类。

Service不是Thread,Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。

通信方式有很多但是需要根据需求去选择合适我们的:

目前Android 组件之间主流的通信方式如上所述的5种,哪种对于项目更加合适,需要考虑的适用情况有:

  1. 进程内通信还是进程间通信
  2. 一对一通信,还是一对多
  3. 单向还是双向
  4. 性能
  5. 其他(安全性,代码可读性,实现复杂性)等等;

Activity和Service之间的通信方式

1.1 使用扩展Binder类(本例从activity向service传送数据)

使用场景:如果你的服务仅供本地应用使用,不需要跨进程工作,则可以实现扩展Binder 类,让你的客户端通过该类直接访问服务中的公共方法。此方法只有在客户端和服务位于同一应用和进程内这一最常见的情况下方才有效。例如,对于需要将 Activity 绑定到在后台播放音乐的自有服务的音乐应用,此方法非常有效。

通信思路:

1、在Service中创建一个Binder实例,然后再onBind()方法中返回这个实例。

2、在客户端中使用bindService从 onServiceConnected() 回调方法接收Binder实例,这个Binder实例就是Service的回调方法onBind()方法返回的Binder,然后使用这个Binder提供的方法调用绑定服务,得到Service实例。

3、然后就可以使用这个Service实例来调用其方法,这样就可以对Sevice服务端进行操作。

4、如果Service端想要给客户端返回数据咋办呢?这个简单,既然已经获得了Service实例,那么就可以在Service当中设置一个接口,然后在客户端实现这个接口,这样Service端就可以通过这个接口给客户端发送消息了!

MainService.java

 public class MainService extends Service{

     private String TAG = "MainService";
public ServiceBinder mBinder = new ServiceBinder(); /* 数据通信的桥梁 */ /* 重写Binder的onBind函数,返回派生类 */
@Override
public IBinder onBind(Intent arg0) {
return mBinder;
} @Override
public void onCreate() {
Toast.makeText( MainService.this, "Service Create...", Toast.LENGTH_SHORT).show();
super.onCreate();
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(MainService.this, "Service StartCommand", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent, flags, startId); //在这里可以接受从activity传递过来的数据
} @Override
public void onDestroy() {
Toast.makeText( MainService.this, "Service Destroy", Toast.LENGTH_SHORT).show();
} /* 第一种模式通信:Binder */
public class ServiceBinder extends Binder { public void startDownload() throws InterruptedException {
/* 模拟下载,休眠2秒 */
Toast.makeText( MainService.this, "模拟下载2秒钟,开始下载...", Toast.LENGTH_SHORT).show();
Thread.sleep(2);
Toast.makeText( MainService.this, "下载结束...", Toast.LENGTH_SHORT).show();
}
}
}

MainActivity.java

 public class MainActivity extends Activity {
/* 通过Binder,实现Activity与Service通信 */
private MainService.ServiceBinder mBinderService;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
} @Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinderService = (MainService.ServiceBinder) service;
try {
mBinderService.startDownload();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}; @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); Intent bindIntent = new Intent(MainActivity.this, MainService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE); //在Intent中设置bundle,可以在这里传递数据
}
}

下面我们又有新的需求:目前上面的代码可以将数据从MainActivity.java传递到Service端,通过Intent的传参或者设置Bundle传参。但是我们如果需要从Service中获取数据该怎么办呢?

1.2 使用扩展Binder类(本例从service向activity传送数据--利用回调)

如果是我们需要从服务中返回一些数据到Activity中的时候,实现起来就有各种各样的方法,比如说使用回调,使用广播等等,今天说的是使用回调的方法。下面的这个例子摘自:http://www.cnblogs.com/Fndroid/p/5187444.html 理解这个例子之前还要好好体会回调函数的作用:

先上服务端的代码,MyService.java

 public class MyService extends Service {
private boolean connecting = false;
private Callback callback; @Nullable
@Override
public IBinder onBind(Intent intent) {
return new Binder();
} public class Binder extends android.os.Binder {
public MyService getService() {
return MyService.this;
}
} @Override
public void onCreate() {
super.onCreate();
connecting = true;
new Thread(new Runnable() { @Override
public void run() {
int i = 0;
while (connecting == true) {
i++;
if (callback != null) {
callback.onDataChange(i + "");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
} public void setCallback(Callback callback) {
this.callback = callback;
} public static interface Callback {
void onDataChange(String data);
} @Override
public void onDestroy() {
super.onDestroy();
connecting = false;
}
}

这段代码和上面的代码没有任何区别,只是添加了回调接口,

public static interface Callback {
void onDataChange(String data);
}

在线程中,回调这个接口的函数,这个回调函数将会返回数据给,MainActivity,并在MainActivity中处理这些从service端传递过来的数据。

@Override
24 public void run() {
25 int i = 0;
26 while (connecting == true) {
27 i++;
28 if (callback != null) {
29 callback.onDataChange(i + "");
30 }
31 try {
32 Thread.sleep(1000);
33 } catch (InterruptedException e) {
34 e.printStackTrace();
35 }
36 }
37 }
38 }).start();

MainActivity.java:

 public class MainActivity extends AppCompatActivity implements View.OnClickListener,
ServiceConnection { private TextView tvOut; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvOut = (TextView) findViewById(R.id.tvOut);
findViewById(R.id.btnBindService).setOnClickListener(this);
} @Override
public void onClick(View v) {
bindService(new Intent(this, MyService.class), this, BIND_AUTO_CREATE);
} @Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyService.Binder binder = (MyService.Binder) service;
MyService myService = binder.getService();
myService.setCallback(new MyService.Callback() {
@Override
public void onDataChange(String data) {
Message msg = new Message();
msg.obj = data;
handler.sendMessage(msg);
}
});
} @Override
public void onServiceDisconnected(ComponentName name) { } private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
tvOut.setText(msg.obj.toString());
}
};
}

注意:开启的服务是在主线程中运行的,如果在服务中开启了线程,那么在子线程中就不能直接更新UI在这里面无法直接更新界面,因为开启了线程,需要使用Handler机制。

在服务中的onCreate方法中,我们打开了一个线程来模拟服务的运行,并在线程每隔1s中给私有变量i赋值递增,然后我们编写了一个公有的接口Callback,并且定义了一个该接口的私有成员,并且在onCreate方法中调用了接口里面的函数onDataChange。接下来我们自定义了一个Binder的子类并在这个类中定义了函数返回当前的这个Service,这里的目的就是在Activity中可以访问到这个Service的回调接口Callback并实现该接口的方法。

因为服务绑定后,会从onBind方法中返回一个Binder对象,这个对象会在onServiceConnectde方法中获取到,所以我们先从Binder对象中获取到我们从服务传递过来的MyService对象,然后调用MyService对象的setCallback方法来设置我们需要的处理逻辑,这里是把i的值打印出来,因为服务中开启了线程,所以这里也不能直接更新UI。

总结:回调机制是Java中的一个重要特性,在Android中使用到的地方很广泛,例如我们给按钮设定点击事件等。这里的回调,其实是通过在发送端定义回调接口,并且调用接口的回调方法,然后在接收端实现该接口的方法。只要接口被调用了,就会回调接收端的被实现了的方法,这样数据就能传递过来。

2. Handler+Binder VS Handler+Message

主要思路:通常我们使用Handler+Message的方式进行通信,都是在同一个进程中,子线程持有一个主线程的Handler对象,并向主线程发送消息。而Android既然可以使用bindler机制进行跨进行通信,所以我们当然可以将Handler与bindler结合起来进行跨进程发送消息。查看API就可以发现,Messenger就是这种方式的实现。具体的使用请看下面的Messenger通信。

3. 广播 (推荐LocalBroadcastManager)

主要思路:

需要在MainActivity中注册能够接收Service发送的数据更新广播:com.spt.activity.CountService;并获取到更新后的数据,显示即可。还需要注意的是:开启服务后,还要停止服务。否则该服务会一直在后台运行。  还发现,使用这种方式同样能够停止该Service:stopService(new Intent(MainActivity.this, CountService.class));

MainActivity.java

 /**
* <功能描述> Service和Activity之间的数据交互;具体表现为: 1. 从Service获取数据源,传递到Activity中; 2.
* 在Activity中作数据更新; 3. Service中的onCreate()在UI线程中执行,作延时需要在子线程中执行;
*
* @author Administrator
*/
public class MainActivity extends Activity {
private static final String TAG = "Demo";
private TextView mTvContent;
private Intent mIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initView();
registerReceiver();
initData();
}
/**
* <功能描述> 初始化视图
*
* @return void [返回类型说明]
*/
private void initView() {
setContentView(R.layout.activity_main);
mTvContent = (TextView) findViewById(R.id.tv_data);
}
/**
* <功能描述> 初始化数据
*
* @return void [返回类型说明]
*/
private void initData() {
mIntent = new Intent(MainActivity.this, CountService.class);
startService(mIntent);
}
/**
* <功能描述> 注册广播
*
* @return void [返回类型说明]
*/
private void registerReceiver() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.spt.activity.CountService");
MainActivity.this.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
mTvContent.setText(bundle
.getInt("com.spt.CountService.count_data") + "");
Log.d(TAG, "count_data=" + mTvContent.getText());
}
}, intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (true) {
stopService(mIntent);
}
}
}

CountService.java

 public class CountService extends Service {
private int countService = 0;
private boolean isServiceRunning = false;
@Override
public void onCreate() {
super.onCreate();
isServiceRunning = true;
// 创建子线程作计数
new Thread(new Runnable() {
@Override
public void run() {
while (isServiceRunning) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
countService++;
Intent intent = new Intent();
intent.putExtra("com.spt.CountService.count_data", countService);
intent.setAction("com.spt.activity.CountService");
// Service中发送广播
sendBroadcast(intent);
}
}
}).start();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
isServiceRunning = false;
countService = 0;
}
}

总结和疑问:

1. 使用Broadcast广播方式实现服务和宿主之间的数据交互,容易造成性能不高的问题;

2. 广播发送的时间不确定性因素,导致数据交互有延时;

4. Messenger

主要思路:先贴上完整的服务端和客户端通信的代码,再做分析。

(1)Server端

import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException; public class MessengerService extends Service
{ private static final int MSG_SUM = 0x110; //最好换成HandlerThread的形式
private Messenger mMessenger = new Messenger(new Handler()
{
@Override
public void handleMessage(Message msgfromClient)
{
Message msgToClient = Message.obtain(msgfromClient);//返回给客户端的消息
switch (msgfromClient.what)
{
//msg 客户端传来的消息
case MSG_SUM:
msgToClient.what = MSG_SUM;
try
{
//模拟耗时
Thread.sleep(2000);
msgToClient.arg2 = msgfromClient.arg1 + msgfromClient.arg2;
msgfromClient.replyTo.send(msgToClient);
} catch (InterruptedException e)
{
e.printStackTrace();
} catch (RemoteException e)
{
e.printStackTrace();
}
break;
} super.handleMessage(msgfromClient);
}
}); @Override
public IBinder onBind(Intent intent)
{
return mMessenger.getBinder();
}
}

服务端就一个Service,可以看到代码相当的简单,只需要去声明一个Messenger信使对象,然后onBind方法返回mMessenger.getBinder();然后坐等客户端将消息发送到handleMessage想法,根据message.what去判断进行什么操作,然后做对应的操作,最终将结果通过 msgfromClient.replyTo.send(msgToClient);返回。

(2)Client 客户端

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView; public class MainActivity extends AppCompatActivity
{
private static final String TAG = "MainActivity";
private static final int MSG_SUM = 0x110; private Button mBtnAdd;
private LinearLayout mLyContainer;
//显示连接状态
private TextView mTvState; private Messenger mService;
private boolean isConn; private Messenger mMessenger = new Messenger(new Handler()
{
@Override
public void handleMessage(Message msgFromServer)
{
switch (msgFromServer.what)
{
case MSG_SUM:
TextView tv = (TextView) mLyContainer.findViewById(msgFromServer.arg1);
tv.setText(tv.getText() + "=>" + msgFromServer.arg2);
break;
}
super.handleMessage(msgFromServer);
}
}); private ServiceConnection mConn = new ServiceConnection()
{
@Override
public void onServiceConnected(ComponentName name, IBinder service) //返回的binder对象,下面通过这个对象创建远程信使
{
mService = new Messenger(service);
isConn = true;
mTvState.setText("connected!");
} @Override
public void onServiceDisconnected(ComponentName name)
{
mService = null;
isConn = false;
mTvState.setText("disconnected!");
}
}; private int mA; @Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); //开始绑定服务
bindServiceInvoked(); mTvState = (TextView) findViewById(R.id.id_tv_callback);
mBtnAdd = (Button) findViewById(R.id.id_btn_add);
mLyContainer = (LinearLayout) findViewById(R.id.id_ll_container); mBtnAdd.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
try
{
int a = mA++;
int b = (int) (Math.random() * 100); //创建一个tv,添加到LinearLayout中
TextView tv = new TextView(MainActivity.this);
tv.setText(a + " + " + b + " = caculating ...");
tv.setId(a);
mLyContainer.addView(tv); Message msgFromClient = Message.obtain(null, MSG_SUM, a, b);
msgFromClient.replyTo = mMessenger;
if (isConn)
{
//往服务端发送消息
mService.send(msgFromClient);
}
} catch (RemoteException e)
{
e.printStackTrace();
}
}
}); } private void bindServiceInvoked()
{
Intent intent = new Intent();
intent.setAction("com.zhy.aidl.calc");
bindService(intent, mConn, Context.BIND_AUTO_CREATE);
Log.e(TAG, "bindService invoked !");
} @Override
protected void onDestroy()
{
super.onDestroy();
unbindService(mConn);
} }

代码也不复杂,首先bindService,然后在onServiceConnected中拿到回调的service(IBinder)对象,通过service对象去构造一个mService =new Messenger(service);然后就可以使用mService.send(msg)给服务端了。那么服务端会收到消息,处理完成会将结果返回,传到Client端的mMessenger中的Handler的handleMessage方法中。

(3)注册service,以及布局文件

<service
android:name=".MessengerService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.zhy.aidl.calc"></action>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
<LinearLayout android:id="@+id/id_ll_container"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity"> <TextView
android:id="@+id/id_tv_callback"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Messenger Test!"/> <Button android:id="@+id/id_btn_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="add"/> </LinearLayout>

上面的代码摘抄自:http://blog.csdn.net/lmj623565791/article/details/47017485#quote

消息传递流程分析:

service创建一个信使对象mMessenger,然后通过onBind()返回给client,client通过获取到的IBinder对象创建客户端的信使对象mService,然后利用mService.send(msg)就可以给服务器端发送消息了。服务器端坐等客户端将消息发送到handleMessage()接收从客户端发送过来的消息,根据message.what去判断进行什么操作,然后做对应的操作,最终将结果通过 msgfromClient.replyTo.send(msgToClient)将信息返回给客户端,最终完成了service和client之间的双向通信。

1.服务端通过

mMessenger = new Messenger(mHandler)

创建一个信使对象。

2.服务端通过onBinde()方法返回一个binder

return mMessenger.getBinder(); 

3.客户端使用bindService(intent, mConn, Context.BIND_AUTO_CREATE)请求远程连链接;通过远程链接返回的binder得到一个信使(远程信使)对象

public void onServiceConnected(ComponentName name, IBinder service) {
rMessenger = new Messenger(service);
     ......
}

4.客户端可以使用这个远程信使对象向远程发送消息:rMessenger.send(msg); 这样远程服务端的Handler对象就能收到消息了,然后可以在其handlerMessage(Message msg)方法中进行处理。(该Handler对象就是第一步服务端创建Messenger时使用的参数mHandler)。

5.经过这5个步骤貌似只有客户端向服务端发送消息,这样的消息传递是单向的,那么如何实现双向传递呢? 首先需要在第5步稍加修改,在send(msg)前通过msm.replyTo = mMessenger将自己的信使设置到消息中,这样服务端接收到消息时同时也得到了客户端的信使对象了,然后服务端可以通过send()完成从服务端向客户端发送消息的功能,这样客服端可以在自己的Handler对象的handlerMessage方法中接收服务端发送来的message进行处理。 双向通信宣告完成。

总的来说就是创建信使,信使传递信息

5. 开源组件(EventBus,otto)

主要思路:利用反射或者注释的方式实现对注册类的注册,然后遍历当前的注册表,通过key进行查询,然后dispatch,调用相应的事件处理方法。(不同的开源框架实现有所区别)

6. AIDL

主要思路://TODO

201709012工作日记--activity与service的通信机制的更多相关文章

  1. Android学习总结(四)—— Activity和 Service进行通信

    一.Activity 和 Service进行通信的基本概念 前面我们学习我生命周期里面包含了启动和停止服务的方法,虽然服务器在活动里启动,但在启动了服务之后,活动与服务基本就没有什么关系了.我们在活动 ...

  2. 201709012工作日记--Android消息机制

    1. android的消息机制——Handler机制 参考:http://www.jianshu.com/p/9e4d1fab0f36. Android异步消息处理机制完全解析,带你从源码的角度理解: ...

  3. 201709012工作日记--一台电脑创建两个Github账户上传代码

    1. 在一台主机上面使用多个GitHub账号 有时候,我们需要将个人账号和公司账号区分,这时候我们就会需要在一台电脑上使用2个不同的git账号. 2. 上传文件 http://blog.csdn.ne ...

  4. 启动activity与使用Intent通信机制解析

    我们都知道,一个activity启动另一个activity最简单的方式就是使用startActivity方法: public void startActivity (Intent intent) 但是 ...

  5. Android中Activity、Service和线程之间的通信

    Activity.Service和线程应该是Android编程中最常见的几种类了,几乎大多数应用程序都会涉及到这几个类的编程,自然而然的,也就会涉及到三者之间的相互通信,本文就试图简单地介绍一下这三者 ...

  6. Activity与Service通信(不同进程之间)

    使用Messenger 上面的方法只能在同一个进程里才能用,如果要与另外一个进程的Service进行通信,则可以用Messenger. 其实实现IPC(Inter-Process Communicat ...

  7. Activity与Service通信的方式有三种:

    在博客园看到的,看着挺不错的,借来分享下 继承Binder类 这个方式仅仅有当你的Acitivity和Service处于同一个Application和进程时,才干够用,比方你后台有一个播放背景音乐的S ...

  8. activity 与 service 之间的通信

    activity和service通信:通过binder 举个我实际项目中的例子:在service中下载更新应用 首先是下载更新apk的service: public class UpdateVersi ...

  9. Activity与Service通信

    Activity与Service通信的方式有三种: 继承Binder类 这个方式只有当你的Acitivity和Service处于同一个Application和进程时,才可以用,比如你后台有一个播放背景 ...

随机推荐

  1. neo4j 学习-2

    Neo4j 查询例句 MATCH (john {name: 'John'})-[:friend]->()-[:friend]->(fof) RETURN john.name, fof.na ...

  2. JDK、JRE和JAR区别(转载)

    JDK里面的工具也是用Java编写的,它们本身运行的时候也需要一套JRE,如C:/Program Files/Java/jdk1.5.x/目录下的JRE.而C:/Program Files/Java/ ...

  3. Group by 内部排序

    1.right join #  update_time  gid=>sid, group_status => s_table select a.* from comment as a ri ...

  4. SQLserver和oracle中对应的数据类型

  5. ubuntu16.04安装virtualbox

    download:download.virtualbox.org/virtualbox/5.0.10/virtualbox-5.0_5.0.10-104061~Ubuntu~trusty_amd64. ...

  6. tf.unstack()、tf.stack()

    tf.unstack 原型: unstack( value, num=None, axis=0, name='unstack' ) 官方解释:https://tensorflow.google.cn/ ...

  7. ECMAScript6新特性之Reflect

    一 Reflect.ownKeys()获取对象属性. 可枚举的.不可枚举的.自有的.继承的. let fruit = { '2' : 'mango', [Symbol.for('pink')] : ' ...

  8. actionBar_Tab导航

    actionBar配合碎片使用  初始化actionBar要注意设置actionbar的导航模式 package com.qf.actionbar04_tab; import java.io.File ...

  9. 201621123008 《Java程序设计》 第三周学习总结

    1. 本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词,如类.对象.封装等 关键词:类,构造函数,方法重载,方法覆盖,封装,继承,多态,类被加载的过程,static,abstract, ...

  10. 优秀UX设计师的八条黄金法则

    与用户保持亲密   成为成功的UX设计师最重要的先决条件之一就是与用户保持紧密的联系,以发现和了解他们的需求和爱好.理想情况下你应该让自己完全地成为产品用户,因为只有这样你才能理解背后的动机.“这样的 ...