編輯:關於Android編程
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文件。
新建包名: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(); Listpackages = 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
效果如下:
通過SpannableStringBuilder來實現,它就像html裡邊的元素改變指定文字的文字顏色或背景色public class MainActivity exte
讀前須知:PPK寫這篇文章的時候,IPhone還沒有生產出4S之後的產品。所以,這篇文章中提到的IPhone,都是指IPhone4S及之前的手機。TOP This pag
花了幾天時間,研究了一下Java的反射機制。在這裡總結一下這幾天學習的成果,一來分享自己的學習過程和在學習中遇到的問題,二來是給像我一樣不太了解Java反射機制的同學做一
qq空間現在也可以打賞紅包啦啦!據了解,QQ空間打賞紅包在上個月QQ6.5版中就有了,現在打賞的最高金額是200元!那麼晚qq空間打賞功能是什麼?qq空間打