IPC跨进程交互(2)AIDL的使用

来源:互联网 发布:周杰伦红模仿知乎 编辑:程序博客网 时间:2024/06/05 16:49

这是IPC交互的第二篇,大部分参考了任玉刚老师的《Android开发艺术探索》,以前也有一篇AIDL的使用,那是一篇比较简单的使用,本篇会写到一些常用的功能,监听,认证等


开始就来演示下功能吧!

具体就是Client软件操作Service软件中的SharedPreferences进行一些操作。并且Service监听了添加事件,并反馈事件!

还有就是对操作数据的时候进行了认证处理


2个软件下面的log

Service


Client



下面就开始贴代码和讲解了!

1.创建AIDL文件

首先在Service项目中创建两个AIDL文件

IMyAidlInterface.aidl

// IMyAidlInterface.aidlpackage com.gjn.myservice;import com.gjn.myservice.IOnNewStringListener;interface IMyAidlInterface {//获取keyString getString(String key);//设置keyvoid setString(String key,String val);//获取全部Map getAll();//清空全部void clear();//绑定监听void registerListener(IOnNewStringListener listener);//解绑监听void unregisterListener(IOnNewStringListener listener);}
这是一个功能AIDL文件,写下了所有要实现的功能

IOnNewStringListener.aidl

// IOnNewStringListener.aidlpackage com.gjn.myservice;interface IOnNewStringListener{    //添加新的key    void onNewStringListener(String key);}
由于AIDL不支持自定义的参数,所以上面实现功能中监听的参数是自己自定义的,需要单独创建一个aidl文件来让系统知道。

然后重新rebuild一下项目。

这两个aidl可以直接拷贝到Client项目下,记得包名要一模一样。

注:这两个文件要放在相同的包下面。


现在我们来实现AIDL上面的全部功能。

2.创建服务

在Service项目下创建一个新的服务,代码如下

AIDLService.java

package com.gjn.myservice;import android.app.Service;import android.content.Context;import android.content.Intent;import android.content.SharedPreferences;import android.content.pm.PackageManager;import android.os.Binder;import android.os.IBinder;import android.os.Parcel;import android.os.RemoteCallbackList;import android.os.RemoteException;import android.util.Log;import java.util.Map;import java.util.concurrent.atomic.AtomicBoolean;public class AIDLService extends Service {    private static final String TAG = "AIDLService";    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);    private RemoteCallbackList<IOnNewStringListener> mListeners = new RemoteCallbackList<>();    private Binder mBinder = new IMyAidlInterface.Stub() {        @Override        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {            int check = checkCallingOrSelfPermission("com.gjn.permission.A");            if (check == PackageManager.PERMISSION_DENIED) {                Log.e(TAG, "onTransact: 权限认证失败");                return false;            }            String packageName = null;            String[] packages = getPackageManager().getPackagesForUid(getCallingUid());            if (packages != null && packages.length > 0) {                packageName = packages[0];            }            if (!packageName.startsWith("com.gjn")) {                Log.e(TAG, "onTransact: 包名开头认证失败");                return false;            }            return super.onTransact(code, data, reply, flags);        }        @Override        public String getString(String key) throws RemoteException {            SharedPreferences sharedPreferences = getSharedPreferences("SP", Context.MODE_PRIVATE);            String result = sharedPreferences.getString(key, "");            Log.e(TAG, "getString: " + result);            return result;        }        @Override        public void setString(String key, String val) throws RemoteException {            SharedPreferences.Editor editor = getSharedPreferences("SP",                    Context.MODE_PRIVATE).edit();            editor.putString(key, val);            Log.e(TAG, "setString: " + key + "=" + val);            editor.apply();            OnNewString(key);        }        @Override        public Map getAll() throws RemoteException {            SharedPreferences sharedPreferences = getSharedPreferences("SP", Context.MODE_PRIVATE);            Log.e(TAG, "getAll");            return sharedPreferences.getAll();        }        @Override        public void clear() throws RemoteException {            SharedPreferences sharedPreferences = getSharedPreferences("SP", Context.MODE_PRIVATE);            sharedPreferences.edit().clear().apply();            Log.e(TAG, "clear");        }        @Override        public void registerListener(IOnNewStringListener listener) throws RemoteException {            mListeners.register(listener);            Log.e(TAG, "registerListener: 监听中");        }        @Override        public void unregisterListener(IOnNewStringListener listener) throws RemoteException {            mListeners.unregister(listener);            Log.e(TAG, "unregisterListener: 解绑监听成功");        }    };    @Override    public IBinder onBind(Intent intent) {        int check = checkCallingOrSelfPermission("com.gjn.permission.A");        if (check == PackageManager.PERMISSION_DENIED) {            Log.e(TAG, "onBind: 认证失败");            return null;        }        return mBinder;    }    @Override    public void onDestroy() {        mIsServiceDestoryed.set(true);        super.onDestroy();    }    private void OnNewString(String key) {        final int N = mListeners.beginBroadcast();        for (int i = 0; i < N; i++) {            IOnNewStringListener listener = mListeners.getBroadcastItem(i);            if (listener != null) {                try {                    listener.onNewStringListener(key);                    Log.e(TAG, "OnNewString: 添加 " + key);                } catch (RemoteException e) {                    e.printStackTrace();                }            }        }        mListeners.finishBroadcast();    }}
这是一个很长的代码。来一步一步解释下吧!


首先这是其实就是拿来判断是否需要解绑监听的

private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);

之后由于是跨进程的listener监听,所以系统自带了listener的绑定和解绑,就是RemoteCallbackList

private RemoteCallbackList<IOnNewStringListener> mListeners = new RemoteCallbackList<>();

下来就是最重要的实现了。

private Binder mBinder = new IMyAidlInterface.Stub() {        @Override        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {            int check = checkCallingOrSelfPermission("com.gjn.permission.A");            if (check == PackageManager.PERMISSION_DENIED) {                Log.e(TAG, "onTransact: 权限认证失败");                return false;            }            String packageName = null;            String[] packages = getPackageManager().getPackagesForUid(getCallingUid());            if (packages != null && packages.length > 0) {                packageName = packages[0];            }            if (!packageName.startsWith("com.gjn")) {                Log.e(TAG, "onTransact: 包名开头认证失败");                return false;            }            return super.onTransact(code, data, reply, flags);        }        @Override        public String getString(String key) throws RemoteException {            SharedPreferences sharedPreferences = getSharedPreferences("SP", Context.MODE_PRIVATE);            String result = sharedPreferences.getString(key, "");            Log.e(TAG, "getString: " + result);            return result;        }        @Override        public void setString(String key, String val) throws RemoteException {            SharedPreferences.Editor editor = getSharedPreferences("SP",                    Context.MODE_PRIVATE).edit();            editor.putString(key, val);            Log.e(TAG, "setString: " + key + "=" + val);            editor.apply();            OnNewString(key);        }        @Override        public Map getAll() throws RemoteException {            SharedPreferences sharedPreferences = getSharedPreferences("SP", Context.MODE_PRIVATE);            Log.e(TAG, "getAll");            return sharedPreferences.getAll();        }        @Override        public void clear() throws RemoteException {            SharedPreferences sharedPreferences = getSharedPreferences("SP", Context.MODE_PRIVATE);            sharedPreferences.edit().clear().apply();            Log.e(TAG, "clear");        }        @Override        public void registerListener(IOnNewStringListener listener) throws RemoteException {            mListeners.register(listener);            Log.e(TAG, "registerListener: 监听中");        }        @Override        public void unregisterListener(IOnNewStringListener listener) throws RemoteException {            mListeners.unregister(listener);            Log.e(TAG, "unregisterListener: 解绑监听成功");        }    };


先不看这个onTransact的方法,上面的都是在AIDL上面写好的方法!这边就是实现这些方法。

这边提下,我在set方法中添加了监听事件。

因为是系统的RemoteCallbackList数组,使用是需要结合

beginBroadcast和finishBroadcast

开始使用了beginBroadcast,结尾必须使用finishBroadcast结束


下来是onBind的方法里面,有一段存在和onTransact一模一样的代码,这边就下面在分析了。

    public IBinder onBind(Intent intent) {        int check = checkCallingOrSelfPermission("com.gjn.permission.A");        if (check == PackageManager.PERMISSION_DENIED) {            Log.e(TAG, "onBind: 认证失败");            return null;        }        return mBinder;    }


下来是onDestroy,这边是销毁了监听。

    public void onDestroy() {        mIsServiceDestoryed.set(true);        super.onDestroy();    }

之后来看下这个onTransact,这个是认证用到得,每次连接mBinder的时候他都会执行一次如果返回false的,那么Client软件就没办法操作Service软件的数据。

            int check = checkCallingOrSelfPermission("com.gjn.permission.A");            if (check == PackageManager.PERMISSION_DENIED) {                Log.e(TAG, "onTransact: 权限认证失败");                return false;            }


这个是判断Client项目的AndroidManifest.xml是否存在 com.gjn.permission.A这个Permission


我们来看下Client的AndroidManifest.xml文件

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.gjn.myclient">    <uses-permission android:name="com.gjn.permission.A" />    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <activity android:name="com.gjn.myclient.MainActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>        <activity android:name="com.gjn.myclient.MessengerActivity" />        <activity android:name="com.gjn.myclient.AIDLActivity" />        <activity android:name="com.gjn.myclient.SocketActivity" />        <activity android:name="com.gjn.myclient.BundleActivity" />    </application></manifest>
这上面就有一个uses permission  就是Service判断的内容了


然后下面的

            String packageName = null;            String[] packages = getPackageManager().getPackagesForUid(getCallingUid());            if (packages != null && packages.length > 0) {                packageName = packages[0];            }            if (!packageName.startsWith("com.gjn")) {                Log.e(TAG, "onTransact: 包名开头认证失败");                return false;            }
是判断包名开头是不是com.gjn。如果不是也会返回false。xml文件在上面有,看下package属性即可


注:由于这个Permission(com.gjn.permission.A)不是系统的,而是我们自己定义的,所以需要在Service的xml文件下面也要添加

具体代码是

    <permission        android:name="com.gjn.permission.A"        android:protectionLevel="normal" />    <uses-permission android:name="com.gjn.permission.A" />

下面是全部代码

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.gjn.myservice">    <permission        android:name="com.gjn.permission.A"        android:protectionLevel="normal" />    <uses-permission android:name="com.gjn.permission.A" />    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <activity android:name=".MainActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>        <service            android:name=".MessengerService"            android:enabled="true"            android:exported="true">            <intent-filter>                <action android:name="com.gjn.myservice.Messenger_Service" />            </intent-filter>        </service>        <service            android:name=".AIDLService"            android:enabled="true"            android:exported="true">            <intent-filter>                <action android:name="com.gjn.myservice.AIDLService" />            </intent-filter>        </service>    </application></manifest>
AIDLService也添加了action名字和上篇的功能是一样的,为了让跨进程实现隐式启动。


至此Service的项目就搭建完成了!

我们开始写Client的操作了。

先将AIDLActivity全部贴下来

布局如下


代码如下

<?xml version="1.0" encoding="utf-8"?><RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context="com.gjn.myclient.AIDLActivity">    <Button        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="写入"        android:id="@+id/btn_write"        android:layout_marginTop="81dp"        android:layout_alignParentTop="true"        android:layout_alignStart="@+id/btn_read" />    <Button        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="读取"        android:id="@+id/btn_read"        android:layout_marginTop="30dp"        android:layout_below="@+id/btn_write"        android:layout_centerHorizontal="true" />    <LinearLayout        android:orientation="vertical"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_alignParentTop="true"        android:layout_alignParentStart="true"        android:layout_toStartOf="@+id/btn_write"        android:layout_above="@+id/btn_read"        android:layout_alignEnd="@+id/btn_write">        <LinearLayout            android:orientation="horizontal"            android:layout_width="match_parent"            android:layout_height="wrap_content">            <TextView                android:layout_width="0dp"                android:layout_height="wrap_content"                android:text="key"                android:layout_weight="1" />            <EditText                android:layout_width="0dp"                android:layout_height="wrap_content"                android:id="@+id/et_key"                android:layout_weight="3" />        </LinearLayout>        <LinearLayout            android:orientation="horizontal"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_gravity="center_horizontal">            <TextView                android:layout_width="0dp"                android:layout_height="wrap_content"                android:text="value"                android:layout_weight="1" />            <EditText                android:layout_width="0dp"                android:layout_height="wrap_content"                android:id="@+id/et_val"                android:layout_weight="3" />        </LinearLayout>    </LinearLayout>    <Button        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="随机添加"        android:id="@+id/btn_save"        android:layout_alignParentBottom="true"        android:layout_alignStart="@+id/btn_read"        android:layout_marginBottom="110dp" />    <Button        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="查看全部"        android:id="@+id/btn_all"        android:layout_centerVertical="true"        android:layout_centerHorizontal="true" />    <Button        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="清空"        android:id="@+id/btn_clean"        android:layout_below="@+id/btn_all"        android:layout_centerHorizontal="true" /></RelativeLayout>


AIDLActivity.java

package com.gjn.myclient;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.os.RemoteException;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.Toast;import com.gjn.myservice.IMyAidlInterface;import com.gjn.myservice.IOnNewStringListener;import java.util.Map;import java.util.Random;public class AIDLActivity extends AppCompatActivity {    private EditText et_key;    private EditText et_val;    private Button btn_read;    private Button btn_write;    private Button btn_save;    private Button btn_all;    private Button btn_clean;    private IMyAidlInterface myAidlInterface;    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            myAidlInterface = IMyAidlInterface.Stub.asInterface(service);            if (myAidlInterface == null) {                Log.e("onServiceConnected", "onServiceConnected: 连接失败!");            } else {                Log.e("onServiceConnected", "onServiceConnected: 连接成功!");            }            try {                myAidlInterface.registerListener(mOnNewStringListener);            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    };    private IOnNewStringListener mOnNewStringListener = new IOnNewStringListener.Stub() {        @Override        public void onNewStringListener(String key) throws RemoteException {            Log.e("IOnNewStringListener", "onNewStringListener: " + key);        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_aidl);        findview();        onclick();        Intent intent = new Intent();        intent.setAction("com.gjn.myservice.AIDLService");        intent.setPackage("com.gjn.myservice");        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);    }    private void findview() {        et_key = (EditText) findViewById(R.id.et_key);        et_val = (EditText) findViewById(R.id.et_val);        btn_read = (Button) findViewById(R.id.btn_read);        btn_write = (Button) findViewById(R.id.btn_write);        btn_save = (Button) findViewById(R.id.btn_save);        btn_all = (Button) findViewById(R.id.btn_all);        btn_clean = (Button) findViewById(R.id.btn_clean);    }    private void onclick() {        btn_read.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                final String key = et_key.getText().toString();                String val = "";                if (!key.equals("")) {                    try {                        val = myAidlInterface.getString(key);                    } catch (RemoteException e) {                        e.printStackTrace();                    }                }                et_val.setText(val);                if (val.equals("")) {                    Toast.makeText(AIDLActivity.this, key + "不存在", Toast.LENGTH_SHORT).show();                } else {                    Toast.makeText(AIDLActivity.this, key + "=" + val, Toast.LENGTH_SHORT).show();                }            }        });        btn_write.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                final String key = et_key.getText().toString();                final String val = et_val.getText().toString();                if (!key.equals("") && !val.equals("")) {                    try {                        myAidlInterface.setString(key, val);                    } catch (RemoteException e) {                        e.printStackTrace();                    }                    Toast.makeText(AIDLActivity.this, key + "保存成功", Toast.LENGTH_SHORT).show();                } else {                    Toast.makeText(AIDLActivity.this, key + "保存失败", Toast.LENGTH_SHORT).show();                }                et_val.setText("");            }        });        btn_save.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Random random = new Random();                int r = random.nextInt(100);                try {                    myAidlInterface.setString("key_" + r, "val_" + r);                    Toast.makeText(AIDLActivity.this, "key_" + r + " = val_" + r + "保存成功",                            Toast.LENGTH_SHORT).show();                } catch (RemoteException e) {                    e.printStackTrace();                }            }        });        btn_all.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                try {                    LogAll(myAidlInterface.getAll());                } catch (RemoteException e) {                    e.printStackTrace();                }            }        });        btn_clean.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                try {                    myAidlInterface.clear();                } catch (RemoteException e) {                    e.printStackTrace();                }                Toast.makeText(AIDLActivity.this, "全部清空", Toast.LENGTH_SHORT).show();            }        });    }    private void LogAll(Map all) {        StringBuffer sb = new StringBuffer("all");        Map<String, String> map = all;        //遍历map   小数据效率差不多,大数据第二种效率高        //第一种        for (String key : map.keySet()) {            Log.i("LogAll", key + " = " + map.get(key));            sb.append("\n" + key + " = " + map.get(key));        }        //第二种        for (Map.Entry<String, String> entry : map.entrySet()) {            Log.e("LogAll", entry.getKey() + " = " + entry.getValue());        }        Toast.makeText(AIDLActivity.this, sb.toString(), Toast.LENGTH_SHORT).show();    }    @Override    protected void onDestroy() {        if (myAidlInterface != null && myAidlInterface.asBinder().isBinderAlive()) {            try {                Log.e("onDestroy", "解绑监听");                myAidlInterface.unregisterListener(mOnNewStringListener);            } catch (RemoteException e) {                e.printStackTrace();            }        }        unbindService(mConnection);        super.onDestroy();    }}

这上面就提及一下,开始的在开始连接中加入了许多东西

        public void onServiceConnected(ComponentName name, IBinder service) {            myAidlInterface = IMyAidlInterface.Stub.asInterface(service);            if (myAidlInterface == null) {                Log.e("onServiceConnected", "onServiceConnected: 连接失败!");            } else {                Log.e("onServiceConnected", "onServiceConnected: 连接成功!");            }            try {                myAidlInterface.registerListener(mOnNewStringListener);            } catch (RemoteException e) {                e.printStackTrace();            }        }

这上面开始判断是否连上,因为加了判断所以有可能传过来的是null的,所以这边加了一个判断。

之后下面就是设置监听事件。

    private IOnNewStringListener mOnNewStringListener = new IOnNewStringListener.Stub() {        @Override        public void onNewStringListener(String key) throws RemoteException {            Log.e("IOnNewStringListener", "onNewStringListener: " + key);        }    };
这边监听了就只是打印一个log。


这边在讲一个,在AIDL中使用Map传数据 在AIDL中只能写Map,不能写Map<object,object>这样,执行rebuild的话会提示报错,提示无法识别。

这边我就直接用了Map,发现没有错误。在获取到Map的地方,在new一个Map<object,object>来实行获取键值就好了。


顺便这边也写了两个遍历Map的方法

    private void LogAll(Map all) {        StringBuffer sb = new StringBuffer("all");        Map<String, String> map = all;        //遍历map   小数据效率差不多,大数据第二种效率高        //第一种        for (String key : map.keySet()) {            Log.i("LogAll", key + " = " + map.get(key));            sb.append("\n" + key + " = " + map.get(key));        }        //第二种        for (Map.Entry<String, String> entry : map.entrySet()) {            Log.e("LogAll", entry.getKey() + " = " + entry.getValue());        }        Toast.makeText(AIDLActivity.this, sb.toString(), Toast.LENGTH_SHORT).show();    }
建议都使用第二种,大数据效率,小数据差不多,虽然写起来麻烦点!


其他就没有别的好解释了,如果连接什么的不懂的话,可以看上一篇,上面已经写过了,隐式启动服务!

整个项目会在,我写完全部之后发上来!

1 0
原创粉丝点击