這個錯誤出現在bindService後,從回調獲得對應服務的Ibinder對象,然後強轉成我們自己實現的Binder對象時,出現的類型錯誤。而且這個錯誤出現的前提是,我是在一個新的進程啓動一個Service
<service android:name=".Service1"
android:process=".text"/><!--如果不配置默認在主進程啓動服務-->
所以原因就是,進程之間不能直接傳遞對象,傳遞的是該Binder對象的映射(代理對象),所以類型當然錯誤了,這裏涉及的進程間的通信Binder,關於這部分的內容可以看一下這篇文章《Android Binder設計與實現 - 設計篇》我對這部分研究不夠深刻,就不敢亂寫了。
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
在這個回調中,onServiceConnected(ComponentName name, IBinder service) 第二個參數,是否在新的進程啓動服務,所返回的對象有所不同。
(1)如果不指定android:process,直接在主進程中啓動service,由於在同一個進程內,對象可以直接傳遞,所以返回了我們自己定義Binder對象。這個強轉就是沒有問題的
(2)如果指定了android:process,由跨進程通信,不能直接對象,傳遞的是代理對象,此時就出現了類型錯誤。如下圖所示
到這裏就明白問題的所在。網上有些帖子,提供的解決方法是去掉android:process,也是闊以“解決”問題,我的解決方法是使用aidl進行通信。
1、定義aidl文件
package com.example.textbinder;// 注意包名位置要對應文件位置,根目錄要命名aidl,不然可能會找不到
// aidl的寫法跟定義接口的寫法一樣
interface IMyAidlInterface {
String getName();
}
Build後,會出現
這個文件就是我們需要的。
2、重寫service的onBind()方法
public class Service2 extends Service {
private MyBinder myBinder;
@Override
public void onCreate() {
super.onCreate();
myBinder = new MyBinder();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return myBinder;
}
// 繼承的 IMyAidlInterface.Stub,而不是 IMyAidlInterface
public class MyBinder extends IMyAidlInterface.Stub {
@Override
public String getName() throws RemoteException {
return "service2";
}
}
}
3、在回調中獲得代理對象
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 獲得代理對象
IMyAidlInterface proxy = IMyAidlInterface.Stub.asInterface(service);
try {
Log.d("xx", "onServiceConnected: " + proxy.getName());
} catch (RemoteException e) {
// 調用遠程方法會拋出RemoteException 錯誤
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
到這裏我們就可以實現Service跨進程通信了,這種方法在Google Play,使用應用內購時也被使用。通過調用谷歌服務,和谷歌服務進行跨進程通信,實現查詢和購買流程,部分業務代碼如下:
1、關聯谷歌服務
mServiceConn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
}
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
boolean b = bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
if (!b) {
showLog("可能不存在谷歌商店");
}
2、查詢已購買商品
Bundle purchases = mService.getPurchases(3, packageName, ITEM_TYPE_INAPP, null);
int response_code = (int) purchases.get("RESPONSE_CODE");//判斷是否請求成功!!!
if (response_code == 0) {
showLog("已成功獲取用戶已經擁有的商品");
ArrayList<String> ownedSkus = purchases.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
for (String thisResponse : ownedSkus) {
if (thisResponse.equals(product)) {
isVip.setText("是");
}
showLog("擁有" + thisResponse);
}
}
3、查詢單個商品
ArrayList<String> skuList = new ArrayList<String>();
skuList.add(product);
Bundle querySkus = new Bundle();
querySkus.putStringArrayList("ITEM_ID_LIST", skuList);
Bundle skuDetails = mService.getSkuDetails(3, packageName, ITEM_TYPE_INAPP, querySkus);
4、購買
Bundle buyIntentBundle = mService.getBuyIntent(3, packageName, product, ITEM_TYPE_INAPP, developerPayload);
int response_code = buyIntentBundle.getInt("RESPONSE_CODE");
showLog(response_code + "");
PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
if (pendingIntent == null) {
showLog("錯誤");
} else {
showLog("成功");
startIntentSenderForResult(pendingIntent.getIntentSender(), 1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));
}
從上面代碼中可以看出,實現Google應用購買的原理,就是Service跨進程通信實現的。
附:使用aidl實現Service跨進程通信和谷歌支付的完整項目代碼 (由於涉及谷歌賬號被封,谷歌支付是無法正常跑完的,需要個人上傳app並發表商品,並使用谷歌手機纔可以跑完整個支付流程)