zoukankan      html  css  js  c++  java
  • 从零开始学android -- Service

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

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

    一、Service的基础学习

    开发工具:Android Studio

    service有两种开启方式

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

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

    那么开始第一种的学习:

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

    先去注册

    <!-- start service -->
    <service android:name=".OneStartService">
       <intent-filter>
         <!-- service也可以为意图筛选器设置action -->
          <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;
        }
    }

    执行结果:


    第二种bindService的方式

    先注册

     <!-- bind service -->
            <service android:name=".TwoBindService">
                <intent-filter>
                    <!-- service也可以意图筛选器设置action -->
                    <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();
        }
    }

    注意看并没有执行这个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);
            }
        }
    }

    输出结果:

    前面的总结:

    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

    然后

    会自动生成这个

    然后点开文件

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

    然后就会看到

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

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

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

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

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

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

    AndroidManifest文件中注册Service

     <!-- 进程间通信 这里切记用action因为进程间通信bindService的intent需要隐性调起这个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文件拷贝过来

     

    编写布局

    <?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()+"
    weigth:"+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服务

    服务端运行如下:

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

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

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

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

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

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

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

    1.服务端的编写

    新建Person类和Pet类

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

    那我们来看看代码

    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<Person> CREATOR = new Creator<Person>() {
            @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<Pet> CREATOR = new Creator<Pet>() {
            @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文件的时候注意如下

    然后会看到

     

    然后在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<Pet> 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:UsershaseeAppDataLocalAndroidSdkuild-tools26.0.0aidl.exe with arguments {-pC:UsershaseeAppDataLocalAndroidSdkplatformsandroid-25framework.aidl -oC:UsershaseeDesktopServiceStudyappuildgeneratedsourceaidldebug -IC:UsershaseeDesktopServiceStudyappsrcmainaidl -IC:UsershaseeDesktopServiceStudyappsrcdebugaidl -IC:Usershasee.androiduild-cache890e459ffafec9839e8beb2e4ea0ea99ab2db296outputaidl -IC:Usershasee.androiduild-cache3a7b9a84b15c112cfaa0e85b08307e38e561ee1boutputaidl -IC:Usershasee.androiduild-cachedeb88aeb21f748a7f6c26343439049729ce5a2d0outputaidl -IC:Usershasee.androiduild-cacheef709e9bed22c020a9eb4c73748328ec3e992e3outputaidl -IC:Usershasee.androiduild-cache29ab5c17255022a24eee6c85ae7d5bcd29305729outputaidl -IC:Usershasee.androiduild-cache54fcd7b672974422a28610b194daab1c1892d695outputaidl -IC:Usershasee.androiduild-cache6b402181ffa22afe07c2d4036d9974e44081db70outputaidl -IC:Usershasee.androiduild-cache3154617e44e892e8214923aa26ecc08a70ac47fdoutputaidl -IC:Usershasee.androiduild-cache8147f36f39393c0b7a5675d512312b7de4fee51boutputaidl -IC:Usershasee.androiduild-cachee39be704f96b1d8b8ecee15ed08f847413faa180outputaidl -dC:UsershaseeAppDataLocalTempaidl12887145426890846.d C:UsershaseeDesktopServiceStudyappsrcmainaidlmdmservicecomservicestudyaidlIPet.aidl}

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

    这里注意下,有可能你用上面新建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

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

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

    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<Person,List<Pet>> pets = new HashMap<>();
    
        static{
            //初始化pets的集合
            ArrayList<Pet> list1 = new ArrayList<>();
            list1.add(new Pet("狗王",4.1f));
            list1.add(new Pet("旺财",2.1f));
            pets.put(new Person(1,"sun","sun"),list1);
    
            ArrayList<Pet> 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<Pet> 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文件原封不动的铐过来

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

    什么问题呢?

    怎么拿呢?这样拿 

    切记切记:

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

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

     

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

    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<Pet> 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<Pet> pets){
    
            StringBuffer buffer = new StringBuffer();
    
            buffer.append("共有宠物"+pets.size() + "只
    ");
    
            for (int i = 0;i<pets.size() ; i++){
                buffer.append("第"+(i+1)+"只name:"+pets.get(i).getName()+"
    ");
                buffer.append("第"+(i+1)+"只weight:"+pets.get(i).getWeight()+"
    ");
            }
            return buffer.toString();
        }

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

    服务端显示:

    客户端显示:

    扩展阅读: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

  • 相关阅读:
    Dynamic Method Binding in Delphi 动态方法绑定
    Server Memory Server Configuration Options 服务器内存服务配置选项
    最大化系统并发连接数.Windows.reg
    js一行代码解决各种IE兼容问题
    [原创]如果软件在网络磁盘中或移动磁盘中运行时需要解决 exception C0000006 异常问题
    用 ghostscript 转化PDF文件为图片 的参数设置
    GhostScript应用一例:使用GhostScript强行修改加密PDF
    Win7 Win8 Win10取不到机器码的处理办法
    WCAG
    页面被iframe与无刷新更换url方法
  • 原文地址:https://www.cnblogs.com/woaixingxing/p/7561034.html
Copyright © 2011-2022 走看看