java.lang.ClassCastException: android.os.BinderProxy cannot be cast to ......

這個錯誤出現在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並發表商品,並使用谷歌手機纔可以跑完整個支付流程)

發佈了25 篇原創文章 · 獲贊 25 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章