android四大组件之Service个人总结


首先是自己的gitHub代码

10月份开始转战Android Studio了,重写了一份一样的代码。。。挺没意思的

Service应该可以看做和Activity平起平坐的存在。这一点从Service在AndroidManifest文件里注册的位置就能够看出来。


第一部分:关于Activity和Service之间进行通信(参考文章)


一、Activity操作Service

startService启动了就启动了,Activity只能在startService(Intent intent)的intent里面一次性传递信息给Service

之后想要操控Service还是需要绑定上去。

所以,无论是bindService还是startService,想要后期从客户端控制Service的话,必须提供IBinder,用于提供客户端用来与服务进行交互的接口。可以通过三种方式定义接口(参考API文档):


第一种方式:继承Binder类,在Activity的onServiceConnected里面向下转型得到Binder对象

继承Service这个类一般可以复写的方法有onCreate()、onStartCommand()、onDestory()以及onBinder()方法。

这里面onBinder()是抽象的,返回的是一个Binder对象。

至于这个Binder对象的作用:

写一个Activity,

1:绑定一个Service:

Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE); // 绑定服务

2.里面放一个成员内部类

private ServiceConnection mServiceConnection = new ServiceConnection(){
	onServiceConnected(ComponentName name, IBinder service){
		//第二个参数就是下面bindService的Intent创建的MyService
		//向下转型就能得到MyService的instance,可以在此回调Service的方法
		//Activity获得Binder的实例,可以在Activity里调用Binder的方法。
	}
	onServiceDisconnected(ComponentName name){	
	}
}

3.Binder是Service的成员变量,可以获得Service的引用。而Activiyt在BindService之后获得了Binder的引用,也就能获得Service的引用了。

第二种:使用Messenger(进程间通信--IPC)

http://stackoverflow.com/questions/17378819/is-the-android-service-still-alive-even-after-the-ondestroy-be-called

stackoverflow上面学来的,可以从Activity传信息(Message)给Service,Service收到信息后还能做出回应,传消息给Activity(Message)

msg有arg1,arg2可以携带两个整型数,数据比较多的话,Message有一个setData方法可以携带一个Bundle(用起来跟Map差不多),所以这种方法在Activity和Service之间传递信息基本能满足所有需求。

过程描述一下就是,

Service在onBind里面把自己的Messenger.getBinder返回出去,Activity在onServiceConnected里面根据这个IBinder创建一个Messenger(可能保留为成员变量)。Binder就把Activity和Service这两大组件联系起来了。通信时,Activity这边obtain一个msg,msg里面装一些arg1(int),arg2(int),replyTo(这个很重要),用BInder创建的Messenger把msg发出去,Service就能拿到replyTo的引用,replyTo是一个Activity创建的Messeneger对象,发送的消息会在Activity里面处理。就等于Service回复Activity了。到此Activity和Service的一个通信来回完成。

其实真正的Messenger就两个,都是根据InComingHandler创造的,InComingHandler确定了接到消息后干什么。只是BindService这个过程把Service里面的Messeneger给了Activity(实际上给的是getBinder),replyTo这个函数把Activity里面的Messeneger给了Service。简单说就是ActIvi和Service分别持有对方的成员变量的引用。

实际应用时,一个Service能持有多个Activity的Messenegr对象(保存在一个List里面),一个Activity应该也能持有多个Service的连接(这我猜的)。

没有什么代码比APIdemo更好了。

package io.appium.android.apis.app;

import io.appium.android.apis.R;
import io.appium.android.apis.app.LocalServiceActivities.Binding;

import android.app.Activity;
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.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MessengerServiceActivities {
    /**
     * Example of binding and unbinding to the remote service.
     * This demonstrates the implementation of a service which the client will
     * bind to, interacting with it through an aidl interface.</p>
     * 
     * <p>Note that this is implemented as an inner class only keep the sample
     * all together; typically this code would appear in some separate class.
     */
    public static class Binding extends Activity {

        /** Messenger for communicating with service. */
        Messenger mService = null;
        /** Flag indicating whether we have called bind on the service. */
        boolean mIsBound;
        /** Some text view we are using to show state information. */
        TextView mCallbackText;
        
        /**
         * Handler of incoming messages from service.
         */
        class IncomingHandler extends Handler {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MessengerService.MSG_SET_VALUE:
                        mCallbackText.setText("Received from service: " + msg.arg1);
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
        }
        
        /**
         * Target we publish for clients to send messages to IncomingHandler.
         */
        final Messenger mMessenger = new Messenger(new IncomingHandler());
        
        /**
         * Class for interacting with the main interface of the service.
         */
        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.  We are communicating with our
                // service through an IDL interface, so get a client-side
                // representation of that from the raw service object.
                mService = new Messenger(service);
                mCallbackText.setText("Attached.");

                // We want to monitor the service for as long as we are
                // connected to it.
                try {
                    Message msg = Message.obtain(null,
                            MessengerService.MSG_REGISTER_CLIENT);
                    msg.replyTo = mMessenger;
                    mService.send(msg);
                    
                    // Give it some value as an example.
                    msg = Message.obtain(null,
                            MessengerService.MSG_SET_VALUE, this.hashCode(), 0);
                    mService.send(msg);
                } catch (RemoteException e) {
                    // In this case the service has crashed before we could even
                    // do anything with it; we can count on soon being
                    // disconnected (and then reconnected if it can be restarted)
                    // so there is no need to do anything here.
                }
                
                // As part of the sample, tell the user what happened.
                Toast.makeText(Binding.this, R.string.remote_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.
                mService = null;
                mCallbackText.setText("Disconnected.");

                // As part of the sample, tell the user what happened.
                Toast.makeText(Binding.this, R.string.remote_service_disconnected,
                        Toast.LENGTH_SHORT).show();
            }
        };
        
        void doBindService() {
            // Establish a connection with the service.  We use an explicit
            // class name because there is no reason to be able to let other
            // applications replace our component.
            bindService(new Intent(Binding.this, 
                    MessengerService.class), mConnection, Context.BIND_AUTO_CREATE);
            mIsBound = true;
            mCallbackText.setText("Binding.");
        }
        
        void doUnbindService() {
            if (mIsBound) {
                // If we have received the service, and hence registered with
                // it, then now is the time to unregister.
                if (mService != null) {
                    try {
                        Message msg = Message.obtain(null,
                                MessengerService.MSG_UNREGISTER_CLIENT);
                        msg.replyTo = mMessenger;
                        mService.send(msg);
                    } catch (RemoteException e) {
                        // There is nothing special we need to do if the service
                        // has crashed.
                    }
                }
                
                // Detach our existing connection.
                unbindService(mConnection);
                mIsBound = false;
                mCallbackText.setText("Unbinding.");
            }
        }

        
        /**
         * Standard initialization of this activity.  Set up the UI, then wait
         * for the user to poke it before doing anything.
         */
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            setContentView(R.layout.messenger_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);
            
            mCallbackText = (TextView)findViewById(R.id.callback);
            mCallbackText.setText("Not attached.");
        }

        private OnClickListener mBindListener = new OnClickListener() {
            public void onClick(View v) {
                doBindService();
            }
        };

        private OnClickListener mUnbindListener = new OnClickListener() {
            public void onClick(View v) {
                doUnbindService();
            }
        };
    }
}

Service

/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.appium.android.apis.app;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;

import java.util.ArrayList;

// Need the following import to get access to the app resources, since this
// class is in a sub-package.
import io.appium.android.apis.R;
import io.appium.android.apis.app.RemoteService.Controller;

/**
 * This is an example of implementing an application service that uses the
 * {@link Messenger} class for communicating with clients.  This allows for
 * remote interaction with a service, without needing to define an AIDL
 * interface.
 *
 * <p>Notice the use of the {@link NotificationManager} when interesting things
 * happen in the service.  This is generally how background services should
 * interact with the user, rather than doing something more disruptive such as
 * calling startActivity().
 */

public class MessengerService extends Service {
    /** For showing and hiding our notification. */
    NotificationManager mNM;
    /** Keeps track of all current registered clients. */
    ArrayList<Messenger> mClients = new ArrayList<Messenger>();
    /** Holds last value set by a client. */
    int mValue = 0;
    
    /**
     * Command to the service to register a client, receiving callbacks
     * from the service.  The Message's replyTo field must be a Messenger of
     * the client where callbacks should be sent.
     */
    static final int MSG_REGISTER_CLIENT = 1;
    
    /**
     * Command to the service to unregister a client, ot stop receiving callbacks
     * from the service.  The Message's replyTo field must be a Messenger of
     * the client as previously given with MSG_REGISTER_CLIENT.
     */
    static final int MSG_UNREGISTER_CLIENT = 2;
    
    /**
     * Command to service to set a new value.  This can be sent to the
     * service to supply a new value, and will be sent by the service to
     * any registered clients with the new value.
     */
    static final int MSG_SET_VALUE = 3;
    
    /**
     * Handler of incoming messages from clients.
     */
    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_REGISTER_CLIENT:
                    mClients.add(msg.replyTo);
                    break;
                case MSG_UNREGISTER_CLIENT:
                    mClients.remove(msg.replyTo);
                    break;
                case MSG_SET_VALUE:
                    mValue = msg.arg1;
                    for (int i=mClients.size()-1; i>=0; i--) {
                        try {
                            mClients.get(i).send(Message.obtain(null,
                                    MSG_SET_VALUE, mValue, 0));
                        } catch (RemoteException e) {
                            // The client is dead.  Remove it from the list;
                            // we are going through the list from back to front
                            // so this is safe to do inside the loop.
                            mClients.remove(i);
                        }
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
    
    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());
    
    @Override
    public void onCreate() {
        mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

        // Display a notification about us starting.
        showNotification();
    }

    @Override
    public void onDestroy() {
        // Cancel the persistent notification.
        mNM.cancel(R.string.remote_service_started);

        // Tell the user we stopped.
        Toast.makeText(this, R.string.remote_service_stopped, Toast.LENGTH_SHORT).show();
    }
    
    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }

    /**
     * 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.remote_service_started);

        // Set the icon, scrolling text and timestamp
        Notification notification = new Notification(R.drawable.stat_sample, text,
                System.currentTimeMillis());

        // The PendingIntent to launch our activity if the user selects this notification
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
                new Intent(this, Controller.class), 0);

        // Set the info for the views that show in the notification panel.
        notification.setLatestEventInfo(this, getText(R.string.remote_service_label),
                       text, contentIntent);

        // Send the notification.
        // We use a string id because it is a unique number.  We use it later to cancel.
        mNM.notify(R.string.remote_service_started, notification);
    }
}






第三种: 使用AIDL(略)




二、Service传递信息给Activity

一般Service里面进行一些后台操作,数据处理什么的。需要把数据展示出来的时候就需要通知Activity去展示界面了。

方法有三种:

1.在onServiceConnected里面获得Service引用后

service.setActivity(MainActivity.this);
简单粗暴


2. 在Activity里面获得Service的实例后,为其设置接口

service.setDataListener(new DataListener(){
  onDataChanged(){ //这里面调用Activity的方法更新UI
}  

 
});

service里面通过接口进行回调,service里的数据就能传给Activity里的DataListener这个匿名内部类了,也就实现了传消息给Activity。


3. Service里面发送广播,Activity里面注册一个BroadcastReceiver ,onReceive里面更新界面。



第二部分:startService和bindService两种不同的形式

startService:

Using startService() overrides the default service lifetime that is managed by bindService(Intent, ServiceConnection, int): it requires the service to remain running until stopService(Intent) is called, regardless of whether any clients are connected to it. Note that calls to startService() are not nesting: no matter how many times you call startService(), a single call to stopService(Intent) will stop it.


bindService:

Connect to an application service, creating it if needed. This defines a dependency between your application and the service. The given conn will receive the service object when it is created and be told if it dies and restarts. The service will be considered required by the system only for as long as the calling context exists. For example, if this Context is an Activity that is stopped, the service will not be required to continue running until the Activity is resumed.


两种形式的Service都可以被应用组件通过Intent的方式调用(即使此服务来自另一应用)。可以在manifest文件里添加android:exported = false属性,确保服务只能被自己的应用调用。


通过Context.BindService创建的Service, Context实例挂掉Service就挂掉了。

而Context.startService创建的Service, Context实例挂不挂和Service没什么关系。


1.startService部分:

如果通过startService启动的service被干掉了,会根据onStartCommand方法返回的整型数确定使用哪种值,具体有四种情况。

public static final int START_NOT_STICKY


Added in API level 5
Constant to return from onStartCommand(Intent, int, int): if this service's process is killed while it is started (after returning from onStartCommand(Intent, int, int)), and there are no new start intents to deliver to it, then take the service out of the started state and don't recreate until a future explicit call to Context.startService(Intent). The service will not receive a onStartCommand(Intent, int, int) call with a null Intent because it will not be re-started if there are no pending Intents to deliver.


This mode makes sense for things that want to do some work as a result of being started, but can be stopped when under memory pressure and will explicit start themselves again later to do more work. An example of such a service would be one that polls for data from a server: it could schedule an alarm to poll every N minutes by having the alarm start its service. When its onStartCommand(Intent, int, int) is called from the alarm, it schedules a new alarm for N minutes later, and spawns a thread to do its networking. If its process is killed while doing that check, the service will not be restarted until the alarm goes off.


Constant Value: 2 (0x00000002)
public static final int START_REDELIVER_INTENT


Added in API level 5
Constant to return from onStartCommand(Intent, int, int): if this service's process is killed while it is started (after returning from onStartCommand(Intent, int, int)), then it will be scheduled for a restart and the last delivered Intent re-delivered to it again via onStartCommand(Intent, int, int). This Intent will remain scheduled for redelivery until the service calls stopSelf(int) with the start ID provided to onStartCommand(Intent, int, int). The service will not receive a onStartCommand(Intent, int, int) call with a null Intent because it will will only be re-started if it is not finished processing all Intents sent to it (and any such pending events will be delivered at the point of restart).


Constant Value: 3 (0x00000003)
public static final int START_STICKY


Added in API level 5
Constant to return from onStartCommand(Intent, int, int): if this service's process is killed while it is started (after returning from onStartCommand(Intent, int, int)), then leave it in the started state but don't retain this delivered intent. Later the system will try to re-create the service. Because it is in the started state, it will guarantee to call onStartCommand(Intent, int, int) after creating the new service instance; if there are not any pending start commands to be delivered to the service, it will be called with a null intent object, so you must take care to check for this.


This mode makes sense for things that will be explicitly started and stopped to run for arbitrary periods of time, such as a service performing background music playback.


Constant Value: 1 (0x00000001)
public static final int START_STICKY_COMPATIBILITY


Added in API level 5
Constant to return from onStartCommand(Intent, int, int): compatibility version of START_STICKY that does not guarantee that onStartCommand(Intent, int, int) will be called again after being killed.


Constant Value: 0 (0x00000000)

以上四个可能的返回值在Service被系统干掉后(一般是系统资源紧张的情况下)会有用,onstartCommand传进来的第一个参数intent可能为null

自己写程序的时候在startService里面intent.setflags,可是在onStartCommand里面传入的intent是null

问题就在这,所以有两种方案:检查一下是否为null,或者返回START_STICKY_COMPATIBILITY

下面是onStartCommand的三个参数的解释

Parameters
intent	The Intent supplied to startService(Intent), as given. <span style="color:#ff0000;">This may be null if the service is being restarted after its process has gone away, and it had previously returned anything except START_STICKY_COMPATIBILITY.</span>
flags	Additional data about this start request. Currently either 0, START_FLAG_REDELIVERY, or START_FLAG_RETRY.
startId	A unique integer representing this specific request to start. Use with stopSelfResult(int).

通过startService(Intent intent)启动service,传递的intent是应用组件与Service之间唯一的通信模式。但是,如果希望该服务能够返回结果,可以在启动服务的客户端

里为广播创建一个pendingIntent并通过启动服务的Intent将其传递给服务,然后,服务就可以使用广播传递结果。(不懂。。。。)


停止服务

停止服务有三个方法:

第一种:

public final void stopSelf ()

Added in API level 1
Stop the service, if it was previously started. This is the same as calling stopService(Intent) for this particular service.
直接停止服务。

第二种:

public final void stopSelf (int startId)

Added in API level 1
Old version of stopSelfResult(int) that doesn't return a result.
来看一下stopSelfResult(int startId)

public final boolean stopSelfResult (int startId)

Added in API level 1
Stop the service if the most recent time it was started was startId. This is the same as calling stopService(Intent) for this particular service but allows you to safely avoid stopping if there is a start request from a client that you haven't yet seen in onStart(Intent, int).

Be careful about ordering of your calls to this function.. If you call this function with the most-recently received ID before you have called it for previously received IDs, the service will be immediately stopped anyway. If you may end up processing IDs out of order (such as by dispatching them on separate threads), then you are responsible for stopping them in the same order you received them.
解释一下,每次startService系统都会生成一个startId,多次向service发送请求就会按照请求顺序生成多个startId,如果stopSelf(int startId)传进来的不是最后一个statrId,那么service不会停止。这样做是为了避免有请求未执行就销毁服务。


第三种:

public abstract boolean stopService (Intent service)

Added in API level 1
Request that a given application service be stopped. If the service is not running, nothing happens. Otherwise it is stopped. Note that calls to startService() are not counted -- this stops the service no matter how many times it was started.

Note that if a stopped service still has ServiceConnection objects bound to it with the BIND_AUTO_CREATE set, it will not be destroyed until all of these bindings are removed. See the Service documentation for more details on a service's lifecycle.
不需要考虑startId问题,但如果被绑定,还不会销毁。


2.bindService部分

通过bindService形式绑定到service,这种service只用于与其绑定的组件,因此如果没有组件绑定到该服务,就会被销毁。

被绑定的服务必须实现onBind回调方法,返回一个Binder的实现。

多个客户端可以同时绑定到一个服务,不过,只有在第一个客户端绑定的时候才会调用onBind,之后不会调用onBind。当最后一个客户端取消绑定后,系统会

销毁该服务(除非这是startService)


ServiceConnection接口有两个回调方法,其中onServiceConnected用于传递onBind方法返回的IBinder

而onServiceDisconnected用于在服务意外断开连接后调用,客户端取消绑定(unBindService)时,绝对不会调用该方法。


关于Activity生命周期的一些:

  • 如果您只需要在 Activity 可见时与服务交互,则应在 onStart() 期间绑定,在 onStop() 期间取消绑定。
  • 如果您希望 Activity 在后台停止运行状态下仍可接收响应,则可在 onCreate() 期间绑定,在onDestroy() 期间取消绑定。请注意,这意味着您的 Activity 在其整个运行过程中(甚至包括后台运行期间)都需要使用服务,因此如果服务位于其他进程内,那么当您提高该进程的权重时,系统终止该进程的可能性会增加
关于Service的生命周期的一些:


官方说明了两种service的生命周期

一般在Activity的onStart里面绑定服务,onServiceConnected里面设置布尔值标识符为true,onServiceDisconnected里面设置标识符为false。在Activity的onStop里面判断下布尔值是否为true,true的话unBindService。


而允许绑定的startService的生命周期就复杂一些





以上主要参考了官方API文档

服务

绑定服务

中文简体版本看着也简单。

先写到这里,看看稍后能不能加一个Demo





















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值