Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 安卓IPC之aidl使用(三)---System aidl調用

安卓IPC之aidl使用(三)---System aidl調用

編輯:關於Android編程

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 = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            java.lang.String _result;
            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 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

效果如下:

\

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved