Android基础知识

Service工作中的方方面面

2018-12-03  本文已影响0人  ZebraWei

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

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

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

二、如何管理service生命周期

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

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

三、Service和IntentService区别

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

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接口。

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);发送过来的数据并处理的。

上一篇下一篇

猜你喜欢

热点阅读