从零开始学android -- Service

废话不多说了,Service是四大组件之一,是一个后台处理长时间运行在主线程不需要依赖ui界面显示的应用组件,切记不能在service中做耗时操作,会阻塞主线程,要做也要在service中开个子线程做耗时操作

本文的demo会在最后给出,谢谢观看和指正错误。

一、Service的基础学习

开发工具:Android Studio

service有两种开启方式

第一种是直接用startService方式      stopService方式关闭

第二种是bindService方式    unbindService方式解除绑定

 

那么开始第一种的学习:

  第一种比较简单,在Activity中开启就行了,好的我们来看看代码

先去注册


<service android:name=".OneStartService">
   <intent-filter>
     
      <action android:name="com.onestartservice.first"/>
   intent-filter>
 service>

MainActivity 中创建一个Intent

startService  = new Intent();
startService.setAction("com.onestartservice.first"); //startService

这里要注意下,因为在android 5.0之后就上面这段代码会报错如下错误:

    IllegalArgumentException: Service Intent must be explicit    

        经过查找相关资料,发现是因为Android5.0中service的intent一定要显性声明,当这样绑定的时候不会报错

 

如果你可以用显示方式那么就可以改成这样:

startService  = new Intent(this,OneStartService.class);

如果必须要用隐示方式那么就有以下两种方式更改:

1.

startService  = new Intent();
startService.setAction("com.onestartservice.first"); //startService
startService.setPackage(getPackageName());  //加上这句代码

2.

Intent intent = new Intent();  
intent.setAction("com.onestartservice.first");  
Intent eintent = new Intent(createExplicitFromImplicitIntent(this,intent));  //把隐性转化为显性 然后使用eintent就可以了

我这里采用的是1方案

public void startSevice(View view){
   startService(startService); //开启服务
}

public void stopService(View view){
   stopService(startService);//关闭服务
}

再来查看StartService代码

package mdm.service.com.servicestudy;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.util.Log;

/**
 * 第一种开启service的方式
 */

public class OneStartService extends Service {

    private final String TAG = "tag";

    private boolean isStop = false;


    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG,"onCreate");
        //服务中开个子线程进行计数
        new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 0;
                while (!isStop){
                    Log.i(TAG,""+ i++);
                    SystemClock.sleep(1000);
                }
            }
        }).start();
    }

    /**
     * 如果多次startService方法
     * 只会第一次执行一次onCreate方法,再次开启服务的话只会重复调用onStartCommand方法
     * @param intent
     * @param flags
     * @param startId
     * @return
     */
    @Override
    public int onStartCommand(Intent intent,int flags, int startId) {
        Log.i(TAG,"onStartCommand");
        return Service.START_STICKY;
    }

    @Override
    public void onDestroy() {
        isStop = true;
        super.onDestroy();
        Log.i(TAG,"onDestroy");
    }


    //必须实现的方法,创建service必须实现
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

执行结果:

从零开始学android -- Service_第1张图片

 


 

第二种bindService的方式

先注册

 
        <service android:name=".TwoBindService">
            <intent-filter>
                
                <action android:name="com.twobindservice.second"/>
            intent-filter>
        service>

MainActivity

bService = new Intent();
bService.setAction("com.twobindservice.second");//bindService
bService.setPackage(getPackageName());
private TwoBindService.MyBinder myBinder;
private ServiceConnection con = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            Log.i("tag","connection success");
            myBinder = (TwoBindService.MyBinder) service; //用来获取到绑定服务中的数据
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

            Log.e("tag","这里只有在非正常服务断开连接的情况下才会调用这个方法,自己解除绑定是不会调用这个方法的");
        }
    };

    public void bindService(View view){
        bindService(bService,con,Service.BIND_AUTO_CREATE);
    }

    public void unbindService(View view){
        unbindService(con);
    }

    public void getServiceData(View view){
        Toast.makeText(this,"获取bind服务中的数据"+myBinder.getCount(),Toast.LENGTH_SHORT).show();
    }

TwoBindService

package mdm.service.com.servicestudy;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.util.Log;

/**
 * 采用bind的方式
 * bind的方式可以和绑定的访问者进行关联,访问者退出bind的service就会自动关闭,而且bind的方式可以与访问者进行通信。
 * 而采用startService方式开启后就跟访问者没有关联,即使访问者退出了,service仍然运行。
 */

public class TwoBindService extends Service {

    private final String TAG = "tag";

    private boolean isStop = false;
    //我们用来作为通信的数据
    private int count;

    public class MyBinder extends Binder{

        public int getCount(){
            return count;
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG,"onBind");
        return new MyBinder();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG,"onCreate");
        //服务中开个子线程进行计数
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (!isStop) {
                    Log.i(TAG, "" + count++);
                    SystemClock.sleep(1000);
                }
            }
        }).start();
    }

    /**
     * bind服务的方式并不会调用这个方法
     * @param intent
     * @param flags
     * @param startId
     * @return
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG,"onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    //解除bind就会调用这个方法
    @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TAG,"unbind");
        return false;
    }

    @Override
    public void onDestroy() {
        Log.i(TAG,"onDestroy");
        isStop = true;
        super.onDestroy();
    }
}

从零开始学android -- Service_第2张图片

注意看并没有执行这个onStartCommand方法,所以这个方法可以不用重写。

 


 

学完前两种基础的开启service和绑定service后我们来分析分析

1.Service不会专门启动一条单独的线程,Service与它所在的应用位于同一个进程中。

2.Service也不是专门一条新的线程,因此不应该在Service中直接处理耗时的任务。

 

因为有上面的缺点这时我们可以使用IntentService,而它的优点是什么?

1.Intent Service会创建单独的worker线程来处理所有的intent请求

2.IntentService会创建单独的worker线程来处理onHandleIntent()方法实现的代码,因此开发者无须处理多线程问题。

3.当所有请求处理完成后,IntentService会自动停止,因此开发者无需调用stopService()方法.

4.为Service的onBind()方法提供了默认实现,默认实现的onBind()方法返回null。

5.为Service的onStartCommand()方法提供了默认实现,该实现会将请求Intent添加到队列。

所以只需要重写onHandleIntent()方法即可。

话不多说来看代码:

<service android:name=".ThreeIntentService"/>

MainActivity

intentService = new Intent(this,ThreeIntentService.class);
 /**
     * 启动IntentService
     * @param view
     */
    public void startIntentService(View view){

        Toast.makeText(this,"开启IntentService服务:请在开发工具后台查看打印数据消息",Toast.LENGTH_SHORT).show();
        startService(intentService);
    }

ThreeIntentService

package mdm.service.com.servicestudy;

import android.app.IntentService;
import android.content.Intent;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.util.Log;

/**
 * IntentService一个封装过的Service子类
 */
public class ThreeIntentService extends IntentService {
    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public ThreeIntentService(String name) {
        super(name);
    }
    public ThreeIntentService() {
        this("ThreeIntentService"); //必须指定一个name
    }

    /**
     * IntentService 会使用单独的线程来执行该方法的代码
     * 因为普通的Service是运行在主线程中,在不另外开启一个线程的情况下执行耗时操作会阻塞app导致ANR(application not responding)
     * 这时你就可以用IntentService它是service的子类
     * @param intent
     */
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        //因为是单独的线程不会引起主线程的阻塞
        long timeLength = System.currentTimeMillis() + 20 * 1000;

        while (System.currentTimeMillis()< timeLength){
            Log.i("tag","倒计时:" + ((timeLength - System.currentTimeMillis())/1000));
            SystemClock.sleep(1000);
        }
    }
}

输出结果:

从零开始学android -- Service_第3张图片

前面的总结:

1.通过Context的startService()方法:通过该方法启用Service,访问者与Service之间没有关联,即使访问者退出了,Service仍然运行。

2.通过Context的bindService()方法:通过该方法启用Service,访问者与Service之间绑定在了一起,访问者一旦退出,Service也就终止。

3.如果你不需要与Service进行通信或方法数据等进行交换的话那么你就可以直接启用start Service的方式,但记得不用的时候stopService

4.如果你需要与Service进行通信数据交换等,那么你就采用bindService的方式。

5.startService方式多次启动服务只会在第一次执行onCreate方法之后启动会重复调用onStartCommand方法。

6.bindService方式多次启动服务的话只会执行一次onbind方法,不会重复执行某个方法。

 

二、进阶知识Service的进程间通信AIDL (Android Interface definition language)

这里我要说下aidl利用的是是bindService的方式,这也说明了为什么上面基础只是说数据交换用的是bind服务了。

1.aidl定义接口的源代码必须以.aidl结尾。

2.aidl接口中用到的数据类型,除了基本类型,String,List,Map,CharSequence之外,其他类型全部都需要导包,即使他们再同一个包中也需要导包。

 

那么我们先来创建aidl文件

首先在自己要创建的包上右键new --> aidl

从零开始学android -- Service_第4张图片

然后

从零开始学android -- Service_第5张图片

会自动生成这个

从零开始学android -- Service_第6张图片

然后点开文件

从零开始学android -- Service_第7张图片

 然后确认包名和工程名一致后点击make project

从零开始学android -- Service_第8张图片

然后就会看到

从零开始学android -- Service_第9张图片

打开这个文件 里面其实可以分析下,里面有俩个class 一个是Stub 一个是Proxy 

里面有些方法会在客户端用到比如这个方法asInterface(IBinder obj)将服务端的Binder对象转成客户端的所需的AIDL对象。

1.若客户端与服务端在同一进程则返回服务端的Stub本身。

2.若客户端与服务端在不同进程则返回的是Stub.proxy对象。

从零开始学android -- Service_第10张图片

到这里我们的AIDL文件算是全部创建好了,那么就开始下一步吧~

  既然是跨进程通信,那么就类似于C/S了,接下来我们就正是开始写服务端的代码

AndroidManifest文件中注册Service

 
        <service android:name=".AidlService">
            <intent-filter>
                <action android:name="com.aildservice.three"/>
            intent-filter>
        service>

 

 AidlService代码

package mdm.service.com.servicestudy;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

import java.util.Timer;
import java.util.TimerTask;

/**
 * Created by  on 2017/9/20.
 */

public class AidlService extends Service {


    private String color;
    private double weight;

    private String[] colors = {
            "black",
            "blue",
            "red",
            "yellow"
    };

    private double[] weights = {
            18.2,
            19.8,
            10,
            28.6
    };

    private Timer mTimer = new Timer();

    private MyAidlBinder myAidlBinder = new MyAidlBinder();
    @Override
    public void onCreate() {
        super.onCreate();
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                int rand = (int) (Math.random() * 4);
                color = colors[rand];
                weight = weights[rand];

                Log.i("tag","-----------" + rand);

            }
        },0,1000); // 0:运行这段代码0秒后执行run方法;1000:每隔1秒执行一次run方法
    }
    //这里我们看到我们继承的不再是Binder而是Stub了,然后实现了我们写的接口方法
    public class MyAidlBinder extends ICat.Stub{
        @Override
        public String getColor() throws RemoteException {
            return color;
        }
        @Override
        public double getWeight() throws RemoteException {
            return weight;
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return myAidlBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i("tag","onUnbind");
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        Log.i("tag","onDestroy");
        mTimer.cancel();
        super.onDestroy();
    }
}

 

那么服务端我们算是建好了,那么开始客户端代码的编写吧

首先我们新建一个工程,并把服务端的aidl文件拷贝过来

 从零开始学android -- Service_第11张图片

编写布局

xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="绑定ServiceStudy中的AidlService"
        android:onClick="bindService"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="获取服务端数据"
        android:onClick="getServiceData"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="解除绑定ServiceStudy中的AidlService"
        android:onClick="unbindService"/>

LinearLayout>

 

MainActivity

package mdm.client.com.cilentstudy;

import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

import mdm.service.com.servicestudy.ICat;

public class MainActivity extends AppCompatActivity {


    Intent aidlIntent;

    private ICat mICat;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        aidlIntent = new Intent();
        aidlIntent.setAction("com.aildservice.three"); //这里要注意下我们服务端代码AndroidManiFest.xml文件中配置的action
        aidlIntent.setPackage("mdm.service.com.servicestudy");  //这里设置的包切记是服务端Service的包名
    }


    private ServiceConnection con = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Toast.makeText(MainActivity.this,"连接服务成功",Toast.LENGTH_SHORT).show();

            //获取服务数据 这里就不能随便获取到对象了 我在上文中也提到了这个方法,可以回看下
            mICat = ICat.Stub.asInterface(service);

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            //意外中断会调用这个方法,非意外中断,比如自己解除并不会调用这个方法
            Toast.makeText(MainActivity.this,"意外断开连接",Toast.LENGTH_SHORT).show();
        }
    };
    /**
     * 绑定另外一个app内的服务
     * @param view
     */
    public void bindService(View view){

        Toast.makeText(this,"绑定成功",Toast.LENGTH_SHORT).show();
        //绑定远程服务
        bindService(aidlIntent,con, Service.BIND_AUTO_CREATE);
    }

    /**
     * 获取服务端数据
     * @param view
     */
    public void getServiceData(View view){
        try{
            if(mICat != null) {
                Toast.makeText(this, "color:"+mICat.getColor()+"\nweigth:"+mICat.getWeight(), Toast.LENGTH_SHORT).show();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 解除绑定
     * @param view
     */
    public void unbindService(View view){
        Toast.makeText(this,"解除绑定",Toast.LENGTH_SHORT).show();
        unbindService(con);
    }
}

 

 然后调试代码,先打开服务端的app然后再运行客户端bind服务

服务端运行如下:

从零开始学android -- Service_第12张图片

客户端这边就直接获取数据就可以了。

以上就是简单数据的跨进程间通信。

接下来我们看看如何使用自定义类型数据进行进程间通信(传递复杂数据的AIDL Service)

aidl支持的类型有限,但我们有时需要复杂的数据传输怎么办?那么我们就可以看看下面的了

其中aidl除了基本的类型支持以外还支持实现了Parcelable接口的自定义的类,那么我们就开始吧

接下来我们来说明下我们想实现的逻辑

一个主人 有几只宠物,我们就实现客户端通过传入主人信息来从服务端获取宠物信息。

1.服务端的编写

新建Person类和Pet类

为了结构清晰我们看下目录

从零开始学android -- Service_第13张图片

那我们来看看代码

Person.java

package mdm.service.com.servicestudy.aidl;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * 主人类
 */
public class Person implements Parcelable {

    private Integer id;
    private String name;
    private String pass;
    public Person(){

    }
    public Person(Integer id,String name,String pass){
        super();
        this.id = id;
        this.name = name;
        this.pass = pass;
    }
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPass() {
        return pass;
    }

    public void setPass(String pass) {
        this.pass = pass;
    }

    /**
     * 下面增加hasCode和equals是为了保证不会有重复数据
     */
    
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;

        result = prime * result + ((name == null)?0:name.hashCode());
        result = prime * result + ((pass == null)?0:pass.hashCode());

        return result;
    }

    @Override
    public boolean equals(Object obj) {

        if(obj == null){
            return false;
        }
        if(obj == this){
            return true;
        }
        if(getClass() != obj.getClass()){
            return false;
        }

        Person other = (Person) obj;

        if(name == null){
            if(other.name != null){
                return false;
            }
        }else if(!name.equals(other.name)){
            return false;
        }

        if(pass == null){
            if(other.pass != null){
                return false;
            }
        }else if(!pass.equals(other.pass)){
            return false;
        }
        return true;
    }

    /****************************Parcelable****************************/
    /**
     * 实现Parcelable接口必须实现的方法
     */
    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * 实现Parcelable接口必须实现的方法
     * @param dest
     * @param flags
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        //把该对象所包含的数据写道Parcel中
        dest.writeInt(id);
        dest.writeString(name);
        dest.writeString(pass);
    }

    /**
     * 这里是自动生成的
     * 添加一个静态成员,名为CREATOR,该对象实现了Parcelable.Creator接口
     */
    public static final Creator CREATOR = new Creator() {
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in.readInt(),in.readString(),in.readString());
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };
}

 

 

Pet.java 

package mdm.service.com.servicestudy.aidl;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * 宠物类
 */

public class Pet implements Parcelable {

    private String name;
    private float weight;

    public Pet(){}

    public Pet(String name,float weight){
        this.name = name;
        this.weight = weight;
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getWeight() {
        return weight;
    }

    public void setWeight(float weight) {
        this.weight = weight;
    }



    @Override
    public int hashCode() {
        int prime = 31;
        int result = 1;
        result = prime * result  + ((name == null)?0:name.hashCode());
        result = (int) (prime * result + ((weight == 0? 0: weight)));
        return result;
    }

    @Override
    public boolean equals(Object obj) {

        if(obj == null){
            return false;
        }
        if(obj == this){
            return true;
        }
        if(obj.getClass() != getClass()){
            return false;
        }
        Pet other = (Pet) obj;

        if(name == null){
            if(other.name != null){
                return false;
            }
        }else if(!name.equals(other.name)){
            return false;
        }

        if(weight == 0){
            if(other.weight != 0){
                return false;
            }
        }else if(weight != other.weight){
            return false;
        }
        return true;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeFloat(weight);
    }

    public static final Creator CREATOR = new Creator() {
        @Override
        public Pet createFromParcel(Parcel in) {
            return new Pet(in.readString(),in.readFloat());
        }

        @Override
        public Pet[] newArray(int size) {
            return new Pet[size];
        }
    };
}

 

编写好了之后我们就准备新建AIDL文件.

注意我们这里单独用了一个aidl包存放了person和pet类所以新建AIDL文件的时候注意如下

从零开始学android -- Service_第14张图片

从零开始学android -- Service_第15张图片

然后会看到

 从零开始学android -- Service_第16张图片

然后在IPet.aidl文件中写下代码

// IPet.aidl
package mdm.service.com.servicestudy.aidl;

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

import mdm.service.com.servicestudy.aidl.Pet; //切记这里要记得导包
import mdm.service.com.servicestudy.aidl.Person;   //这里也要记得导包

interface IPet {
    List getPets(in Person owner);  //表示数据流向这里只能是in 不写编译会报错
}

 

然后到这里请注意:如果是上面的简单AIDL方法的话,我们这时就应该是直接make project了,但自定义类型数据不行,make project 编译报错,有如下错误

Error:Execution failed for task ':app:compileDebugAidl'.
> java.lang.RuntimeException: com.android.ide.common.process.ProcessException: Error while executing process C:\Users\hasee\AppData\Local\Android\Sdk\build-tools\26.0.0\aidl.exe with arguments {-pC:\Users\hasee\AppData\Local\Android\Sdk\platforms\android-25\framework.aidl -oC:\Users\hasee\Desktop\ServiceStudy\app\build\generated\source\aidl\debug -IC:\Users\hasee\Desktop\ServiceStudy\app\src\main\aidl -IC:\Users\hasee\Desktop\ServiceStudy\app\src\debug\aidl -IC:\Users\hasee\.android\build-cache\890e459ffafec9839e8beb2e4ea0ea99ab2db296\output\aidl -IC:\Users\hasee\.android\build-cache\3a7b9a84b15c112cfaa0e85b08307e38e561ee1b\output\aidl -IC:\Users\hasee\.android\build-cache\deb88aeb21f748a7f6c26343439049729ce5a2d0\output\aidl -IC:\Users\hasee\.android\build-cache\0ef709e9bed22c020a9eb4c73748328ec3e992e3\output\aidl -IC:\Users\hasee\.android\build-cache\29ab5c17255022a24eee6c85ae7d5bcd29305729\output\aidl -IC:\Users\hasee\.android\build-cache\54fcd7b672974422a28610b194daab1c1892d695\output\aidl -IC:\Users\hasee\.android\build-cache\6b402181ffa22afe07c2d4036d9974e44081db70\output\aidl -IC:\Users\hasee\.android\build-cache\3154617e44e892e8214923aa26ecc08a70ac47fd\output\aidl -IC:\Users\hasee\.android\build-cache\8147f36f39393c0b7a5675d512312b7de4fee51b\output\aidl -IC:\Users\hasee\.android\build-cache\e39be704f96b1d8b8ecee15ed08f847413faa180\output\aidl -dC:\Users\hasee\AppData\Local\Temp\aidl12887145426890846.d C:\Users\hasee\Desktop\ServiceStudy\app\src\main\aidl\mdm\service\com\servicestudy\aidl\IPet.aidl}

为了避免这个我们得加上一步操作

从零开始学android -- Service_第17张图片

这里注意下,有可能你用上面新建aidl的方式新建不了,需要自己手动新建file文件然后改个后缀.aidl就行了,接下来然后

查看里面写了什么代码

Person.aidl

// IPet.aidl
package mdm.service.com.servicestudy.aidl;  //注意这个包名是aidl包

parcelable Person;  //parcelable 是我们类实现接口Parcelable的名字加上我们的类名,简单吧

 

 Pet.aidl

// IPet.aidl
package mdm.service.com.servicestudy.aidl;

parcelable Pet;

 

经过这一步后,然后去点击make project

从零开始学android -- Service_第18张图片

然后你会很高兴的看到没有错误了,自动生成了文件

从零开始学android -- Service_第19张图片

这时候我们就可以去编写我们的Service了

从零开始学android -- Service_第20张图片

CompleAidlService.java

package mdm.service.com.servicestudy;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import mdm.service.com.servicestudy.aidl.IPet;
import mdm.service.com.servicestudy.aidl.Person;
import mdm.service.com.servicestudy.aidl.Pet;

/**
 * 使用自定义类型的服务端代码
 */
public class ComplexAidlService extends Service {

    private MyPetBinder mBinder = new MyPetBinder();

    private static Map> pets = new HashMap<>();

    static{
        //初始化pets的集合
        ArrayList list1 = new ArrayList<>();
        list1.add(new Pet("狗王",4.1f));
        list1.add(new Pet("旺财",2.1f));
        pets.put(new Person(1,"sun","sun"),list1);

        ArrayList list2 = new ArrayList<>();
        list2.add(new Pet("金毛",5.1f));
        list2.add(new Pet("泰迪",3.0f));
        pets.put(new Person(2,"ba","ba"),list2);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("tag","onCreate");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i("tag","onBind");
        return mBinder;
    }

    private class MyPetBinder extends IPet.Stub{

        @Override
        public List getPets(Person owner) throws RemoteException {
            //通过主人获取宠物
            return pets.get(owner);
        }
    }


    @Override
    public boolean onUnbind(Intent intent) {
        Log.i("tag","onUnbind");
        return true;
    }

    @Override
    public void onDestroy() {
        Log.i("tag","onDestroy");
        super.onDestroy();
    }
}

 最后记得注册下


        <service android:name=".ComplexAidlService">
            <intent-filter>
                <action android:name="com.aidlservice.four"/>
            intent-filter>
        service>

 

 那么我们服务端就ok了.

2.客户端的编写

和之前一样我们先吧aidl文件原封不动的铐过来

从零开始学android -- Service_第21张图片

然后我们兴高采烈的去在client中make project ,然后又出问题了

什么问题呢?

从零开始学android -- Service_第22张图片

怎么拿呢?这样拿 

切记切记:

1.从服务端拿过来是什么样就是什么样,不能随意更改。

2.包的完整名称从服务端那边是什么包名就是什么包名不能随便更改否则会无效。(这里我们从服务端拿过来的包名是: mdm.service.com.servicestudy.aidl这个包下

 从零开始学android -- Service_第23张图片

然后上面的错误就会消失了,接下来我们就正式编写客户端代码了

activity_main.xml

<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="绑定ServiceStudy中的ComplexAidlService"
        android:onClick="bindFService"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="获取服务端数据"
        android:onClick="getFServiceData"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="解除绑定ServiceStudy中的ComplexAidlService"
        android:onClick="unbindFService"/>


    <TextView
        android:id="@+id/show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:textColor="@android:color/black"
        android:layout_gravity="center"
        android:text="用来显示获取到的数据"/>

 

MainAcitivity.java

fAidlIntent = new Intent();
fAidlIntent.setAction("com.aidlservice.four");
fAidlIntent.setPackage("mdm.service.com.servicestudy");

textView = (TextView) findViewById(R.id.show);

 

private IPet mIPet;
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Toast.makeText(MainActivity.this,"连接服务成功",Toast.LENGTH_SHORT).show();
            mIPet = IPet.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            //意外中断会调用这个方法,非意外中断,比如自己解除并不会调用这个方法
            Toast.makeText(MainActivity.this,"意外断开连接",Toast.LENGTH_SHORT).show();
        }
    };

    /**
     * 绑定另外一个app内的服务
     * @param view
     */
    public void bindFService(View view){

        Toast.makeText(this,"绑定成功",Toast.LENGTH_SHORT).show();
        //绑定远程服务
        bindService(fAidlIntent,conn, Service.BIND_AUTO_CREATE);
    }

    /**
     * 获取服务端数据
     * @param view
     */
    public void getFServiceData(View view){
        try{
            if(mIPet != null) {
                //从服务端方法 通过主任信息获取宠物集合
                List pets = mIPet.getPets(new Person(1,"sun","sun"));

                textView.setText(getString(pets));
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 解除绑定
     * @param view
     */
    public void unbindFService(View view){
        Toast.makeText(this,"解除绑定",Toast.LENGTH_SHORT).show();
        unbindService(conn);
    }

    /**
     * 解析list数据
     * @param pets
     * @return
     */
    private String getString(List pets){

        StringBuffer buffer = new StringBuffer();

        buffer.append("共有宠物"+pets.size() + "只\n");

        for (int i = 0;i){
            buffer.append("第"+(i+1)+"只name:"+pets.get(i).getName()+"\n");
            buffer.append("第"+(i+1)+"只weight:"+pets.get(i).getWeight()+"\n");
        }
        return buffer.toString();
    }

 

接下来我们来看看运行结果:

服务端显示:

从零开始学android -- Service_第24张图片

客户端显示:

从零开始学android -- Service_第25张图片

 

 

扩展阅读:onStartCommand方法中的Intent为什么有时候会为空?

                你真的理解AIDL中的in,out,inout么?

 

本文demo:  

  1.Service基础学习demo https://pan.baidu.com/s/1kVpBfmR

  2.Service包含AIDL进阶知识的demo https://pan.baidu.com/s/1pLeDHXL

 

转载于:https://www.cnblogs.com/woaixingxing/p/7561034.html

你可能感兴趣的:(从零开始学android -- Service)