美文网首页Android基础知识
Service工作中的方方面面

Service工作中的方方面面

作者: ZebraWei | 来源:发表于2018-12-03 18:24 被阅读0次

    版权声明:本文为小斑马伟原创文章,转载请注明出处!

    一、service和线程的区别和场景

    线程:是程序执行的最小单元。也是分配CPU的基本单位。在android中,UI线程分为主线程和工作线程。主线程一般负责UI的绘制和事件的响应操作,为了保证UI线程的响应能力, 一般主线程不会执行耗时的逻辑操作,否者容易造成ANR和用户体验效果不佳问题。
    Service:是Android提供的一种机制,四大主件之一。运行在主线程中,是系统进程托管。

    二、如何管理service生命周期

    • 1.通过绑定服务,来进行service和activity之间的绑定。
    • 2.直接开启一个服务。两种生命周期不一样。但都会回调onCreate和onDestory方法。 Service的生命周期:一种是通过startService创建的生命周期,另外一种是通过bindservice创建的生命周期 两种都是从onCreate开始和onDestory结束。通过生命周期的方法,可以监控服务整体的执行过程。包括创建,运行,销毁。而服务有效的生命周期分别是在onStartCommand和onBind开始的。
      startService:它分别会回调onCreate和onStartCommand方法。一个Service被startService多次启动之后,它的onCreate方法只会调用一次。而onStartCommand方法调用的次数和startService次数是一样的。
      stopService:是关闭Service服务。手动调用之后,会调用onDestory方法。如果一个Service被启动,且被绑定的时候,如果没有解绑的情况下,stopService是无法停止服务的。
      bindService: 作用就是绑定service服务,分别会调用onCreate和onBind方法。
      unBindService: 作用就是解绑service服务,内部会调用onUnbind方法和onDestory来解绑服务和销毁服务的操作。

    启动和绑定Service服务先后次序的问题

    • 1.先绑定服务后启动服务: 当service先与绑定状态运行了,然后再启动服务。绑定服务将会转换为启动服务状态。 如果之前绑定的Activity被销毁了,也不会影响服务的运行。直到收到停止服务的时候,才会停止运行下去。
    • 2.先启动服务后绑定服务:如果当前serfice服务已经启动了,再进行绑定。不会转换为绑定服务状态。会与Activity绑定,即使Activity解除绑定了,服务还有在后台运行。

    启动服务的优先级比绑定服务的优先级要高,服务再其托管进程的主线程中运行(UI线程)不会创建自己的线程,也不会单独在进程中运行。如果在服务中做耗时的操作,必须开启子线程。

    三、Service和IntentService区别

    Service是用于后台服务的,当应用程序被挂到后台的时候,为了保证应用的某些组件后台可以继续工作。这时候就会引用Service。service不是一个独立的进程,更不是独立的线程。它是依赖于我们应用程序的主线程的。不建议在service中编写耗时的逻辑和操作,否者会引起ANR。因此引入IntentService。
    IntentService:是继承并处理异步请求的一个类 。内部有一个工作线程HandlerThread来处理耗时操作。使用上跟service是一样的。在执行完任务之后,IntentService会自动的停止。可以多起启动IntentService,而每次耗时的操作都会以工作队列的方式在IntentService的回调方法中执行。每次只执行一个工作线程,执行完第一个依次执行下一个。

      1. IntentService是继承并处理异步请求的一个类。
      1. 内有一个工作线程来处理耗时操作 。
      1. IntentService内部则是通过消息的方式发送给HandlerThread的,然后由Handler中的Looper来处理消息。

    IntentService源码分析
    IntentService构造方法

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public IntentService(String name) {
        super();
        mName = name;
    }
    

    分析:该构造方法需在子类中调用,用于创建一个IntentService对象。参数name用于定义工作线程的名称,仅仅用于调式作用。我们知道Service服务的生命周期是从onCreate方法开始的。那么就来看看IntentService#onCreate方法吧。
    IntentService#onCreate方法

    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.
    
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
    
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
    

    分析:该方法首先利用HandlerThread类创建了一个循环的工作线程thread,然后将工作线程中的Looper对象作为参数来创建ServiceHandler消息执行者。由另一篇博客Android HandlerThread 源码分析可知,HandlerThread+Handler构建成了一个带有消息循环机制的异步任务处理机制。因此开发者就可以将异步任务封装成消息的形式发送到工作线程中去执行了。Service服务生命周期第二步执行IntentService#onStartCommand方法。

    IntentService#onStartCommand方法

    /**
     * You should not override this method for your IntentService. Instead,
     * override {@link #onHandleIntent}, which the system calls when the IntentService
     * receives a start request.
     * @see android.app.Service#onStartCommand
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
    

    分析:在IntentService子类中你无需重写该方法。然后你需要重写onHandlerIntent方法,
    系统会在IntentService接受一个请求开始调用该方法。我们看到在该方法中仅仅是调用了onStart方法而已,跟踪代码:

    四、Parcelable(android 特定序列化的接口)和Serializable

    因为存在内存中的对象都是暂时的,为了对象的状态保存下来。需要把对象写到磁盘和其他地方中。过程就是对象序列化。
    序列化:内存中对象-->磁盘
    反序列化:磁盘中对象-->内存
    区别:从实现上和效率上区分

    /**
     * Created by Mjj on 2018/8/11.
     */
    
    public class SerializableImplement implements Serializable {
    /**
     * 生成序列号标识
     */
    private static final long serialVersionUID = -2083503801443301445L;
    
    private int id;
    private String name;
    
    
    public int getId() {
        return id;
    }
    
    public void setId(int id) {
        this.id = id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    

    Serializable接口:专门对对象序列化和反序列化操作的。声明了一个serialVersionID标识。用来辅助我们序列化和反序列化过程的。只有当序列化和反序列化的ID相同。才能被反序列化。序列化的过程时:会把serialVersionID写到序列化的文件中,反序列化的时候会在序列化的文件中检查这个ID。判断是否一致,如果一致进行反序列化,否者失败。
    实现简单,但是内存开销非常大。

    public class ParcableImplement implements Parcelable {
    public int id;
    public String name;
    
    /**
     * 当前对象的内容描述,一般返回0即可
     *
     * @return
     */
    @Override
    public int describeContents() {
        return 0;
    }
    
    protected ParcableImplement(Parcel in) {
        this.id = in.readInt();
        this.name = in.readString();
    }
    
    /**
     * 将当前对象写入序列化结构中
     *
     * @param dest
     * @param flags
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.id);
        dest.writeString(this.name);
    }
    
    /**
     * public static final一个都不能少,内部对象CREATOR的名称也不能改变,必须全部大写。
     * 重写接口中的两个方法:
     * createFromParcel(Parcel in) 实现从Parcel容器中读取传递数据值,封装成Parcelable对象返回逻辑层,
     * newArray(int size) 创建一个类型为T,长度为size的数组,供外部类反序列化本类数组使用。
     */
    public static final Creator<ParcableImplement> CREATOR = new Creator<ParcableImplement>() {
        /**
         * 从序列化后的对象中创建原始对象
         */
        @Override
        public ParcableImplement createFromParcel(Parcel in) {
            return new ParcableImplement(in);
        }
    
        /**
         * 创建指定长度的原始对象数组
         * @param size
         * @return
         */
        @Override
        public ParcableImplement[] newArray(int size) {
            return new ParcableImplement[size];
        }
    };
    

    Parcelable接口:在性能上比Serializable接口好,因为内存开销方面比Serializable要小。在内存间传输数据一般推举用Parcelable接口。缺点就是代码实现麻烦。通过writeToParcel映射成Parcel对象。然后通过Creator映射成我们的对象。可以简单把Parcel对象看作是读写流。

    五、AIDL

    Binder:最常见的应用就是AIDL。是android接口定义语言,是进程间通信(IPC)机制。在android中一个进程无法去访问另外一个进程的内存。这是就可以使用AIDL,实现客户端和服务端进程通信的机制,进程间相互通信都认可的一个接口。通过这个接口,定义好自己的AIDL的接口文件,通过IDE的编译系统就可以自动生成Binder接口。

    • 1.创建AIDL:实体对象、新建AIDL文件、make工程
    • 2.服务端:新建Service、创建Binder对象、定义方法
    • 3.客户端:实现serviceConnection、BindService

    Person类: 用于序列化和反序列化操作。为了服务端和客户端跨进程通信使用。

    public class Person implements Parcelable {
    private String mName;
    
    public Person(String name) {
        mName = name;
    }
    
    protected Person(Parcel in) {
        mName = in.readString();
    }
    
    public static final Creator<Person> CREATOR = new Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }
    
        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };
    
    @Override
    public int describeContents() {
        return 0;
    }
    
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mName);
    }
    
    @Override
    public String toString() {
        return "Person{" +
                "mName='" + mName + '\'' +
                '}';
    }
    

    AIDE文件

    package com.zhonghong.bean;
    
    parcelable Person;
    

    IMyAidl文件

    package com.zhonghong.aidl;
    
    import com.zhonghong.bean.Person;
    
    interface IMyAidl {
        void addPerson(in Person person);
    
        List<Person> getPersonList(); 
    }
    

    服务端service实现

    public class MyAidlService extends Service {
    private final String TAG = this.getClass().getSimpleName();
    
    private ArrayList<Person> mPersons;
    
    /**
     * 创建生成的本地 Binder 对象,实现 AIDL 制定的方法
     */
    private IBinder mIBinder = new IMyAidl.Stub() {
        
        @Override
        public List<Person> getPersonList() throws RemoteException {
            return mPersons;
        }
        
        @Override
        public void addPerson(Person person) throws RemoteException {
            mPersons.add(person);
        }
    };
    /**
     * 客户端与服务端绑定时的回调,返回 mIBinder 后客户端就可以通过它远程调用服务端的方法,即实现了通讯
     *
     * @param intent
     * @return
     */
    @Override
    public IBinder onBind(Intent intent) {
         mPersons = new ArrayList<Person>();
         Log.d(TAG, "MyAidlService onBind");
         return mIBinder;
    }
    

    客户端:

    public class MyServiceActivity extends Activity {
    private Button mBtn;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBtn = (Button) findViewById(R.id.btyStartService);
    
        mBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent1 = new Intent(getApplicationContext(),
                        MyAidlService.class);
                bindService(intent1, mConnection, BIND_AUTO_CREATE);
                addPerson();
            }
        });
    }
    
    private IMyAidl mAidl;
    
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 连接后拿到 Binder,转换成 AIDL,在不同进程会返回个代理
            mAidl = IMyAidl.Stub.asInterface(service);
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mAidl = null;
        }
    };
    
    List<Person> personList;
    
    public void addPerson() {
        Random random = new Random();
        Person person = new Person("shixin" + random.nextInt(10));
        
        try {
            mAidl.addPerson(person);
            personList = mAidl.getPersonList();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
    

    Aidl本质就是简化了进程间通信的一种方式,内核实质还是Binder机制。Stub内部类就是Bidner包装的一个类。
    一、构造方法

    /**
     * Construct the stub at attach it to the interface.
     * 构造方法
     */
    public Stub() {
        this.attachInterface(this, DESCRIPTOR);
    }
     //binder中的方法
    public void attachInterface(IInterface owner, String descriptor) {
       mOwner = owner;
       mDescriptor = descriptor;
    }
    

    构造方法中调用了binder中的attachInterface(Interface owner, String descriptor)方法,其中descriptor。可以看做是进程的唯一标识,,IInterface c参数则把Stub自己传了进去,这个在后面asInterface()方法中会用到。
    二、asInterface(android.os.IBinder obj)方法。这个方法是在绑定服务成功后客户端调用的,用于在获取到服务端返回的IBInder对象后,将其转换为对应的具有功能方法的对象,毕竟IBinder只是一个具有跨进程传输的接口。(类似是把一个接口转换成它对应的实现类,可以这么理解,
    但并不是这样的)。
    binder和现在的进程是同一进程,就返回Stub类,否则返回Stub的代理类Proxy类。至于Proxy。

    android.os.IInterface iin = queryLocalInterface (DESCRIPTOR) 得到的结果返回不同的值。如果iin不空就返回iin,否则就返回Stub的一个代理类。看一下queryLocalInterface (DESCRIPTOR)
    binder和现在的进程是同一进程,就返回Stub类,否则返回Stub的代理类Proxy类。至于Proxy.

    add方法但是并没有真正的去做add方法的业务功能,而是把参数封装到了Parcel 中。通过binder传递给了远程的服务。可以看出绑定远程服务后,要调用远程服务的方法是通过执行这个代理类中的对应方法,
    该方法再通过 mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0)把数据传递到远程服务,等远程服务执行完后再通过代理 _result = _reply.readInt();
    获取到结果返回给我们的程序的。

    Stub中的onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)方法。这个方法作为服务端的方法,负责接收远程客户端通过mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);发送过来的数据并处理的。

    • 1、asInterface(android.os.IBinder obj) 用来根据不同进程返回Stub类自己还是Proxy代理类
    • 2、代理类proxy 用来向远程进程包装数据、发送数据、解析返回结果
    • 3、onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)方法 作为服务端进程调用,用来处理客户端进程传递过来的数据
    • 4、接口的功能方法,Stub继承了接口,这些方法用来处理客户端进程的具体业务。

    相关文章

      网友评论

        本文标题:Service工作中的方方面面

        本文链接:https://www.haomeiwen.com/subject/ogaycqtx.html