本地/远程Service 和Activity 的交方式(转)

android SDK提供了Service,用于类似*nix守护进程或者windows的服务。

Service有两种类型:

  1. 本地服务(Local Service):用于应用程序内部
  2. 远程服务(Remote Sercie):用于android系统内部的应用程序之间

前者用于实现应用程序自己的一些耗时任务,比如查询升级信息,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验比较好。

后者可被其他应用程序复用,比如天气预报服务,其他应用程序不需要再写这样的服务,调用已有的即可。

编写不需和Activity交互的本地服务示例

本地服务编写比较简单。首先,要创建一个Service类,该类继承android的Service类。这里写了一个计数服务的类,每秒钟为计数器加一。在服务类的内部,还创建了一个线程,用于实现后台执行上述业务逻辑。

原文转自(含原码):http://yangguangfu.iteye.com/blog/699306

package com.easymorse;

import
android.app.Service;
import
android.content.Intent;
import
android.os.IBinder;
import
android.util.Log;

public

class
CountService
extends
Service {

private

boolean
threadDisable;

private

int
count;

@Override
   
public
IBinder onBind(Intent intent) {
       
return

null
;
    }

@Override
   
public

void
onCreate() {
       
super
.onCreate();
       
new
Thread(
new
Runnable() {

@Override
           
public

void
run() {
               
while
(
!
threadDisable) {
                   
try
{
                        Thread.sleep(
1000
);
                    }
catch
(InterruptedException e) {
                    }
                    count
++
;
                    Log.v(
"
CountService
"
,
"
Count is
"

+
count);
                }
            }
        }).start();
    }

@Override
   
public

void
onDestroy() {
       
super
.onDestroy();
       
this
.threadDisable
=

true
;
        Log.v(
"
CountService
"
,
"
on destroy
"
);
    }

public

int
getCount() {
       
return
count;
    }

}

需要将该服务注册到配置文件AndroidManifest.xml中,否则无法找到:

<?
xml version="1.0" encoding="utf-8"
?>

<
manifest
xmlns:android
="http://schemas.android.com/apk/res/android"

package
="com.easymorse"
android:versionCode
="1"
android:versionName
="1.0"
>

<
application
android:icon
="@drawable/icon"
android:label
="@string/app_name"
>

<
activity
android:name
=".LocalServiceDemoActivity"

android:label
="@string/app_name"
>

<
intent-filter
>

<
action
android:name
="android.intent.action.MAIN"

/>

<
category
android:name
="android.intent.category.LAUNCHER"

/>

</
intent-filter
>

</
activity
>

<
service
android:name
="CountService"

/>

</
application
>

<
uses-sdk
android:minSdkVersion
="3"

/>

</
manifest/>

在Activity中启动和关闭本地服务。

package
com.easymorse;

import
android.app.Activity;
import
android.content.Intent;
import
android.os.Bundle;

public

class
LocalServiceDemoActivity
extends
Activity {
   
/**
Called when the activity is first created.
*/

@Override
   
public

void
onCreate(Bundle savedInstanceState) {
       
super
.onCreate(savedInstanceState);
        setContentView(R.layout.main);

this
.startService(
new
Intent(
this
, CountService.
class
));
    }

@Override
   
protected

void
onDestroy() {
       
super
.onDestroy();
       
this
.stopService(
new
Intent(
this
, CountService.
class
));
    }
}

可通过日志查看到后台线程打印的计数内容。

编写本地服务和Activity交互的示例

上面的示例是通过startService和stopService启动关闭服务的。适用于服务和activity之间没有调用交互的情况。如果之间需要传递参数或者方法调用。需要使用bind和unbind方法。

具体做法是,服务类需要增加接口,比如ICountService,另外,服务类需要有一个内部类,这样可以方便访问外部类的封装数据,这个内部类
需要继承Binder类并实现ICountService接口。还有,就是要实现Service的onBind方法,不能只传回一个null了。

这是新建立的接口代码:

package
com.easymorse;

public

interface
ICountService {
   
public

abstract

int
getCount();
}

修改后的CountService代码:

package
com.easymorse;

import
android.app.Service;
import
android.content.Intent;
import
android.os.Binder;
import
android.os.IBinder;
import
android.util.Log;

public

class
CountService
extends
Service
implements
ICountService {

private

boolean
threadDisable;

private

int
count;

private
ServiceBinder serviceBinder
=
new
ServiceBinder();

public

class
ServiceBinder
extends
Binder
implements
ICountService{
        @Override
       
public

int
getCount() {
           
return
count;

}

}

@Override

public
IBinder onBind(Intent intent) {
       
return
serviceBinder;
    }

@Override
   
public

void
onCreate() {
       
super
.onCreate();
       
new
Thread(
new
Runnable() {

@Override
           
public

void
run() {
               
while
(
!
threadDisable) {
                   
try
{
                        Thread.sleep(
1000
);
                    }
catch
(InterruptedException e) {
                    }
                    count
++
;
                    Log.v(
"
CountService
"
,
"
Count is
"

+
count);
                }
            }
        }).start();
    }

@Override
   
public

void
onDestroy() {
       
super
.onDestroy();
       
this
.threadDisable
=

true
;
        Log.v(
"
CountService
"
,
"
on destroy
"
);
    }

/*
(non-Javadoc)
     * @see com.easymorse.ICountService#getCount()
    
*/

public

int
getCount() {
       
return
count;
    }

}

服务的注册也要做改动,AndroidManifest.xml文件:

<?
xml version="1.0" encoding="utf-8"
?>

<
manifest
xmlns:android
="http://schemas.android.com/apk/res/android"

package
="com.easymorse"
android:versionCode
="1"
android:versionName
="1.0"
>

<
application
android:icon
="@drawable/icon"
android:label
="@string/app_name"
>

<
activity
android:name
=".LocalServiceDemoActivity"

android:label
="@string/app_name"
>

<
intent-filter
>

<
action
android:name
="android.intent.action.MAIN"

/>

<
category
android:name
="android.intent.category.LAUNCHER"

/>

</
intent-filter
>

</
activity
>

<
service
android:name
="CountService"
>

<
intent-filter
>

<
action
android:name
="com.easymorse.CountService"
/>

</
intent-filter
>

</
service
>

</
application
>

<
uses-sdk
android:minSdkVersion
="3"

/>

</
manifest
>

Acitity代码不再通过startSerivce和stopService启动关闭服务,另外,需要通过ServiceConnection的内部类实现来连接Service和Activity。

package
com.easymorse;

import
android.app.Activity;
import
android.content.ComponentName;
import
android.content.Intent;
import
android.content.ServiceConnection;
import
android.os.Bundle;
import
android.os.IBinder;
import
android.util.Log;

public

class
LocalServiceDemoActivity
extends
Activity {

private
ServiceConnection serviceConnection
=

new
ServiceConnection() {

@Override
       
public

void
onServiceConnected(ComponentName name, IBinder service) {
            countService
=
(ICountService) service;
            Log.v(
"
CountService
"
,
"
on serivce connected, count is
"

+
countService.getCount());
        }

@Override
       
public

void
onServiceDisconnected(ComponentName name) {
            countService
=

null
;
        }

};

private
ICountService countService;

/**
Called when the activity is first created.
*/

@Override
   
public

void
onCreate(Bundle savedInstanceState) {
       
super
.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
this
.bindService(
new
Intent(
"
com.easymorse.CountService
"
),
               
this
.serviceConnection, BIND_AUTO_CREATE);
    }

@Override
   
protected

void
onDestroy() {

this
.unbindService(serviceConnection);

super
.onDestroy();       //注意先后

}
}

编写传递基本型数据的远程服务

上面的示例,可以扩展为,让其他应用程序复用该服务。这样的服务叫远程(remote)服务,实际上是进程间通信(RPC)。

这时需要使用android接口描述语言(AIDL)来定义远程服务的接口,而不是上述那样简单的java接口。扩展名为aidl而不是java。
可用上面的ICountService改动而成ICountSerivde.aidl,eclipse会自动生成相关的java文件。

package com.easymorse;

interface
ICountService {
   
int
getCount();
}

编写服务(Service)类,稍有差别,主要在binder是通过远程获得的,需要通过桩(Stub)来获取。桩对象是远程对象的本地代理。

package
com.easymorse;

import
android.app.Service;
import
android.content.Intent;
import
android.os.IBinder;
import
android.os.RemoteException;
import
android.util.Log;

public

class
CountService
extends
Service {

private

boolean
threadDisable;

private

int
count;

private
ICountService.Stub serviceBinder
=

new
ICountService.Stub() {

@Override
       
public

int
getCount()
throws
RemoteException {
           
return
count;
        }
    };

@Override
   
public
IBinder onBind(Intent intent) {
       
return
serviceBinder;
    }

@Override
   
public

void
onCreate() {
       
super
.onCreate();
       
new
Thread(
new
Runnable() {

@Override
           
public

void
run() {
               
while
(
!
threadDisable) {
                   
try
{
                        Thread.sleep(
1000
);
                    }
catch
(InterruptedException e) {
                    }
                    count
++
;
                    Log.v(
"
CountService
"
,
"
Count is
"

+
count);
                }
            }
        }).start();
    }

@Override
   
public

void
onDestroy() {
       
super
.onDestroy();
       
this
.threadDisable
=

true
;
        Log.v(
"
CountService
"
,
"
on destroy
"
);
    }
}

配置文件AndroidManifest.xml和上面的类似,没有区别。

在Activity中使用服务的差别不大,只需要对ServiceConnection中的调用远程服务的方法时,要捕获异常。

private
ServiceConnection serviceConnection
=

new
ServiceConnection() {

@Override
   
public

void
onServiceConnected(ComponentName name, IBinder service) {
        countService
=
(ICountService) service;
       
try
{
            Log.v(
"
CountService
"
,
"
on serivce connected, count is
"

+
countService.getCount());
        }
catch
(RemoteException e) {
           
throw

new
RuntimeException(e);
        }
    }

@Override
   
public

void
onServiceDisconnected(ComponentName name) {
        countService
=

null
;
    }

};

这样就可以在同一个应用程序中使用远程服务的方式和自己定义的服务交互了。

如果是另外的应用程序使用远程服务,需要做的是复制上面的aidl文件和相应的包构到应用程序中,其他调用等都一样。

编写传递复杂数据类型的远程服务

远程服务往往不只是传递java基本数据类型。这时需要注意android的一些限制和规定:

  1. android支持String和CharSequence
  2. 如果需要在aidl中使用其他aidl接口类型,需要import,即使是在相同包结构下;
  3. android允许传递实现Parcelable接口的类,需要import;
  4. android支持集合接口类型List和Map,但是有一些限制,元素必须是基本型或者上述三种情况,不需要import集合接口类,但是需要对元素涉及到的类型import;
  5. 非基本数据类型,也不是String和CharSequence类型的,需要有方向指示,包括in、out和inout,in表示由客户端设置,out表示由服务端设置,inout是两者均可设置。

这里将前面的例子中返回的int数据改为复杂数据类型:

package
com.easymorse;

import
android.os.Parcel;
import
android.os.Parcelable;

public

class
CountBean
implements
Parcelable {

public

static

final
Parcelable.Creator
<
CountBean
>
CREATOR
=

new
Creator
<
CountBean
>
() {

@Override
       
public
CountBean createFromParcel(Parcel source) {
            CountBean bean
=

new
CountBean();
            bean.count
=
source.readInt();
           
return
bean;
        }

@Override
       
public
CountBean[] newArray(
int
size) {
           
return

new
CountBean[size];
        }

};

public

int
count;

@Override
   
public

void
writeToParcel(Parcel dest,
int
flags) {
        dest.writeInt(
this
.count);
    }

@Override
   
public

int
describeContents() {
       
return

0
;
    }

}

然后,需要在相同包下建一个同名的aidl文件,用于android生成相应的辅助文件:

package
com.easymorse;

parcelable CountBean;

这一步是android 1.5后的变化,无法通过adt生成aidl,也不能用一个比如全局的project.aidl文件,具体见:

http://www.anddev.org/viewtopic.php?p=20991

然后,需要在服务的aidl文件中修改如下:

package
com.easymorse;

import
com.easymorse.CountBean;

interface
ICountService {
    CountBean getCount();
}

时间: 2024-08-12 00:38:46

本地/远程Service 和Activity 的交方式(转)的相关文章

android开发之使用Messenger实现service与activity交互

service与activity交互的方式有多种,这里说说使用Messenger来实现两者之间的交互. Service程序 public class MessengerService extends Service { final Messenger mMessenger = new Messenger(new IncomingHandler()); @Override public IBinder onBind(Intent intent) { return mMessenger.getBin

Android Activity与远程Service的通信学习总结

当一个Service在androidManifest中被声明为 process=":remote", 或者是还有一个应用程序中的Service时,即为远程Service, 远程的意思是和当前程序不在同一个进程中执行. Activity和远程Service的跨进程通信(IPC)通过Binder机制,使用AIDL服务实现. 一.  定义远程服务端 1.新建一个project,project文件夹例如以下 2. 文件内容 aidl传递复杂对象时,须要该对象实现Parcelable或Seria

Android Service与Activity之间通信的几种方式

在Android中,Activity主要负责前台页面的展示,Service主要负责需要长期运行的任务,所以在我们实际开发中,就会常常遇到Activity与Service之间的通信,我们一般在Activity中启动后台Service,通过Intent来启动,Intent中我们可以传递数据给Service,而当我们Service执行某些操作之后想要更新UI线程,我们应该怎么做呢?接下来我就介绍两种方式来实现Service与Activity之间的通信问题 通过Binder对象 当Activity通过调

android 远程Service以及AIDL的跨进程通信

在Android中,Service是运行在主线程中的,如果在Service中处理一些耗时的操作,就会导致程序出现ANR. 但如果将本地的Service转换成一个远程的Service,就不会出现这样的问题了. 转换成远程Service非常简单,只需要在注册Service的时候将他的android:process的属性制定成 :remote就可以了. 重新运行项目,你会发现,不会出现ANR了. 为什么将MyService转换成远程Service后就不会导致程序ANR了呢?这是由于,使用了远程Serv

Service与Activity通信与AIDL

出差深圳一个月,终于回来了,一个月里干了不少,这些天里会慢慢总结一点东西出来,今天说的是关于Service的一点事:通信.通信的做法比较固定,基本上按照模板来写就可以实现. 1.Service与Activity通信 Activity通过startService()方法启动Service之后,Service将独立于Activity运行(虽然仍然是同一个进程),Activity无法指导Service如何运行.当Activity需要根据一些条件决定Service如何运行的时候,就需要有另外的方法了:将

Android Service的两种启动方式

参考链接 1. 概念 开始,先稍稍讲一点android中Service的概念和用途吧~ Service分为本地服务(LocalService)和远程服务(RemoteService): 1.本地服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC, 也不需要AIDL.相应bindService会方便很多.主进程被Kill后,服务便会终止. 2.远程服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符

(Android数据传递)Service和Activity之间-- 借助BroadcastReceiver--的数据传递

实现逻辑如下: 左侧为Activity中的执行逻辑,右侧为Service中的执行逻辑: /** * <功能描述> Service和Activity之间的数据交互:具体表现为: 1. 从Service获取数据源,传递到Activity中: 2. * 在Activity中作数据更新: 3. Service中的onCreate()在UI线程中执行,作延时需要在子线程中执行: * * @author Administrator */ public class MainActivity extends

Android—Service与Activity的交互

service-Android的四大组件之一.人称"后台服务"指其本身的运行并不依赖于用户可视的UI界面 实际开发中我们经常需要service和activity之间可以相互传递数据维持程序的运行. 先了解Service的生命周期吧. 新建一个类继Service: package com.example.myservicedemo.service; import android.app.Service; import android.content.Intent; import andr

016_03浅谈远程Service和AIDL

 如果需要访问不同进程间service中的数据或者方法,需要使用AIDL(android interface description language)工具,可以通过如下方法: public boolean bindService(Intent intent, ServiceConnection conn, int flags) :         public void unbindService(ServiceConnection conn); intent是跳转到service的inten