流程死亡后如何使用接口在活动之间进行通信? [英] How to use interface to communicate between activities after process death?

查看:74
本文介绍了流程死亡后如何使用接口在活动之间进行通信?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个SDK,需要在活动之间实现回调,而无需实际完成活动.我以前使用过onActivityResult将结果提供回呼叫者活动.但是,这将关闭活动,我需要提供回调,不完成SDK的活动.我当前的实现:

I am building an SDK and need to implement callbacks between activities, without actually finish an activity. I previously used onActivityResult to provide results back to caller activity. However, this closes activity and I need to deliver callback, without finishing activity from SDK. My current implementation:

fun initializeSDK(){
    SDK.getInstance().initialize(resultsCallbackImpl)
}
val resultsCallbackImpl:ResultsCallback = object : ResultsCallback {
    override fun response1() {
        
    }

    override fun response2() {
        
    }
};

例如,客户端在单击按钮后从其活动中调用 initializeSDK().然后,客户端将接口作为参数传递,该接口在SDK单例中设置为属性.然后,我使用该界面返回结果.

For example, the client calls initializeSDK() from his activity after the button click. Then the client passes interface as parameter, which is set as a property in SDK singleton. Then I use that interface to return results.

该问题在进程终止后发生.该接口变为空,因为它未序列化,并且我无法再将回调返回给客户端.我应该如何编辑代码以解决此问题?甚至有可能吗?

The problem occurs after process death. The interface becomes null, because it is not serialized and I can't return callback to client anymore. How should I edit my code to tackle this issue? Is it even possible?

我知道客户端可以在应用程序类中初始化SDK,然后在进程终止后将其重新设置.但是,这种方法将导致客户端难以将结果传达回应用程序类的活动.

I know that client can initialize SDK in the application class, then it will be re-set after process death. However, such an approach will result in difficulty for the client to communicate results back to activity from application class.

推荐答案

更新:

右键单击项目树,然后添加一个名为IMyAidlInterface.aidl的新AIDL文件:

Do a right click on the project tree and add a new AIDL file called IMyAidlInterface.aidl:

package com.test.aidlsample;

import com.test.aidlsample.MyData;

interface IMyAidlInterface {
    List<MyData> getData(long id);
}

如果您需要将对象返回给客户端,则需要声明它们并将其定义为可打包的,并将它们也导入到aidl文件中,这是MyData.aidl,它应位于其他aidl文件旁边:

If you need to return objects to your client you need to declare and define them as parcelable and import them in aidl file too, here is the MyData.aidl that should be beside the other aidl file:

package com.test.aidlsample;

// Declare MyData so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable MyData;

这是java文件夹中的MyData.java:

and this is MyData.java in the java folder:

public class MyData implements Parcelable {
    private long productId;
    private String productName;
    private long productValue;

    public MyData(long productId, String productName, long productValue) {
        this.productId = productId;
        this.productName = productName;
        this.productValue = productValue;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeLong(this.productId);
        dest.writeString(this.productName);
        dest.writeLong(this.productValue);
    }

    protected MyData(Parcel in) {
        this.productId = in.readLong();
        this.productName = in.readString();
        this.productValue = in.readLong();
    }

    public static final Parcelable.Creator<MyData> CREATOR = new Parcelable.Creator<MyData>() {
        @Override
        public MyData createFromParcel(Parcel source) {
            return new MyData(source);
        }

        @Override
        public MyData[] newArray(int size) {
            return new MyData[size];
        }
    };
}

现在构建项目,以便构建Stub类.成功构建之后,继续使用该服务:

Now build the project so Stub class gets built. After a successful build continue with the service:

public class SdkService extends Service {

    private IMyAidlInterface.Stub binder = new IMyAidlInterface.Stub() {
        @Override
        public List<MyData> getData(long id) throws RemoteException {
            //TODO: get data from db by id;
            List<MyData> data = new ArrayList<>();
            MyData aData = new MyData(1L, "productName", 100L);
            data.add(aData);
            return data;
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
}

,然后将服务添加到sdk清单中.如果要将sdk作为依赖项添加到客户端,例如: implementation project(':sdk'),则无需将AIDL文件添加到客户端.如果不是,则必须添加它们并构建客户端应用程序.现在,仅剩下执行客户端活动的情况了:

and add the service to the sdk manifest. If you are adding sdk as a dependency to the client like: implementation project(':sdk') you don't need to add AIDL files to client. If not, you have to add them and build the client application. Now, only remains to implement the client activity:

public class MainActivity extends AppCompatActivity {

    IMyAidlInterface mService;

    /**
     * Class for interacting with the main interface of the service.
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className,
                                       IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the service object we can use to
            // interact with the service.  We are communicating with our
            // service through an IDL interface, so get a client-side
            // representation of that from the raw service object.
            mService = IMyAidlInterface.Stub.asInterface(service);

            try {
                List<MyData> data = mService.getData(1L);
                updateUi(data);
            } catch (RemoteException e) {
                // In this case the service has crashed before we could even
                // do anything with it; we can count on soon being
                // disconnected (and then reconnected if it can be restarted)
                // so there is no need to do anything here.
            }

        }

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

    private void updateUi(List<MyData> data) {
        //TODO: Update UI here
    }

    @Override
    protected void onResume() {
        if (mService == null) {
            Intent serviceIntent = new Intent();
            
            //CAREFUL: serviceIntent.setComponent(new ComponentName("your.client.package", "your.sdk.service.path"));
            serviceIntent.setComponent(new ComponentName("com.test.sampleclient", "com.test.aidlsample.SdkService"));
            bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
        } else {
            try {
                updateUi(mService.getData(1L));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        super.onResume();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

每当您的客户活动获得可见性时,它就会从sdk服务获取数据.只需在此模板上构建您的逻辑即可.在sdk活动中,将数据保存到数据库中,然后在服务中从数据库中查询它们.我在此示例中使用了简单的参数.

every time your client activity gets visibility, it gets data from sdk service. Just build your logic over this template. In sdk activity save data to a database and in service query them from database. I've used simple parameters in this sample.

我假设您的SDK是客户端应用程序中的一个库.如果没有,您可能需要做一些小的修改.正如我之前提到的,您可以在此处找到更多详细信息: Android接口定义语言(AIDL).关于此主题的SO中有很多样本,甚至还有更多的Q/A.祝你好运.

I assumed your sdk is a library in the client app. If not, you need to do some small modifications maybe. And as I mentioned before you can find more details here: Android Interface Definition Language (AIDL). There are lots of samples and even more Q/A here in the SO on the subject. Good luck.

原始:您需要从当前不可见的活动中获取回调,因为您的SDK活动位于最前面,对吗?为此,您可以为SDK创建数据库,将数据持久保存到数据库中,并通过获取数据AIDL 在开始活动中:

Original: You need to get callbacks from an activity that is currently invisible since your SDK activity is in front, right? To do that you can create a database for your SDK, persist data to your database and get data via an AIDL in the starting activity:

SdkService sdkService;
CallbackData callbackData

private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established
    public void onServiceConnected(ComponentName className, IBinder service) {
        sdkService = SdkService.Stub.asInterface(service);
    }

    // Called when the connection with the service disconnects unexpectedly
    public void onServiceDisconnected(ComponentName className) {
        Log.e(TAG, "Service has unexpectedly disconnected");
        sdkService = null;
    }
};

在onCreate中:

in onCreate:

Intent i = new Intent()
i.setClassName("your.sdk.packageName", "your.sdk.service.path.and.name");
bindService(i, mConnection, Context.BIND_AUTO_CREATE);

并在需要时插入:

if(sdkService != null){
    callbackData = sdkService.getCallbacks();
    updateUI();
}

请务必小心,使绑定器是异步工作,因此,如果您调用bindService,并且在调用sdkService.getCallbackData之后立即获得NullPointerException.因此,您可能希望将getCallbacks和updateUI移到onServiceConnected内,并在onResume中调用bindService,以便每次活动可见时,您都要检查是否存在CallbackData,以便可以更新UI或其他内容.

Just be careful getting a binder is an async job so if you call bindService and right after call sdkService.getCallbackData you get a NullPointerException. So you might want to move getCallbacks and updateUI inside the onServiceConnected and call bindService in onResume so every time activity becomes visible you would check if there is CallbackData so you can update your UI or whatever.

这篇关于流程死亡后如何使用接口在活动之间进行通信?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆