首页 > 代码库 > 安卓IPC之aidl使用(三)---System aidl调用
安卓IPC之aidl使用(三)---System aidl调用
安卓IPC之aidl使用(三)—System aidl调用
安卓IPC之aidl使用(一)–aidl常见使用
安卓IPC之aidl使用(二)—aidl本地实现
AIDL的理解:
Service中的IBinder
还记得我们在MyService中利用new IMyInterface.Stub()向上转型成了IBinder然后在onBind方法中返回的。那我们就看看IMyInterface.Stub吧:
public static abstract class Stub extends android.os.Binder implements aidl.IMyInterface {
..........
}
可以看到,Stub是IMyInterface中的一个静态抽象类,继承了Binder,并且实现了IMyInterface接口。这也就解释了我们定义IMyInterface.Stub的时候为什么需要实现IMyInterface中的方法了,也说明了为什么我们可以把IMyInterface.Stub向上转型成IBinder了。
Activity中的IMyInterface
在Activity中,通过ServiceConnection连接MyService并成功回调onServiceConnected中我们把传回来的IBinder通过IMyInterface.Stub.asInterface(service)转换成为IMyInterface,那就来看看这里是如何转换的吧:
public static abstract class Stub extends android.os.Binder implements aidl.IMyInterface {
..........
public static aidl.IMyInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//检查Binder是不是在当前进程
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof aidl.IMyInterface))) {
return ((aidl.IMyInterface) iin);
}
return new aidl.IMyInterface.Stub.Proxy(obj);
}
}
首先,我们因该明白的是,传回来的IBinder就是我们在Service的onBind( )方法所return的IBinder,然后我们调用Stub中的静态方法asInterface并把返回来的IBinder当参数传进去。
在asInterface方法中,首先判断了传进来的IBinder是不是null,如果为null就返回一个null;接着就判断传进来的IBinder是不是就在当前进程里面,如果是的话就直接返回IMyInterface,不是的话就返回IMyInterface.Stub.Proxy(obj)。这里我觉得需要明白的是:直接返回的IMyInterface是实现了定义的接口方法getInfor的。因为在IMyInterface.Stub中所实现的。当然如果是在同一进程中,那么我们调用IMyInterface的方法时就是在本地调用方法,直接调用就可以了。
如果没在同一进程,就会返回IMyInterface.Stub.Proxy(obj):
private static class Proxy implements aidl.IMyInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public java.lang.String getInfor(java.lang.String s) throws android.os.RemoteException {
android.os.Parcel _data = http://www.mamicode.com/android.os.Parcel.obtain();"hljs-keyword">try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(s);
//传送数据到远程的
mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
_reply.readException();
//接受从远端传回的数据
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getInfor = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
在Proxy中,我们首先把Service连接成功返回的IBinder它的内部变量mRemote,这里在提一下,这里得IBinder还是是MyService中onBind所返回的。然后,当我们调用IMyInterface的方法的时候,其实就是调用的Proxy的方法了,这也是为什么这个类叫做Porxy的原因了。
当调用IMyInterface.getInfor(String s) ,我们就看Proxy中的getInfor,先获取了两个Parcel对象 _data、_data,从变量名就可以看出,一个是传送数据的,另一个则是接受返回数据的。接着,向_data中写入了DESCRIPTOR(也就是这个类的全名),再写入了方法参数。然后就到了最重要的一步了,
mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
这里我们调用了IBinder的transact方法,来把数据传给远端的服务器。然后在我们远程的MyService中,里面的Stub中就会回调onTransact()(因为你把数据传个远程的服务,远端的服务收到数据也就回调了)
注意:这里是在远程的服务里调用的。
@Overridepublic
boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getInfor: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
//取出参数
_arg0 = data.readString();
// 远程服务调用自己本地实现的方法获取返回值
java.lang.String _result = this.getInfor(_arg0);
reply.writeNoException();
//写入返回值
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
onTransact方法是在Stub的内部实现的。
先看一下它的四个参数:
code:每个方法都有一个int类型的数字用来区分(后面中的swicth),在我们例子中也就是我们Proxy中的Stub.TRANSACTION_getInfor。
data:传过来的数据,其中包含我们的参数,以及类的描述。
reply:传回的数据,我们要写入是否发生了Exception,以及返回值
flags:该方法是否有返回值 ,0表示有返回值。
调用onTransact就表示有数据传来,首先就会通过swicth判断是哪个方法,然后取出方法参数,调用本地实现的方法获取返回值,写入返回值到reply。最后,返回true,才会把数据发送出去,发挥false就不会把结果返回给Activity了。这里也就是说,只有返回true,我们Proxy中才能接受从远端传回的数据。
//传送数据到远程的
mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
_reply.readException();
//接受从远端传回的数据
_result = _reply.readString();
使用系统的aidl(获取安卓系统缓存)
记得最开始还没有毕业的时候做的一个手机杀毒软件里面的就是利用了aidl来实现的,直接使用别人写好的aidl文件。
新建包名:android.content.pm,并且把这3个文件拷贝过来,
自己的项目视为客户端,来实现跨进程通信。
使用如下:
package com.losileeya.systemaidl;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.text.format.Formatter;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.lang.reflect.Method;
import java.util.List;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView tvShowCaches, tvAppCache;
private Button btnScanCache, btnClearAll;
private PackageManager pm;
StringBuilder sb = new StringBuilder();
StringBuilder sbCache = new StringBuilder();
private long cacheS;
Handler mHadler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnScanCache = (Button) findViewById(R.id.btn_scanCache);
btnClearAll = (Button) findViewById(R.id.btn_clearAll);
tvShowCaches = (TextView) findViewById(R.id.tv_showAppInfo);
tvAppCache = (TextView) findViewById(R.id.tv_appCache);
sbCache.append("所有缓存:\n");
tvAppCache.setText(sbCache.toString());
btnScanCache.setOnClickListener(this);
btnClearAll.setOnClickListener(this);
}
@Override
public void onClick(View v) {
cacheS = 0;
if (v.getId() == btnScanCache.getId()) {
getCaches();
// ==========获取每个app的缓存
} else if (v.getId() == btnClearAll.getId()) {
cleanAll(v);
getCaches();
}
}
class MyPackageStateObserver extends IPackageStatsObserver.Stub {
@Override
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) throws RemoteException {
String packageName = pStats.packageName;
long cacheSize = pStats.cacheSize;
long codeSize = pStats.codeSize;
long dataSize = pStats.dataSize;
cacheS += cacheSize;
// sb.delete(0, sb.length());
if (cacheSize > 0) {
sb.append("packageName = " + packageName + "\n")
.append(" cacheSize: " + cacheSize + "\n")
.append(" dataSize: " + dataSize + "\n")
.append("-----------------------\n")
;
Log.e("aaaa", sb.toString());
}
}
}
class ClearCacheObj extends IPackageDataObserver.Stub {
@Override
public void onRemoveCompleted(String packageName, final boolean succeeded) throws RemoteException {
mHadler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "清除状态: " + succeeded, Toast.LENGTH_SHORT).show();
}
});
}
}
/**
* 清理全部应用程序缓存的点击事件
*
* @param view
*/
public void cleanAll(View view) {
//freeStorageAndNotify
Method[] methods = PackageManager.class.getMethods();
for (Method method : methods) {
if ("freeStorageAndNotify".equals(method.getName())) {
try {
method.invoke(pm, Long.MAX_VALUE, new ClearCacheObj());
} catch (Exception e) {
e.printStackTrace();
}
return;
}
}
}
private void getCaches() {
// scan
pm = getPackageManager();
List<PackageInfo> packages = pm.getInstalledPackages(0);
int max = packages.size();
int current = 0;
sb.delete(0, sb.length());
sb.append("所有已安装的app信息:\n");
sb.append("所有App 总和:" + max + " \n");
tvShowCaches.setText(sb.toString());
for (PackageInfo pinfo : packages) {
String packageName = pinfo.packageName;
try {
Method getPackageSizeInfo = PackageManager.class
.getDeclaredMethod("getPackageSizeInfo", String.class, IPackageStatsObserver.class);
getPackageSizeInfo.invoke(pm, packageName, new MyPackageStateObserver());
current++;
} catch (Exception e) {
current++;
e.printStackTrace();
}
}
//===到这里,数据准备完成
mHadler.postDelayed(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "缓存信息获取完成", Toast.LENGTH_SHORT).show();
sbCache.append(Formatter.formatFileSize(getApplicationContext(), cacheS) + "\n");
tvShowCaches.setText(sb.toString());
tvAppCache.setText(sbCache.toString());
sbCache.delete(0, sbCache.length());
}
}, 1000);
//ok,所有应用程序信息显示完成
}
}
通过AIDL的方法来获取到应用的缓存信息,getPackageSizeInfo是PackageManager里面的一个私有方法来的,
我们通过反射就可以调用到它的了,这个方法里面会传递一个IPackageStatsObserver.Stub的对象
通过freeStorageAndNotify方法后使用反射ClearCacheObj
效果如下:
demo 传送门:AIDLMaster
更多学习资料:
Android:学习AIDL,这一篇文章就够了(上)
Android:学习AIDL,这一篇文章就够了(下)
Android中的Service:Binder,Messenger,AIDL
安卓IPC之aidl使用(三)---System aidl调用