4

我查看了许多具有类似标题的其他线程,但似乎没有一个能解决我的问题。所以,就这样吧。

我正在使用 Google 市场扩展文件 (apkx) 库和示例代码,并进行了一些修改。此代码依赖于从处理后台下载、许可证检查等的服务接收回调。

我有一个错误,服务没有正确连接,导致软锁。为了使这更加无用,此错误从未在某些设备上发生,但在其他设备上大约有三分之二的时间发生。我相信它独立于 Android 版本,当然我有两台运行 2.3.4 的设备,其中一台(Nexus S)没有问题,另一台(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(在谷歌市场扩展库中)

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(在谷歌市场扩展库中,不变)

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