• Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- AIDL


    服务端:

    最终项目结构:

    这个项目中,我们将用到自定义类CustomData作为服务端与客户端传递的数据。

    Step 1:创建CustomData类

    package com.ldb.android.example.aidl;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    import android.util.Log;
    
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    /**
     * Created by lsp on 2016/9/1.
     */
    public class CustomData implements Parcelable {
    
        private static final String TAG = "CustomData";
    
        private String mName;
        private List<String> mReference;
        private Date mCreated;
    
        public CustomData(){
            mName = "";
            mReference = new ArrayList<>();
            mCreated = new Date();
        }
    
        public String getName() {
            return mName;
        }
    
        public void setName(String name) {
            mName = name;
        }
    
        public List<String> getReference() {
            return mReference;
        }
    
        public void setReference(List<String> reference) {
            mReference = reference;
        }
    
        public Date getCreated() {
            return mCreated;
        }
    
        public void setCreated(Date created) {
            mCreated = created;
        }
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(mName);
            dest.writeStringList(mReference);
            dest.writeLong(mCreated.getTime());
        }
    
        @Override
        public boolean equals(Object o) {
            if(this == o) return true;
            if(o == null || getClass() != o.getClass()) return false;
            CustomData that = (CustomData) o;
            return mCreated.equals(that.mCreated) && mName.equals(that.mName);
        }
    
        @Override
        public int hashCode() {
            int result = mName.hashCode();
            result = 31 * result + mCreated.hashCode();
            return result;
        }
    
        public static final Parcelable.Creator<CustomData> CREATOR = new Parcelable.Creator<CustomData>(){
            @Override
            public CustomData createFromParcel(Parcel source) {
                CustomData customData = new CustomData();
                customData.mName = source.readString();
    //            customData.mReference = new ArrayList<>();
                source.readStringList(customData.mReference);
                Long created = source.readLong();
                Log.d(TAG, "createFromParcel " + created);
                customData.mCreated = new Date(created);
                return customData;
            }
    
            @Override
            public CustomData[] newArray(int size) {
                return new CustomData[size];
            }
        };
    }

    为了实现进程间传递,CustomData 需要实现接口Parcelable,writeToParcel()方法和CREATOR是不可少的。

    Step 2:创建CustomData类对应的aidl文件, 不过aidl文件先任意命名,不能是CustomData,否则Android Studio不让继续执行。创建完之后再对aidl重命名为CustomData.aidl。注意此aidl文件的package与CustomData的package要保持一致。模块名app上右键-->new-->AIDL,生成文件后重命名,然后修改文件内容为:

    // CustomData.aidl
    package com.ldb.android.example.aidl;
    
    parcelable CustomData;

    Step 3:继续生成AidlCallback.aidl文件和ApiInterfaceV1.aidl文件,修改文件内容为:

    AidlCallback.aidl:

    // AidlCallback.aidl
    package com.ldb.android.example.aidl;
    
    // Declare any non-default types here with import statements
    import com.ldb.android.example.aidl.CustomData;
    
    oneway interface AidlCallback {
        void onDataUpdated(in CustomData[] data);
    }

    ApiInterfaceV1.aidl:

    // ApiInterfaceV1.aidl
    package com.ldb.android.example.aidl;
    
    // Declare any non-default types here with import statements
    import com.ldb.android.example.aidl.CustomData;
    import com.ldb.android.example.aidl.AidlCallback;
    
    interface ApiInterfaceV1 {
        boolean isPrime(long value);
        void getAllDataSince(long timestamp, out CustomData[] result);
        void storeData(in CustomData data);
        void setCallback(in AidlCallback callback);
    }

    Step 4:菜单 Build --> Make Project 或者 Rebuild Project,如果顺利的话,就能够自动生成AidlCallback.aidl文件和ApiInterfaceV1.aidl文件对应的.java文件。

    在编译目录下,如我的目录是AidlServiceappuildgeneratedsourceaidl... 下有AidlCallback.java和ApiInterfaceV1.java两个文件。或者在创建服务的时候再进行验证。

    Step 5:创建服务类AidlService:

    package com.ldb.android.example.aidlservice;
    
    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 com.ldb.android.example.aidl.AidlCallback;
    import com.ldb.android.example.aidl.ApiInterfaceV1;
    import com.ldb.android.example.aidl.CustomData;
    
    import java.util.ArrayList;
    import java.util.Date;
    
    /**
     * Created by lsp on 2016/9/1.
     */
    public class AidlService extends Service {
    
        private static final String TAG = "AidlService";
    
        private ArrayList<CustomData> mCustomDataCollection;
        private AidlCallback mCallback;
    
        @Override
        public void onCreate() {
            super.onCreate();
            mCustomDataCollection = new ArrayList<>();
            // TODO Populate the list with stored value...
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return mBinder;
        }
    
        @Override
        public boolean onUnbind(Intent intent) {
            Log.d(TAG, "onUnbind");
            return super.onUnbind(intent);
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.d(TAG, "onDestroy");
        }
    
        private static boolean isPrimeImpl(long number) {
            // Implementation left out for brevity...
            return false;
        }
    
        private void getDataSinceImpl(CustomData[] result, Date since) {
            int size = mCustomDataCollection.size();
            Log.d(TAG, "getDataSinceImpl size = " + size);
            Log.d(TAG, "since: " + since);
            int pos = 0;
            for (int i = 0; i < size && pos < result.length; i++) {
                CustomData storedValue = mCustomDataCollection.get(i);
                Log.d(TAG, "storedValue " + i + ": " + storedValue.getCreated());
                if (since.before(storedValue.getCreated())) {
                    Log.d(TAG, "add " + i);
                    result[pos++] = storedValue;
                }
            }
        }
    
        private void storeDataImpl(CustomData data) {
            int size = mCustomDataCollection.size();
            try {
                Thread.sleep(30000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < size; i++) {
                CustomData customData = mCustomDataCollection.get(i);
                if (customData.equals(data)) {
                    mCustomDataCollection.set(i, data);
                    return;
                }
            }
            mCustomDataCollection.add(data);
        }
    
        private final ApiInterfaceV1.Stub mBinder = new ApiInterfaceV1.Stub() {
            @Override
            public boolean isPrime(long value) throws RemoteException {
                return isPrimeImpl(value);
            }
    
            @Override
            public void getAllDataSince(long timestamp, CustomData[] result) throws RemoteException {
                getDataSinceImpl(result, new Date(timestamp));
            }
    
            @Override
            public void storeData(CustomData data) throws RemoteException {
                Log.d(TAG, data.getName() + " -- " + data.getCreated());
                storeDataImpl(data);
                if(mCallback != null){
                    mCallback.onDataUpdated(new CustomData[]{data});
                }
            }
    
            @Override
            public void setCallback(AidlCallback callback) throws RemoteException {
                mCallback = callback;
                mCallback.asBinder().linkToDeath(new DeathRecipient() {
                    @Override
                    public void binderDied() {
                        Log.d(TAG, "binderDied");
                        mCallback = null;
                    }
                }, 0);
            }
        };
    
    }

    服务类中AidlCallback 和 ApiInterfaceV1 分别对应上一步的AidlCallback.java和ApiInterfaceV1.java,与客户端进行通信的就是mBinder,mBinder继承了ApiInterfaceV1.StubApiInterfaceV1.Stub是上一步自动生成的一个类, 查看它的代码,ApiInterfaceV1.Stub实际就是一个Binder,同时它实现了接口ApiInterfaceV1,但没有实现ApiInterfaceV1具体的方法,因此它还是个抽象类,具体实现就得由我们在服务类中完成。而Binder在服务端正是通过onTransact(...)这个方法进行接收客户端的调用的(客户端则是调用transact(...)方法)。

    因此服务端要完成的操作是:

    1、定义Aidl文件。

    2、IDE自动生成Aidl文件对应的java文件。

    3、在服务类中定义一个成员变量,这个成员变量是上一步java文件中生成的Stub的一个实例,并且由我们实现Aidl文件中定义的接口方法。

    4、在onBind()方法中返回此成员变量。

    5、在AndroidManifest.xml文件中声明服务,并且在<inten-filter>中定义<action android.name="..." />,这样客户端可通过此action定位此服务。

    客户端:

    最终项目结构:

      运行效果,三个按钮对应服务的三个方法。

    Step 1:将服务端的Aidl文件和CustomData.java文件拷贝到客户端,注意保持package与服务端一致。

    Step 2:菜单 Build --> Make Project 或者 Rebuild Project,如果顺利的话,就能够自动生成AidlCallback.aidl文件和ApiInterfaceV1.aidl文件对应的.java文件。

    Step 3:实现回调接口AidlCallback.Stub,并定义一个此实现的变量作为客户端成员变量,用于给服务端设置回调。

    // Implement the callback
            mAidlCallback = new AidlCallback.Stub() {
                @Override
                public void onDataUpdated(final CustomData[] data) throws RemoteException {
                    Log.d(TAG, data[0].getName() + " was updated");
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Log.d(TAG, data[0].getName() + " was updated");
                            Toast.makeText(MainActivity.this, data[0].getName() + " was updated",
                                    Toast.LENGTH_SHORT).show();
                        }
                    });
                }
            };

    Step 4:实现接口ServiceConnection,这步是使用Binder进行服务通信必须做的一件事,因为服务端onBind()传出的Binder,最终作为onServiceConnected(ComponentName name, IBinder service)的参数传到客户端。在此方法的实现中,通过ApiInterfaceV1.Stub.asInterface(service)可得到服务端的代理对象。

    @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = ApiInterfaceV1.Stub.asInterface(service);
            try {
                mService.setCallback(mAidlCallback);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

    Step 5:bindService,通过Intent并指定action(与服务端设置的保存一致),来实现绑定,不过从Android 5.0(Lollipop)开始需要显示Intent才能完成bindService。

    // Since Android 5.0(Lollipop), bindService should use explicit intent.
            Intent intent = new Intent("com.ldb.android.example.aidlservice.AidlService");
            bindService(
                    new Intent(createExplicitFromImplicitIntent(this, intent)),
                    this, BIND_AUTO_CREATE);

    Step 6:unbindService。

    以上是实现客户端与服务端进行通信的基本步骤。

    客户端实例代码:

    package com.ldb.android.example.aidlclient;
    
    import android.content.ComponentName;
    import android.content.Context;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.content.pm.PackageManager;
    import android.content.pm.ResolveInfo;
    import android.os.IBinder;
    import android.os.RemoteException;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.Toast;
    
    import com.ldb.android.example.aidl.AidlCallback;
    import com.ldb.android.example.aidl.ApiInterfaceV1;
    import com.ldb.android.example.aidl.CustomData;
    
    import java.util.Date;
    import java.util.GregorianCalendar;
    import java.util.List;
    
    public class MainActivity extends AppCompatActivity implements ServiceConnection{
    
        private static final String TAG = "MainActivity";
    
        private ApiInterfaceV1 mService;
        private EditText mNumber;
        private Button mPrime;
        private Button mStore;
        private Button mGet;
        private AidlCallback.Stub mAidlCallback;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mNumber = (EditText) findViewById(R.id.number_input);
            mPrime = (Button) findViewById(R.id.prime);
            mStore = (Button) findViewById(R.id.store);
            mGet = (Button) findViewById(R.id.get);
    
            mPrime.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onCheckForPrime();
                }
            });
            mStore.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    final CustomData customData = new CustomData();
                    String name = mNumber.getText().toString();
                    customData.setName(name);
                    customData.getReference().add(name + "1");
                    customData.getReference().add(name + "2");
                    customData.getReference().add(name + "3");
    //                customData.setCreated(new GregorianCalendar(2016, 9, 1, 9, 0 ).getTime());
    //                try {
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    mService.storeData(customData);
                                    Log.d(TAG, "mService.storeData1");
                                } catch (RemoteException e) {
                                    e.printStackTrace();
                                }
                            }
                        }).start();
    
                        Log.d(TAG, "mService.storeData2");
    //                } catch (RemoteException e) {
    //                    e.printStackTrace();
    //                }
                }
            });
            mGet.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    CustomData[] result = new CustomData[10];
                    Date since = new GregorianCalendar(2016, 8, 1, 8, 0 ).getTime();
                    try {
                        mService.getAllDataSince(since.getTime(), result);
                        Log.d(TAG, "Result: " + result.length);
                        for(int i = 0; i < result.length; i++){
                            CustomData customData = result[i];
                            if(customData != null) {
                                Log.d(TAG, result[i].getName() + result[i].getCreated().toString());
                                for (String s : result[i].getReference()) {
                                    Log.d(TAG, "  -- " + s);
                                }
                            }
                        }
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            // Since Android 5.0(Lollipop), bindService should use explicit intent.
            Intent intent = new Intent("com.ldb.android.example.aidlservice.AidlService");
            bindService(
                    new Intent(createExplicitFromImplicitIntent(this, intent)),
                    this, BIND_AUTO_CREATE);
    
            // Implement the callback
            mAidlCallback = new AidlCallback.Stub() {
                @Override
                public void onDataUpdated(final CustomData[] data) throws RemoteException {
                    Log.d(TAG, data[0].getName() + " was updated");
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Log.d(TAG, data[0].getName() + " was updated");
                            Toast.makeText(MainActivity.this, data[0].getName() + " was updated",
                                    Toast.LENGTH_SHORT).show();
                        }
                    });
                }
            };
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            unbindService(this);
        }
    
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = ApiInterfaceV1.Stub.asInterface(service);
            try {
                mService.setCallback(mAidlCallback);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mService = null;
        }
    
        public void onCheckForPrime() {
            long number = Long.valueOf(mNumber.getText().toString());
            boolean isPrime = false;
            try {
                isPrime = mService.isPrime(number);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            String message = isPrime ? "number_is_prime" : "number_not_prime";
            Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
        }
    
        public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
            // Retrieve all services that can match the given intent
            PackageManager pm = context.getPackageManager();
            List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
    
            // Make sure only one match was found
            if (resolveInfo == null || resolveInfo.size() != 1) {
                return null;
            }
    
            // Get component info and create ComponentName
            ResolveInfo serviceInfo = resolveInfo.get(0);
            String packageName = serviceInfo.serviceInfo.packageName;
            String className = serviceInfo.serviceInfo.name;
            ComponentName component = new ComponentName(packageName, className);
    
            // Create a new intent. Use the old one for extras and such reuse
            Intent explicitIntent = new Intent(implicitIntent);
    
            // Set the component to be explicit
            explicitIntent.setComponent(component);
    
            return explicitIntent;
        }
    
    }

    布局文件:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <EditText
            android:id="@+id/number_input"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
        <Button
            android:id="@+id/prime"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="prime"/>
        <Button
            android:id="@+id/store"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="store"/>
        <Button
            android:id="@+id/get"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="get"/>
    </LinearLayout>
  • 相关阅读:
    html显示乱码
    java面向对象的三大特性——封装
    java面向对象——类
    16-vue-cli4.0脚手架-项目中定义全局@mixin,变量,cli中配置以供单文件组件中直接使用
    02-滚动加载
    14.oss上传封装
    13-动态组件-keepalive-滚动加载-返回位置
    09-JSONP跨域实现原理
    08- application/x-www-form-urlencoded 与 application/json区别 / querystring
    11-利用Promise的图片异步加载 / Promise封装ajax,模拟axios / Promise的finally原理
  • 原文地址:https://www.cnblogs.com/yarightok/p/5833173.html
Copyright © 2020-2023  润新知