4

私は同様のタイトルを持つ他の多くのスレッドを見てきましたが、どれも私の問題をカバーしていないようです。だから、ここに行きます。

Googleマーケットエクスパンションファイル(apkx)ライブラリとサンプルコードを使用していますが、いくつか変更が加えられています。このコードは、バックグラウンドダウンロード、ライセンスチェックなどを処理するサービスからのコールバックの受信に依存しています。

サービスが正しく接続されず、ソフトロックが発生するというバグがあります。これをさらに役に立たなくするために、このバグは一部のデバイスでは発生しませんが、他のデバイスでは約3分の2の時間で発生します。Androidのバージョンに依存しないと思います。確かに、2.3.4を実行している2つのデバイスがあり、そのうちの1つ(Nexus S)には問題がなく、もう1つ(HTC Evo 3D)には問題があります。

サービスへの接続を試みるために、bindServiceが呼び出され、trueを返します。次に、OnBindが期待どおりに呼び出され、適切な値を返しますが、(バグが発生した場合)onServiceConnectedは発生しません(念のため20分待ちました)。

他の誰かがこのようなものを見たことがありますか?そうでない場合、そのような行動を引き起こすために私が何をした可能性があるかについての推測はありますか?誰も考えていない場合は、明日コードを投稿します。

編集:これが関連するコードです。私が何かを逃したならば、尋ねてください。

このコードを追加しているときに、小さなバグを見つけました。それを修正すると、私が解決しようとしている問題の頻度が、テストしている電話で3回に2回から6回に約1回に変化しました。他の電話への影響についてはわかりません。これは私に競合状態または同様のものを示唆し続けます、しかし私は何であるかわかりません。

OurDownloaderActivity.java(Googleサンプルコードからコピーおよび変更)

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ...

    //Test the licence is up to date
    //if (current stored licence has expired)
    {
        startLicenceCheck();
        initializeDownloadUI();
        return;
    }

    ...
}

@Override
protected void onResume() {
    if (null != mDownloaderClientStub) {
        mDownloaderClientStub.connect(this);
    }
    super.onResume();
}

private void startLicenceCheck()
{
    Intent launchIntent = OurDownloaderActivity.this
            .getIntent();
    Intent intentToLaunchThisActivityFromNotification = new Intent(OurDownloaderActivity
            .this, OurDownloaderActivity.this.getClass());
    intentToLaunchThisActivityFromNotification.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
            Intent.FLAG_ACTIVITY_CLEAR_TOP);
    intentToLaunchThisActivityFromNotification.setAction(launchIntent.getAction());

    if (launchIntent.getCategories() != null) {
        for (String category : launchIntent.getCategories()) {
            intentToLaunchThisActivityFromNotification.addCategory(category);
        }
    }

    // Build PendingIntent used to open this activity from Notification
    PendingIntent pendingIntent = PendingIntent.getActivity(OurDownloaderActivity.this,
            0, intentToLaunchThisActivityFromNotification,
            PendingIntent.FLAG_UPDATE_CURRENT);

    DownloaderService.startLicenceCheck(this, pendingIntent, OurDownloaderService.class);
}

initializeDownloadUI()
{
    mDownloaderClientStub = DownloaderClientMarshaller.CreateStub
            (this, OurDownloaderService.class);

    //do a load of UI setup
    ...
}

//This should be called by the Stub's onServiceConnected method
/**
 * Critical implementation detail. In onServiceConnected we create the
 * remote service and marshaler. This is how we pass the client information
 * back to the service so the client can be properly notified of changes. We
 * must do this every time we reconnect to the service.
 */
@Override
public void onServiceConnected(Messenger m) {
    mRemoteService = DownloaderServiceMarshaller.CreateProxy(m);
    mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger());
}

DownloaderService.java(Googleマーケット拡張ライブラリにありますが、多少編集されています)

//this is the onBind call that happens fine; the value it returns is definitely not null
@Override
public IBinder onBind(Intent paramIntent) {
    return this.mServiceMessenger.getBinder();
}

final private IStub mServiceStub = DownloaderServiceMarshaller.CreateStub(this);
final private Messenger mServiceMessenger = mServiceStub.getMessenger();

//MY CODE, derived from Google's code
//I have seen the bug occur with a service started by Google's code too,
//but this code happens more often so is more repeatably related to the problem
public static void startLicenceCheck(Context context, PendingIntent pendingIntent, Class<?> serviceClass)
{       
    String packageName = serviceClass.getPackage().getName();
    String className = serviceClass.getName();

    Intent fileIntent = new Intent();
    fileIntent.setClassName(packageName, className);
    fileIntent.putExtra(EXTRA_LICENCE_EXPIRED, true);
    fileIntent.putExtra(EXTRA_PENDING_INTENT, pendingIntent);
    context.startService(fileIntent);
}

@Override
protected void onHandleIntent(Intent intent) {
    setServiceRunning(true);
    try {
        final PendingIntent pendingIntent = (PendingIntent) intent
            .getParcelableExtra(EXTRA_PENDING_INTENT);

        if (null != pendingIntent)
        {
            mNotification.setClientIntent(pendingIntent);
            mPendingIntent = pendingIntent;
        } else if (null != mPendingIntent) {
            mNotification.setClientIntent(mPendingIntent);
        } else {
            Log.e(LOG_TAG, "Downloader started in bad state without notification intent.");
            return;
        }

        if(intent.getBooleanExtra(EXTRA_LICENCE_EXPIRED, false))
        {
            //we are here due to startLicenceCheck
            updateExpiredLVL(this);
            return;
        }
    ...
    }
}

//MY CODE, based on Google's, again
public void updateExpiredLVL(final Context context) {
    Context c = context.getApplicationContext();
    Handler h = new Handler(c.getMainLooper());
    h.post(new LVLExpiredUpdateRunnable(c));
}

private class LVLExpiredUpdateRunnable implements Runnable
{
    LVLExpiredUpdateRunnable(Context context) {
        mContext = context;
    }

    final Context mContext;

    @Override
    public void run() {
        setServiceRunning(true);
        mNotification.onDownloadStateChanged(IDownloaderClient.STATE_LVL_UPDATING);
        String deviceId = getDeviceId(mContext);

        final APKExpansionPolicy aep = new APKExpansionPolicy(mContext,
                new AESObfuscator(getSALT(), mContext.getPackageName(), deviceId));

        // Construct the LicenseChecker with a Policy.
        final LicenseChecker checker = new LicenseChecker(mContext, aep,
                getPublicKey() // Your public licensing key.
        );
        checker.checkAccess(new LicenseCheckerCallback() {
            ...
        });
    }
}

DownloaderClientMarshaller.java(Google市場拡張ライブラリ内)

public static IStub CreateStub(IDownloaderClient itf, Class<?> downloaderService) {
    return new Stub(itf, downloaderService);
}

同じファイルのStubクラス:

private static class Stub implements IStub {
    private IDownloaderClient mItf = null;
    private Class<?> mDownloaderServiceClass;
    private boolean mBound;
    private Messenger mServiceMessenger;
    private Context mContext;
    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    final Messenger mMessenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_ONDOWNLOADPROGRESS:                        
                    Bundle bun = msg.getData();
                    if ( null != mContext ) {
                        bun.setClassLoader(mContext.getClassLoader());
                        DownloadProgressInfo dpi = (DownloadProgressInfo) msg.getData()
                                .getParcelable(PARAM_PROGRESS);
                        mItf.onDownloadProgress(dpi);
                    }
                    break;
                case MSG_ONDOWNLOADSTATE_CHANGED:
                    mItf.onDownloadStateChanged(msg.getData().getInt(PARAM_NEW_STATE));
                    break;
                case MSG_ONSERVICECONNECTED:
                    mItf.onServiceConnected(
                            (Messenger) msg.getData().getParcelable(PARAM_MESSENGER));
                    break;
            }
        }
    });

    public Stub(IDownloaderClient itf, Class<?> downloaderService) {
        mItf = itf;
        mDownloaderServiceClass = downloaderService;
    }

    /**
     * Class for interacting with the main interface of the service.
     */
    private ServiceConnection mConnection = new ServiceConnection() {

        //this is the critical call that never happens

        public void onServiceConnected(ComponentName className, IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the object we can use to
            // interact with the service. We are communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            mServiceMessenger = new Messenger(service);
            mItf.onServiceConnected(
                    mServiceMessenger);
            mBound = true;                
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            mServiceMessenger = null;
            mBound = false;
        }
    };

    @Override
    public void connect(Context c) {
        mContext = c;
        Intent bindIntent = new Intent(c, mDownloaderServiceClass);
        bindIntent.putExtra(PARAM_MESSENGER, mMessenger);
        if ( !c.bindService(bindIntent, mConnection, 0) ) {
            if ( Constants.LOGVV ) {
                Log.d(Constants.TAG, "Service Unbound");
            }
        }

    }

    @Override
    public void disconnect(Context c) {
        if (mBound) {
            c.unbindService(mConnection);
            mBound = false;
        }
        mContext = null;
    }

    @Override
    public Messenger getMessenger() {
        return mMessenger;
    }
}

DownloaderServiceMarshaller.java(Google市場拡張ライブラリ内、変更なし)

private static class Proxy implements IDownloaderService {
    private Messenger mMsg;

    private void send(int method, Bundle params) {
        Message m = Message.obtain(null, method);
        m.setData(params);
        try {
            mMsg.send(m);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    public Proxy(Messenger msg) {
        mMsg = msg;
    }

    @Override
    public void requestAbortDownload() {
        send(MSG_REQUEST_ABORT_DOWNLOAD, new Bundle());
    }

    @Override
    public void requestPauseDownload() {
        send(MSG_REQUEST_PAUSE_DOWNLOAD, new Bundle());
    }

    @Override
    public void setDownloadFlags(int flags) {
        Bundle params = new Bundle();
        params.putInt(PARAMS_FLAGS, flags);
        send(MSG_SET_DOWNLOAD_FLAGS, params);
    }

    @Override
    public void requestContinueDownload() {
        send(MSG_REQUEST_CONTINUE_DOWNLOAD, new Bundle());
    }

    @Override
    public void requestDownloadStatus() {
        send(MSG_REQUEST_DOWNLOAD_STATE, new Bundle());
    }

    @Override
    public void onClientUpdated(Messenger clientMessenger) {
        Bundle bundle = new Bundle(1);
        bundle.putParcelable(PARAM_MESSENGER, clientMessenger);
        send(MSG_REQUEST_CLIENT_UPDATE, bundle);
    }
}

private static class Stub implements IStub {
    private IDownloaderService mItf = null;
    final Messenger mMessenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_REQUEST_ABORT_DOWNLOAD:
                    mItf.requestAbortDownload();
                    break;
                case MSG_REQUEST_CONTINUE_DOWNLOAD:
                    mItf.requestContinueDownload();
                    break;
                case MSG_REQUEST_PAUSE_DOWNLOAD:
                    mItf.requestPauseDownload();
                    break;
                case MSG_SET_DOWNLOAD_FLAGS:
                    mItf.setDownloadFlags(msg.getData().getInt(PARAMS_FLAGS));
                    break;
                case MSG_REQUEST_DOWNLOAD_STATE:
                    mItf.requestDownloadStatus();
                    break;
                case MSG_REQUEST_CLIENT_UPDATE:
                    mItf.onClientUpdated((Messenger) msg.getData().getParcelable(
                            PARAM_MESSENGER));
                    break;
            }
        }
    });

    public Stub(IDownloaderService itf) {
        mItf = itf;
    }

    @Override
    public Messenger getMessenger() {
        return mMessenger;
    }

    @Override
    public void connect(Context c) {

    }

    @Override
    public void disconnect(Context c) {

    }
}

/**
 * Returns a proxy that will marshall calls to IDownloaderService methods
 * 
 * @param ctx
 * @return
 */
public static IDownloaderService CreateProxy(Messenger msg) {
    return new Proxy(msg);
}

/**
 * Returns a stub object that, when connected, will listen for marshalled
 * IDownloaderService methods and translate them into calls to the supplied
 * interface.
 * 
 * @param itf An implementation of IDownloaderService that will be called
 *            when remote method calls are unmarshalled.
 * @return
 */
public static IStub CreateStub(IDownloaderService itf) {
    return new Stub(itf);
}
4

0 に答える 0