转载需注明出处,本文出自
fanfan的博客
原文地址为:
原文地址
service需要在所在应用的androidmanifest文件中进行配置:
<!-- name是service的包名路径-->
<!-- permission定义了开启该service时所需要的权限-->
<service android:name="com.android.server.backup.KeyValueBackupJob" android:permission="android.permission.BIND_JOB_SERVICE" > </service>
Service笔记:
service运行在进程的主线程中,这也就是说,如果service需要做一些消耗CPU的操作(比如回放mps3)或者是阻塞的操作(请求网络),那么此时就要单独开一个线程来进行这些耗时或者占用cpu的操作。
转载需注明出处,本文出自
fanfan的博客
原文地址为:
原文地址
接下来涉及到的内容包括
在研究service是什么时,先来看看service不是什么:
- service不是一个单独的进程。除非特别说明,否则service不会运行在他自己的进程中,而是运行在应用进程中。
- service不是一个线程。service也不是脱离主线程而运行的线程(避免anr:Application not responding)
service本身很简单,有两个主要的特征:
当service通过以上任何一种方式启动后,系统就会去初始化service并且调用service的onCreate方法,以及主线程中其他相关回调。这取决于service是否实现了一些相关的行为,比如开启一个线程来进行service所要做的工作。
Service本身很简单,可以借助service来实现一些简单或者复杂的交互。可以把service看成一个提供各自功能方法的Java本地对象,也可以是借助aidl实现远程调用。
有两种方式可以让service开启:
先来看一张Android源码提供的service的生命周期图
由图可知,此时service 的生命周期为onCreate–>onStartCommand–>onDestroy。
system会调用service的onCreate方法(如果需要的话)和onStartCommand方法,其中onStartCommand方法中的参数由客户端提供。service从此开始一直运行直到调用stopService或者stopSelf停止。多次调用startService方法时会多次触发onStartCommand方法,但是一旦调用了stopService或者是stopSelf方法,service会被停止(不论调用了多少次startService方法),开发者可以调用stopSelf(int) 来当service的开启意图被处理后才去停止service
当服务开启后有两种主要的运行模式,这个运行模式取决于onStartCommand的返回值:
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
}
首先看一下决定返回值的boolean变量mStartCompatibility的取值
public final void attach(
Context context,
ActivityThread thread, String className, IBinder token,
Application application, Object activityManager) {
.........
mStartCompatibility = getApplicationInfo().targetSdkVersion
< Build.VERSION_CODES.ECLAIR;
}
Bluid.VERSION_CODES.ECLAIR取值是API5,对应的Android版本是2.0。也就是说Android2.0以前service的运行模式是START_STICKY_COMPATIBILITY,Android2.0以后service的运行模式为START_STICKY。这是Service.java中默认返回的值,但总共有四种取值,那么这四种模式都是什么,分别有什么区别呢?
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
........
return Service.START_NOT_STICKY;
}
START_REDELIVER_INTENT(3):顾名思义,redeliver_intent,重新传一遍intent。当service被kill之后,会重新开启restarted,并且传入最后一次开启服务的intent。Intent会被一直保存,直到调用了stopself(int)终止了某个intent。在这种情况下onStartCommand中的intent不会为null,因为只有intent不为null的情况下被kill的service才会别restarted。
此时,service的生命周期为onCreate–>onBind–>onUnBind–>onDestroy
如果服务不处在running的状态,则会去调用onCreate方法,但是不会调用onStartCommand方法。Service会调用onBind方法返回一个 android.os.IBinder对象提供给客户端。客户端借助IBinder对象实现与service之间的通信。一旦service连接成功,则该service就会处于running的状态(无论客户端是否有Service的IBinder对象,他都会处于running的状态)。通常IBinder返回的是一个aidl中的对象。
一个Service可以同时保持开启和绑定连接两种形式。在这种情况下,service只要是被start或者是有一个多个连接存在(这些连接带有的标志为BIND_AUTO_CREATE、BIND_AUTO_CREATE),service就会保持running。
一旦以上所有情况都停了,service就会调用onDestroy方法并且终止。所有的清理工作(停止线程,注销广播接收器)都要在onDestroy时完成。
如下图所示
图中所述,
在Androidmanifest的service节点下可以添加一些属性对service的全局可用增加一些限制,比如:
<service android:name="。。。。。" android:permission="android.permission.。。。。" > </service>
这种情况下,如果其他应用想要使用该service,需要在androidmanifest中声明用户权限user-permission,赋予它开启、停止、绑定服务的权限。
从Android2.3开始,在startService时可以设置intent的flag:
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
这些flag赋予service临时拥有访问Intent中特定uri的权限。临时拥有的意思是当sevice被stopSelf后或者是service被stopped之后,这种权限就不再保留。
举个例子:
Intent intent = new Intent(Intent.ACTION_SEND);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri.Builder b = new Uri.Builder();
b.scheme("content");
b.authority("com.example.android.apis.content.FileProvider");
TypedValue tv = new TypedValue();
getResources().getValue(R.drawable.jellies, tv, true);
b.appendEncodedPath(Integer.toString(tv.assetCookie));
b.appendEncodedPath(tv.string.toString());
Uri uri = b.build();
intent.setType("image/jpeg");
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.setClipData(ClipData.newUri(getContentResolver(),
"image",uri));
startActivity(Intent.createChooser(intent, "Select share target"));
以上代码会赋予intent所指向的组件访问uri中type为image的数据。
Android系统会尽量将服务维持很长时间。什么样的服务会让系统这么做呢?开启的服务或者是与客户端绑定的服务
但是在系统在低内存的情况下时,不得不去杀死一些服务。接下来按照由高到低的优先级的顺序来说明(系统优先保持其运行的优先级)。
bindService(mBindIntent, conn,
Context.BIND_AUTO_CREATE | Context.BIND_ADJUST_WITH_ACTIVITY
| Context.BIND_WAIVE_PRIORITY)
看一下官网给的例子
servcie最常见的用法就是作为应用程序的第二大组件。除非特殊声明,否则一个.apk中的所有组件都运行在同一个进程中。在同一个进程中时,调用service的client可以很容易就获取到service的Ibinder实体对像。
大致分为两步
demo如下:
第一步,定义service
/** *这是一个提供本地通信的service,即进程内通信 */
public class LocalService extends Service {
private NotificationManager mNM;
// Unique Identification Number for the Notification.
// We use it on Notification start, and to cancel it.
private int NOTIFICATION = R.string.local_service_started;
/** * Class for clients to access. Because we know this service always * runs in the same process as its clients, we don't need to deal with * IPC. * 该service实现的是本地的通信,即不需要跨进程,所以只需提供一个Binder实体 * 对象供其使用即可 */
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
@Override
public void onCreate() {
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// Display a notification about us starting. We put an icon
//in the status bar.
//一旦通过start开启了service就会在状态栏进行通知
showNotification();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("LocalService", "Received start id " + startId + ": " + intent);
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
// Cancel the persistent notification.
mNM.cancel(NOTIFICATION);
// Tell the user we stopped.
Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
// This is the object that receives interactions from clients. See
// RemoteService for a more complete example.
private final IBinder mBinder = new LocalBinder();
/** * Show a notification while this service is running. */
private void showNotification() {
// In this sample, we'll use the same text for the ticker and
//the expanded notification
CharSequence text = getText(R.string.local_service_started);
// The PendingIntent to launch our activity if the user
//selects this notification
//当点击该通知时启动activity
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, LocalServiceActivities.Controller.class), 0);
// Set the info for the views that show in the notification panel.
Notification notification = new Notification.Builder(this)
// the status icon设置状态栏通知图标
.setSmallIcon(R.drawable.stat_sample)
// the status text设置状态栏通知文本
.setTicker(text)
// the time stamp设置时间戳
.setWhen(System.currentTimeMillis())
// the label of the entry下拉时显示的title
.setContentTitle(getText(R.string.local_service_label))
// the contents of the entry下拉时显示的文本
.setContentText(text)
// The intent to send when the entry is clicked点击时的动作
.setContentIntent(contentIntent)
.build();
// Send the notification.
mNM.notify(NOTIFICATION, notification);
}
}
//END_INCLUDE(service)
第二步,绑定服务或者是开启服务
public static class Binding extends Activity {
private boolean mIsBound;
// BEGIN_INCLUDE(bind)
private LocalService mBoundService;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. Because we have bound to a explicit
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
//当绑定服务时会触发该方法
mBoundService = ((LocalService.LocalBinder)service).getService();
// Tell the user about this for our demo.
Toast.makeText(Binding.this, R.string.local_service_connected,
Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
// Because it is running in our same process, we should never
// see this happen.
mBoundService = null;
Toast.makeText(Binding.this, R.string.local_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};
void doBindService() {
// Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be
// supporting component replacement by other applications).
bindService(new Intent(Binding.this,
LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService() {
if (mIsBound) {
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
doUnbindService();
}
// END_INCLUDE(bind)
private OnClickListener mBindListener = new OnClickListener() {
public void onClick(View v) {
doBindService();
}
};
private OnClickListener mUnbindListener = new OnClickListener() {
public void onClick(View v) {
doUnbindService();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.local_service_binding);
// Watch for button clicks.
Button button = (Button)findViewById(R.id.bind);
button.setOnClickListener(mBindListener);
button = (Button)findViewById(R.id.unbind);
button.setOnClickListener(mUnbindListener);
}
}
或者是通过start方式开启服务
public static class Controller extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.local_service_controller);
// Watch for button clicks.
Button button = (Button)findViewById(R.id.start);
button.setOnClickListener(mStartListener);
button = (Button)findViewById(R.id.stop);
button.setOnClickListener(mStopListener);
}
private OnClickListener mStartListener = new OnClickListener() {
public void onClick(View v) {
// Make sure the service is started. It will continue running
// until someone calls stopService(). The Intent we use to find
// the service explicitly specifies our service component, because
// we want it running in our own process and don't want other
// applications to replace it.
startService(new Intent(Controller.this,
LocalService.class));
}
};
private OnClickListener mStopListener = new OnClickListener() {
public void onClick(View v) {
// Cancel a previous call to startService(). Note that the
// service will not actually stop at this point if there are
// still bound clients.
stopService(new Intent(Controller.this,
LocalService.class));
}
};
}
跨进程调用和进程内调用区别就在于如何去绑定一个服务,以及如何获取到binder对象。有了进程内调用的详细demo做参考,接下来会出一个简化的跨进程调用的demo
为了营造跨进程的条件,可以选择在同一台设备运行两个应用程序,或者是在一个应用程序中的清单配置文件中声明service的Android:proces属性在另一个进程中。
本例中,创建两个应用程序,首先是远程service的应用程序:
public class RemoteService extends Service {
static class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 0:
Log.i("fang","客户端绑定啦~~");
Message message = Message.obtain();
try {
msg.replyTo.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
}
final static Messenger messenger = new Messenger(new IncomingHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}
在另一个应用程序中调用
public class RemoteServiceActivities {
/** * 绑定服务 */
public static class BindServiceActivity extends Activity implements View.OnClickListener{
private boolean mIsBound ;
private Messenger mService = null;
private static class IncomingHandler extends Handler{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.i("fang","接收到服务器端消息啦~~");
}
}
private Messenger messenger = new Messenger(new IncomingHandler());
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//获取到binder
mService = new Messenger(service);
Message message = Message.obtain(null,0);
//给service传递messenger用于信息返回
message.replyTo = messenger;
try {
mService.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bind);
initView();
}
void initView(){
TextView bind = (TextView) findViewById(R.id.bind);
TextView unbind = (TextView) findViewById(R.id.unbind);
setClickListener(bind,unbind);
}
void setClickListener(View...views){
for (View view:views) {
if (view != null){
view.setOnClickListener(this);
}
}
}
void doBindService(){
Intent intent = new Intent("com.android.zrf.remoteservice");
bindService(intent,connection,BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService(){
if (mIsBound){
mIsBound = false;
unbindService(connection);
}
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.bind:
doBindService();
break;
case R.id.unbind:
doUnbindService();
break;
}
}
}
/** * 开启服务 */
public static class StartServiceActivity extends Activity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.start);
initView();
}
void initView(){
TextView start = (TextView) findViewById(R.id.start);
TextView stop = (TextView) findViewById(R.id.stop);
setClickListener(start,stop);
}
void setClickListener(View...views){
for (View view:views) {
if (view != null){
view.setOnClickListener(this);
}
}
}
void startService(){
Intent intent = new Intent(StartServiceActivity.this,LocalService.class);
startService(intent);
}
void stopService(){
Intent intent = new Intent(StartServiceActivity.this,LocalService.class);
stopService(intent);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.start:
startService();
break;
case R.id.stop:
stopService();
break;
}
}
}
}
转载需注明出处,本文出自fanfan的博客原文地址为:原文地址