进程死亡后如何使用接口在活动之间进行通信?

2023-12-05

我正在构建一个 SDK,需要在活动之间实现回调,但没有实际完成活动。我之前使用 onActivityResult 将结果提供给调用者活动。但是,这会关闭活动,我需要传递回调,无需完成 SDK 的活动。我当前的实现:

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

    override fun response2() {
        
    }
};

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

该问题发生在进程死亡后。接口变成空,因为它没有序列化,我无法再向客户端返回回调。我应该如何编辑代码来解决这个问题?有可能吗?

我知道客户端可以初始化SDK在应用程序类中,那么它将在进程死亡后重新设置。然而,这样的做法会给客户带来沟通上的困难。结果返回到应用程序类的活动.


Update:

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

package com.test.aidlsample;

import com.test.aidlsample.MyData;

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

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

package com.test.aidlsample;

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

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

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];
        }
    };
}

现在构建项目,以便构建存根类。成功构建后继续服务:

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 文件添加到客户端。如果没有,您必须添加它们并构建客户端应用程序。现在,只剩下实现客户端活动了:

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 活动中将数据保存到数据库并在服务中从数据库中查询它们。我在此示例中使用了简单的参数。

我假设你的 sdk 是客户端应用程序中的一个库。如果没有,您可能需要做一些小的修改。正如我之前提到的,您可以在这里找到更多详细信息:Android 接口定义语言 (AIDL)。关于该主题的 SO 中有很多示例,甚至还有更多问答。祝你好运。

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

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 中:

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 或其他内容。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

进程死亡后如何使用接口在活动之间进行通信? 的相关文章

随机推荐

  • 使用 Jersey 客户端的 HTTPS

    如何使用 Jersey Client API 将 GET 请求发送到在 HTTPS 协议上运行的服务器 有我可以使用的示例代码吗 像这样构建你的客户端 HostnameVerifier hostnameVerifier HttpsURLCo
  • IIS 服务器和 ASP.Net Core - 500.19,httpplatformhandler 标记上的错误代码为 0x8007000d

    当我尝试使用 IIS Server v7 5 启动 ASP Net Core 应用程序时 出现以下错误 我已成功将网站 Visual Studio 中的文件系统选项 发布到特定目录 它可以从 approot web cmd 文件正常启动 但
  • 为 Android 标记信息窗口添加图像

    我正在制作一个 Android 应用程序 在此应用程序中 有一些酒店的标记 当用户单击标记信息窗口时会出现 它应该如下图所示 这是一个网络应用程序 我的导航抽屉片段中有一个列表视图 地图片段有标记 这是我尝试加载信息窗口图像之前的代码Mai
  • Cython 是否提供任何相当简单且有效的方法来迭代 Numpy 数组,就好像它们是扁平的一样?

    假设我想实现 Numpy 的 x 1 在赛通 我可以写 cython boundscheck False cython wraparoundcheck False def add1 np ndarray np float32 t ndim
  • 如何在 HTML5 中播放 3gp 视频?

    使用 HTML5 播放 3gp 文件不起作用 我有 Firefox 16 和 Chromium Version 22 我得到的结果如下 找不到格式和 MIME 类型受支持的视频 这是我使用的代码
  • 替换来自 stdin bash 的 Curl 请求中的 JSON 正文

    我正在尝试使用来自标准输入的输入填充卷曲请求正文中的一个变量 echo 123 curl d query match number XPOST url com 不幸的是 没有被替换 我希望请求的正文与以下内容相匹配 query match
  • 使用 smtp 时 Hotmail SSL3 版本号错误

    我正在尝试使用 python 中的 hotmail smtp 服务器 但是 我的登录尝试出现了明显的 SSL3 版本号错误 如何更改我正在使用的版本以及如何调查此问题 gt gt s connect smtp live com 587 22
  • matplotlib 中的垂直线和水平线

    我不太明白为什么我无法在指定的限制内创建水平和垂直线 我想用这个框绑定数据 然而 双方似乎并没有遵守我的指示 为什么是这样 CREATING A BOUNDING BOX BOTTOM HORIZONTAL plt axhline y 4
  • Rails 中的求和与分组

    我有一个这样定义的表 Name Order Widget Count Bob 12311 6 Nancy 12555 8 BoB 87573 12 Nancy 12929 4 我想这样检索它们 Bob 18 Nancy 12 即 它们的小部
  • VB.Net(或 C#)2008 多线程导入

    我希望构建一个多线程文本导入工具 通常是 CSV 到 SQL Server 2005 并希望在 VB NET 中执行此操作 但我并不反对 C 我有 VS 2008 试用版 只是不知道从哪里开始 谁能指出我可以在哪里查看和使用源代码的方向VE
  • 将 T-SQL string_agg 转换为 LINQ C#

    我正在尝试将 T SQL 查询转换为 LINQ 我的查询 SELECT l Id s SystemSerial v LicenseVersion l CreatedDate STRING AGG sf Name as Features FR
  • 使用 jQuery 的日期选择器突出显示特定范围内的日期

    我需要突出显示开始日期和结束日期之间的日期 我应该能够指定这些日期 谁能帮我 您可以使用演出日之前活动 对于需要在日历中显示的每个日期都会调用它 它传入一个日期并返回一个数组 0 isSelectable 1 cssClass 2 Some
  • 根据点之间的距离排序?

    我有一个由 3 个点 A B 和 C 组成的向量 我想根据这些点之间的距离对该向量进行排序 假设最大距离是 B 和 C 之间的距离 而不是 C 和 A 之间的距离以及最后的 A 和 B 之间的距离 我怎样才能做到这一点 std sort v
  • 我的编辑按钮在下一页不起作用(仅在第一页起作用)

    我的编辑和删除查询仅适用于数据表的第一页 但不适用于第二页 我正在使用 ajax 和 jquery 我是新人 这是我在大学的项目 谢谢你帮助我 这是代码
  • 如何通过 ssh 将 echo 的输出重定向到文件

    我正在尝试通过 ssh 将变量的内容重定向到文件 喜欢 ssh MachineIP echo CM Config gt mName CM CONFIG where CM Config是我的主机中包含多行的局部变量 并且 mName CM C
  • 为什么我们必须重写 Java 中的 equals() 方法?

    我对我们覆盖的原因有些困惑 equals method 例如 Test test1 new Test 3 Test test2 new Test 3 The if comparison does the same thing that th
  • Windows 窗体应用程序错误与水晶报告

    我是Windows窗体应用程序的新手 这是我在办公室的第一次申请 我的办公室管理人员给了我源代码 但他们对此一无所知 我在 Visual Studio 12 64 位 上运行这些代码 一切都在运行 但水晶报表不起作用 抛出错误 错误是 无法
  • 创建 WPF 自定义控件

    我目前正在使用 WPF 创建用户界面 并希望在我的窗口上显示饼图 据我所知 这将涉及创建自定义控件 在这种情况下 将样式或模板应用于现有控件就足够了 我读过几篇关于自定义控件的文章 所有提供详细描述的文章似乎都使用了非常不同的方法 不同的作
  • 如何解析此输出并分隔每个字段/单词

    这是阿克沙塔 我陷入了解析以下数据的困境 我想单独获取每个单词 我可以有一个示例代码以便我可以继续吗 RTRV HDR RH01 SIMULATOR 09 11 18 16 13 19 M RH01 COMPLD RTRV EQPT ALL
  • 进程死亡后如何使用接口在活动之间进行通信?

    我正在构建一个 SDK 需要在活动之间实现回调 但没有实际完成活动 我之前使用 onActivityResult 将结果提供给调用者活动 但是 这会关闭活动 我需要传递回调 无需完成 SDK 的活动 我当前的实现 fun initializ