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文件
<?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(); }建议都使用第二种,大数据效率,小数据差不多,虽然写起来麻烦点!
其他就没有别的好解释了,如果连接什么的不懂的话,可以看上一篇,上面已经写过了,隐式启动服务!
整个项目会在,我写完全部之后发上来!
- IPC跨进程交互(2)AIDL的使用
- IPC跨进程交互(1)Messenger的使用
- IPC跨进程交互(3)Socket的使用
- IPC跨进程交互(4)Binder池的使用
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- android使用AIDL实现跨进程通讯(IPC)
- android使用AIDL实现跨进程通讯(IPC)
- android跨进程通信(IPC):使用AIDL
- android跨进程通信(IPC):使用AIDL
- 示例:Android使用AIDL实现跨进程通讯(IPC)
- AbstractListView源码分析1
- LLDB使用详解
- CentOS下安装word2Vec
- JAVA 解压tar.gz格式文件
- php 与类相关的系统函数;
- IPC跨进程交互(2)AIDL的使用
- 使用iPhone来提醒自己-日历和提醒事项
- FME geotiff合并步骤
- linux学习笔记--ipcs命令
- 真机测试报错:Please try rebooting and reconnecting the device. (0xE8000076)
- Android自定义之高仿淘宝下拉刷新
- 批量改名C#
- 常用的sql语句用法
- Spring MVC - 框架介绍