【Android】Service深度、高质量学习

0 Service先导:线程、IntentService、Binder等

  1. service和线程的区别和场景
    线程是程序执行的最小单元,分配CPU的最小单位;
    Android中,线程分为主线程和工作线程;主线程主要负责UI界面的绘制和时间响应操作,为了保证应用的响应能力,一般不在主线程做耗时操作;
    service是四大组件之一,运行在主线程中。

  2. 如何管理service生命周期
    不管用什么方法启动service,都会调用onCreate(),onDestroy()方法

  3. service和IntentService的区别
    service跑在应用程序的主线程,不建议在service中做耗时操作,可能会造成性能问题;
    为了解决这个问题,提供了IntentService,IntentService内部有个工作线程,启动IntentService之后,会不断开启并轮询,任务执行完后会自动停止,不像service,一旦开启若不关闭会一直存在。

  4. 启动服务和绑定服务先后次序问题
    服务的状态有两种:启动和绑定;先启动再绑定,和先绑定再启动,两种启动方式是有区别的;
    启动服务,会无限期运行下去;
    Android系统,只会为service创建一个实例对象,不管启动服务还是绑定服务,操作的都是同一个service。

  5. 序列化:Parcelable 和 Serializable
    为了把对象的状态保存下来,存到磁盘中,会用到序列化

  6. Binder
    是service的核心和难点
    多进程之间的通信都是依赖于底层的Binder IBC通信,Binder机制也是一种远程进程通信方案

1. service和Thread的区别和场景

  1. Thread:程序执行的最小单元,是分配CPU的基本单位
  2. Service是Android的一种机制,运行在主线程上;和应用程序运行在一个进程中,进程名称一般用包名表示;service与其他组件之间的通信,如service和Activity通信,类似于client客户端和server服务端之间的通信,是轻量级的进程间通信的机制,通信的载体是Binder,在Linux层交换信息;

Thread生命周期

  1. 新建 new Thread创建Thread类的实例的时候,线程被新建,可能进入未启动状态
  2. 就绪 线程已经被启动了,正在等待分配给我们的CPU时间片,线程在就绪队列里排队等待CPU资源
  3. 运行 线程已经获得CPU资源,正在执行任务,这是调用run方法,除非线程自动放弃CPU资源,或者这时有优先级更高的线程进入,否则它会一直运行直到结束
  4. 死亡 线程执行完毕,或被其他线程杀死,线程进入死亡状态,此时线程不可能再进入就绪状态
  5. 阻塞 由于某种原因导致这个线程让出CPU,并暂停自己的执行,这时线程会进入阻塞状态

非常非常致命的缺点:无法控制!!!!

两者都在后台执行任务,都可以没有界面;
一般都是在Activity中开启子线程,但线程的运行是独立于Activity的,当一个Activity被关闭,不再持有该线程的引用,无法控制这个thread。如果没有主动停止子线程的run方法,那该thread仍会运行。

如果Thread需要连续不停地每隔一段时间就连接服务器一次,此时如果Activity被杀死,那么这个thread就变成了野线程;此时需要用到service。可以开启service,可以在service内部运行并控制thread,service会一直运行在后台,不会丢失对thread的控制。

Service生命周期

系统调用时,在主线程中运行的

  1. onCreate()
  2. onStart()
  3. onDestroy()
  4. onBind()
  5. onUnbind()

2 如何管理service生命周期

不管用什么方法启动service,都是从调用onCreate(),到调用onDestroy(),形成的完整生命周期。就是都会调用这两个方法。如视频播放器,在onCreate中创建用于视频播放的线程,而在onDestroy中停止该线程。

在这里插入图片描述

左边通过startService启动的服务,右边通过bindService启动的服务;

  1. startService
    开启service服务,onCreate—》onStartCommand,onCreate方法只调用1次,onStartCommand方法会调用多次,具体次数与startService调用次数相同;

  2. stopService
    关闭service服务,onDestroy,如果一个service被启动且被绑定了,就是又调用了bindService方法,在没有解绑的前提下,调用stopService也无法解绑该服务;

  3. bindService
    绑定service服务,onCreate—》onBind;

  4. unBindService
    解绑服务,onUnbind—》onDestroy。

  5. 启动服务,和绑定服务的结合体:
    首先调用startService启动服务,而后用户需要控制后台信息时,该Activity可以调用bindService绑定到该服务。

3. service和IntentService的区别

service跑在应用程序的主线程中,所以不建议在service中做耗时操作,否则可能会造成ANR问题;

为了解决这个问题,提供了IntentService;

IntentService内部有一个工作线程HandlerThread来处理耗时操作。启动IntentService之后,会不断开启并轮询,任务执行完后会自动停止,不像service,一旦开启若不关闭会一直存在。

@Override
    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是异步线程处理类,mServiceLooper与这个异步线程处理类绑定。相当于把HandlerThread持有到Looper类对象转交给了mServiceHandler,这样mServiceHandler就持有了异步线程的Looper(mServiceLooper),就可以执行异步任务了。

onCreate—》onStartCommand–》onStart

 private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

onHandleIntent方法需要我们实现

总结

  1. IntentService是继承并处理异步请求的一个类;

  2. 内有一个工作线程HandlerThread来处理耗时操作;

  3. 每次执行一个后台任务就必须启动一个IntentService;**IntentService内部则是通过消息的方式发送给HandlerThread来处理,然后由Handler中的Looper来去轮询处理消息;**所以说,IntentService后台任务执行是顺序的,当有多个任务同时存在的时候,这些后台任务会按照外部调用的顺序依次执行。

4. 启动服务和绑定服务先后次序问题

服务的状态有两种:启动和绑定;先启动再绑定,和先绑定再启动,两种启动方式是有区别的;
启动服务,会无限期运行下去;
Android系统,只会为service创建一个实例对象,不管启动服务还是绑定服务,操作的都是同一个service。

4.1 绑定服务3步骤:

1
首先实现onBind方法,返回被绑定的Service的当前实例,即MyBind对象。

创建一个服务,继承自Service,并重写onBind方法。
在onBind方法中返回一个实例,就是Binder,这个实例也是Service实例,可以让Activity绑定,从而获得这个实例中的接口(getService方法)。

public class MyService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("--onBind()--");
        return new MyBinder();
    }

    public class MyBinder extends Binder {
        MyService getService() {
            return MyService.this;
        }
    }
}

2
在Activity中实现一个ServiceConnection,Service和其他组件的链接要表示成一个ServiceConnection接口,如果想要和其他组件链接,就要让MyServiceConn去实现ServiceConnection接口,建立这个链接后,可以重写两个方法,来获得Service实例的引用。

class MyServiceConn implements ServiceConnection {
    // 服务被绑定成功之后执行
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        //service为onBind方法返回的Service实例
        binder = (MyService.MyBinder) service;
    }

    // 服务奔溃或者被杀掉执行
    @Override
    public void onServiceDisconnected(ComponentName name) {
    }
}

3
通过bindService执行绑定Service的工作

public class ServiceActivity extends Activity {

    private Button mBtn;
    public Intent intent;
    public MyServiceConn myServiceConn;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBtn = findViewById(R.id.btn);
        intent = new Intent(this,MyService.class);
        mBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                bindService(intent, myServiceConn, Context.BIND_AUTO_CREATE);

//                startService(intent);
            }
        });
    }

    MyService.MyBinder binder = null;
    class MyServiceConn implements ServiceConnection {
        // 服务被绑定成功之后执行
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //service为onBind方法返回的Service实例
            binder = (MyService.MyBinder) service;
        }

        // 服务奔溃或者被杀掉执行
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    }
}

4.2 启动服务1步骤

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mBtn = findViewById(R.id.btn);
    intent = new Intent(this,MyService.class);
    mBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // bindService(intent, myServiceConn, Context.BIND_AUTO_CREATE);

            startService(intent);  这就是以启动服务的方式启动了服务
        }
    });
}

启动服务和绑定服务顺序

  1. 先绑定再启动:转换成启动状态运行

  2. 先启动再绑定:仍然是启动状态运行,生命周期和启动服务一样长

绑定服务依托于Activity,启动服务依托于自己的service和内存的情况。

总结

  1. 启动服务的优先级比绑定服务高;
  2. 服务在其托管线程的主线程中运行(UI),耗时操作放子线程中进行;

5. 序列化:Parcelable 和 Serializable

为了把对象的状态保存下来,存到磁盘中,会用到序列化

  1. 序列化:内存中的对象—>磁盘
  2. 反序列化:磁盘中的对象—>内存

5.1 Serializable

用serialVersionUID辅助,一个类对应一个serialVersionUID。反序列化时,先判断是否是当前类的对象,再决定是否可以反序列化。

实现简单,内存开销大。

import java.io.Serializable;
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;
    }
}

5.2 Parcelable

为了解决Serializable内存开销大的问题,安卓提供了Parcelable。

Parcelable代码实现麻烦,但是内存开销小。

  1. 用writeToParcel方法,将对象映射成Parcel对象;
  2. 通过createFromParcel方法,将Parcel对象映射成我们的对象
import android.os.Parcel;
import android.os.Parcelable;

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];
        }
    };
}

5.3 Parcelable 和 Serializable的区别

  1. 实现上的差异:
    Serializable只需通过Serializable接口,给对象添加一个UID,系统会自动将其序列化;
    Parcelable不只需要实现Parcelable接口,还需要在类中添加一个静态成员变量;要去实现读写的抽象方法,包括writeToParcel和createFromParcel等等。

  2. 效率上的对比:
    在内存间传递数据,推荐使用Parcelable。安卓端的数据传递,包括Activity,AIDL等的数据传递。
    序列化到存储设备,推荐使用Serializable。

6. Binder

是service的核心和难点
多进程之间的通信都是依赖于底层的Binder IBC通信,Binder机制也是一种远程进程通信方案

6.1 Binder应用:AIDL—进程间通信机制IPC

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

6.1.1 创建AIDL:实体对象,新建AIDL文件,make工程

在main下新建AIDL,创建下列两个类。然后make,生成debug中的IMyAidl文件。

IMyAidl.aidl:

// IMyAidl.aidl
package com.example.jiajiemu.test;

import com.example.jiajiemu.test.bean.Person;

// Declare any non-default types here with import statements

interface IMyAidl {
    void addPerson(in Person person);

    List<Person> getPersonList();
}

Person类:

// Person.aidl
package com.example.jiajiemu.test.bean;

// Declare any non-default types here with import statements

parcelable Person;

6.1.2 服务端:新建Service,创建Binder对象,定义方法

服务端实现了接口,并在onBind方法中返回一个mIBinder对象,该对象属于IBinder类型。客户端拿到这个对象,就可以调用远程的方法。

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 void addPerson(Person person) throws RemoteException {
            mPersons.add(person);
        }

        @Override
        public List<Person> getPersonList() throws RemoteException {
            return mPersons;
        }
    };

    /**
     * 客户端与服务端绑定时的回调,返回 mIBinder 后客户端就可以通过它远程调用服务端的方法,即实现了通讯
     *
     * @param intent
     * @return
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        mPersons = new ArrayList<>();
        Log.d(TAG, "MyAidlService onBind");
        return mIBinder;
    }
}

6.1.3 客户端:实现ServiceConnection接口,BindService

首先,实现ServiceConnection接口。
连接后拿到 Binder,转换成 AIDL,在不同进程会返回个代理。
通过bindService绑定服务,好进行进程间通信。
最后,可以调用服务器定义好的方法,如:mAidl.addPerson(person);

public class MyServiceActivity extends Activity {

    private Button mBtn;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBtn = findViewById(R.id.btn);
        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();
        }
    }
}

6.2 Binder机制通信:AIDL生成java文件分析

AIDL帮我们生成了Binder对象,同时也生成了跨平台接口的转换类Stub,以及在不同进程时客户端拿到的代理Proxy。

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: E:\\AndroidStudio_Projects\\Android_Study\\AndroidBasic\\Review\\coding-101\\app\\src\\main\\aidl\\com\\example\\jiajiemu\\test\\IMyAidl.aidl
 */
package com.example.jiajiemu.test;
// Declare any non-default types here with import statements

public interface IMyAidl extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.example.jiajiemu.test.IMyAidl {
        private static final java.lang.String DESCRIPTOR = "com.example.jiajiemu.test.IMyAidl";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.example.jiajiemu.test.IMyAidl interface,
         * generating a proxy if needed.
         */
        public static com.example.jiajiemu.test.IMyAidl asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.jiajiemu.test.IMyAidl))) {
                return ((com.example.jiajiemu.test.IMyAidl) iin);
            }
            return new com.example.jiajiemu.test.IMyAidl.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_addPerson: {
                    data.enforceInterface(DESCRIPTOR);
                    com.example.jiajiemu.test.bean.Person _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.example.jiajiemu.test.bean.Person.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addPerson(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getPersonList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.example.jiajiemu.test.bean.Person> _result = this.getPersonList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.example.jiajiemu.test.IMyAidl {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public void addPerson(com.example.jiajiemu.test.bean.Person person) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((person != null)) {
                        _data.writeInt(1);
                        person.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public java.util.List<com.example.jiajiemu.test.bean.Person> getPersonList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.example.jiajiemu.test.bean.Person> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.example.jiajiemu.test.bean.Person.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public void addPerson(com.example.jiajiemu.test.bean.Person person) throws android.os.RemoteException;

    public java.util.List<com.example.jiajiemu.test.bean.Person> getPersonList() throws android.os.RemoteException;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值