zoukankan      html  css  js  c++  java
  • Android AIDL Service 跨进程传递复杂数据

    黑夜

    黑夜给了我黑色的眼睛,我却用它寻找光明~

    传值方式

    AIDL是同意跨进程传递值的,一般来说有三种方式:
    - 广播;这样的算是比較常见的一种方式了,传递小数据不错
    - 文件;这个是保存到文件里。然后读取,传递大数据不错
    - Service Bind模式。这个算是居中的一种方式,只是效率要高的多,唯一麻烦的是编写代码较为麻烦。

    特别是复杂类型数据传递麻烦。
    其是,另一些其它的办法进行数据传递。另外传递也并非仅仅能够使用一种,能够採用几种结合的方式进行。
    今天要说的就是Service Bind进行复杂数据的传递。


    传递类型

    在AIDL中之所以使用Bind进行传递会比較麻烦是由于:其在跨进程的情况下仅仅同意传递例如以下类型数据:
    - String
    - CharSequence
    - android.os.Parcelable
    - java.util.List
    - java.util.Map

    尽管能够使用 List与Map可是其类型一样不能使用复杂类型;当然上面 int、long、bool就没有单独写出来了。
    如过要进行复杂数据传递,如传递User类的实例,此时就要使用Parcelable来辅助完毕。

    简单流程

    Created with Raphaël 2.1.0A进程数据User在A进程把User打包为Parcelable在B进程把Parcelable解包为UserB进程数据User

    其调用方式依旧为:绑定服务->得到目标服务的Binder->调用相应方法


    跨进程传递数据麻烦就在于打包/解包Parcelable的操作。

    目标:开启一个独立进程的服务,在主进程中绑定目标服务。调用服务的方法。
    实现:将须要进行复杂传递的数据类,继承Parcelable,并实现当中的序列化与反序列化方法。

    传递类

    传递类包括两个:User.java、User.aidl
    当中java类是详细的实现,aidl文件仅仅仅仅是用于对java文件的声明。告知进程其能够看作Parcelable处理。

    User.Java

    package net.qiujuer.sample.service.bean;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    
    import java.util.UUID;
    
    /**
     * Created by qiujuer on 15/7/15.
     */
    public class User implements Parcelable {
        private UUID id;
        private int age;
        private String name;
    
        public User(int age, String name) {
            this.age = age;
            this.name = name;
            this.id = UUID.randomUUID();
        }
    
        protected User(Parcel in) {
            // Id
            long m = in.readLong();
            long l = in.readLong();
            id = new UUID(m, l);
    
            age = in.readInt();
            name = in.readString();
        }
    
        public static final Creator<User> CREATOR = new Creator<User>() {
            @Override
            public User createFromParcel(Parcel in) {
                return new User(in);
            }
    
            @Override
            public User[] newArray(int size) {
                return new User[size];
            }
        };
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            // ID
            long m = id.getMostSignificantBits();
            long l = id.getLeastSignificantBits();
            dest.writeLong(m);
            dest.writeLong(l);
    
            dest.writeInt(age);
            dest.writeString(name);
        }
    
        @Override
        public String toString() {
            return "Id:" + id.toString() + " Age:" + age + " Name:" + name;
        }
    }

    在类中,包括三个属性。两个基本类型,一个UUID,对于基本类型能够直接序列化。而UUID则不能,此时两个方案。一种是把UUID看作String进行处理。当解包时则把String转换为UUID就可以。
    另外一种则是得到当中重要的作用,各自是两个long值。然后对long值进行传递,并反序列化。

    在类中,我们实现了Parcelable接口,则须要完毕两个方法与一个静态值操作。
    - describeContents 为描写叙述方法。通常返回0
    - writeToParcel 详细的写入操作,在这里对须要传输的数据进行写入。请一定须要注意的是写入顺序则决定了读取顺序


    - CREATOR 此静态属性,则是为了反编译时使用,在Parcelable进行反编译时将会调用该属性,所以名称写法基本是固定不变的。
    - User 在该类的构造函数中。我们对其进行了反序列化读取数据操作。

    User.aidl

    // User.aidl
    package net.qiujuer.sample.service.bean;
    
    parcelable User;

    在该文件里。仅仅须要两行代码就OK,一个指定包名,一个为parcelable申明。

    # Service类
    这个部分主要包括两个文件。一个AIDL申明文件

    IServiceAidlInterface.aidl

    // IServiceAidlInterface.aidl
    package net.qiujuer.sample.service;
    import net.qiujuer.sample.service.bean.User;
    
    
    // Declare any non-default types here with import statements
    
    interface IServiceAidlInterface {
        void addAge();
        void setName(String name);
        User getUser();
    }

    在该文件里,定义了一个接口。当中有一个方法getUser(),该方法返回服务中的User类的实例;当然须要使用该类所以须要加上import net.qiujuer.sample.service.bean.User;

    IndependentService.java

    package net.qiujuer.sample.service;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.os.RemoteException;
    
    import net.qiujuer.sample.service.bean.User;
    
    public class IndependentService extends Service {
        private ServiceBinder mBinder;
    
        public IndependentService() {
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            if (mBinder == null)
                mBinder = new ServiceBinder();
            return mBinder;
        }
    
        class ServiceBinder extends IServiceAidlInterface.Stub {
            private User user;
    
            public ServiceBinder() {
                user = new User(21, "XiaoMing");
            }
    
            @Override
            public void addAge() throws RemoteException {
                user.setAge(user.getAge() + 1);
            }
    
            @Override
            public void setName(String name) throws RemoteException {
                user.setName(name);
            }
    
            @Override
            public User getUser() throws RemoteException {
                return user;
            }
        }
    
    }

    该类为详细的服务实现,在该服务中实现了服务接口,并进行了简单实现。

    文件梳理与配置

    文件结构

    这里写图片描写叙述

    独立进程配置

    由于我们须要让服务为独立进程,所以须要在AndroidManifest文件里Service申明的地方加上process属性:

    <?

    xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.qiujuer.sample.service"> <application android:allowBackup="true" android:label="@string/app_name"> <service android:name=".IndependentService" android:enabled="true" android:exported="true" android:permission="1000" android:process=":AidlService" /> </application> </manifest>

    在这里我设置的是:android:process=”:AidlService”

    使用

    在APP Model中。我建立了一个MainActivity,并在当中调用服务的方法。

    核心代码

    public class MainActivity extends AppCompatActivity {
        private final String TAG = this.getClass().getSimpleName();
        private TextView mText;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mText = (TextView) findViewById(R.id.txt_str);
    
            bindService();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            unBindService();
        }
    
        private IServiceAidlInterface mService = null;
    
        private ServiceConnection mConn = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                mService = IServiceAidlInterface.Stub.asInterface(iBinder);
                if (mService != null)
                    run();
                else
                    showText("Bind Error.");
            }
    
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                mService = null;
            }
        };
    
        private void bindService() {
            // UnBind
            unBindService();
    
            Intent intent = new Intent(this, IndependentService.class);
            bindService(intent, mConn, Context.BIND_AUTO_CREATE);
        }
    
        private void unBindService() {
            // Service
            IServiceAidlInterface service = mService;
            mService = null;
            if (service != null) {
                unbindService(mConn);
            }
        }
    
        private void run() {
            User user = null;
            try {
                user = mService.getUser();
                showText(user.toString());
    
                mService.addAge();
                user = mService.getUser();
                showText(user.toString());
    
                mService.setName("FangFang");
                user = mService.getUser();
                showText(user.toString());
    
            } catch (RemoteException e) {
                e.printStackTrace();
                showText(e.toString());
            }
        }
    
        private void showText(String str) {
            Log.d(TAG, str);
            mText.append("
    ");
            mText.append(str);
        }
    }

    打印日志

    MainActivity﹕ Id:cdfb5bb4-7674-4a8a-b754-92256dfea8f4 Age:21 Name:XiaoMing
    MainActivity﹕ Id:cdfb5bb4-7674-4a8a-b754-92256dfea8f4 Age:22 Name:XiaoMing
    MainActivity﹕ Id:cdfb5bb4-7674-4a8a-b754-92256dfea8f4 Age:22 Name:FangFang

    进程

    这里写图片描写叙述

    能够看出,服务的进程的确是独立于主进程的。

    引申

    在这里,我们是传递的一个User。假如传递的User中又包括一个Account类呢?Account中又包括其它的类呢?这个该怎样办?

    有两种办法:
    第一种:使用上面UUID传递相似的方式。得到当中的核心数据,然后传递,在解包时进行还原。
    另外一种:将类也实现Parcelable接口。这样就能完美的攻克了。

    在这里简单写一下另外一种的代码:
    Account.java

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

    User.java
    这里仅仅写修改部分。事实上这些修改的东西。全然能够借助编译工具自己主动生成,你仅仅须要写好属性。然后继承接口。然后让编译工具帮助你完毕接口相应方法就OK。

    public class User implements Parcelable {
        private Account account;
    
        protected User(Parcel in) {
            ...
            account = in.readParcelable(Account.class.getClassLoader());
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            ...
            dest.writeParcelable(account, flags);
        }
    }

    另外须要注意的是。在aidl中申明类。仅仅仅仅须要申明aidl接口中须要传递的类就OK。在这里直接传递的类仅仅有User。所以仅仅须要写一个User.aidl文件就OK,就算User中包括了Account类,但不须要写Account.aidl文件来申明。

    代码

    当然,源代码肯定是会有的:
    AidlService 源代码

    写在最后

    近期没事儿捣鼓了一个APP[UPMiss]。一个简单的生日,纪念日提醒软件。欢迎大家尝鲜。

    UPMiss} 思念你的夏天
    下载地址:

    ========================================================
    作者:qiujuer
    博客:blog.csdn.net/qiujuer
    站点:www.qiujuer.net
    开源库:github.com/qiujuer/Genius-Android
    开源库:github.com/qiujuer/Blink
    转载请注明出处:http://blog.csdn.net/qiujuer/article/details/46885987
    —— 学之开源,用于开源;刚開始学习的人的心态。与君共勉。

    ========================================================

  • 相关阅读:
    SpringMVC 多文件上传
    get传参乱码问题
    springMVC配置
    带参sql$和#的区别(注解)
    java多线程--实现Runnable接口方式
    java复制文件夹及所有子目录和文件
    Angularjs 学习笔记
    springboot 项目 docker化部署
    docker 基础
    Java-马士兵动态代理模式
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/7252214.html
Copyright © 2011-2022 走看看