ios sqlite_在Android和iOS统一使用sqlite

本文介绍了如何在Unity开发环境中利用SQLite数据库实现Android和iOS平台的数据存储一致性,通过引用和翻译相关资源,阐述了在跨平台项目中使用sqlite的关键步骤和技术要点。
摘要由CSDN通过智能技术生成

ios sqlite

While going through the RudderStack SDK roadmap, we decided to include Unity next in the list of the supported platforms. Why Unity? We found that despite heavily relying on data, the hosted customer data management solutions out there underserve the gaming industry. This dependency on data is the result of a number of factors. Games generate a high volume of data, especially telemetry data coming from mobile devices while gaming companies face great difficulty in the monetization of their products.

在浏览RudderStack SDK路线图时,我们决定在支持的平台列表中加入Unity。 为什么选择Unity? 我们发现,尽管高度依赖数据,但托管的客户数据管理解决方案仍无法满足游戏行业的需求。 对数据的这种依赖性是许多因素的结果。 游戏产生大量数据,尤其是来自移动设备的遥测数据,而游戏公司则在其产品货币化方面面临巨大困难。

背景 (Background)

RudderStack promises to capture user-generated events. We route these events to various destinations, such as cloud applications, data warehouses, and cloud file systems.

RudderStack承诺捕获用户生成的事件。 我们将这些事件路由到各个目的地,例如云应用程序,数据仓库和云文件系统。

For the successful implementation of the Unity SDK, we had four main requirements:

为了成功实施Unity SDK,我们有四个主要要求:

  1. The SDK should implement initialization and event collection mechanisms.

    SDK应该实现初始化和事件收集机制。
  2. Events should be temporarily stored on persistent storage to ensure the delivery semantics that we want for the platform.

    事件应该暂时存储在持久性存储中,以确保我们希望平台具有交付语义。
  3. Events should be pushed to the RudderServer. These events will run in different environments.

    事件应推送到RudderServer。 这些事件将在不同的环境中运行。
  4. Finally, there should be a mechanism to handle device-mode SDKs. Device-mode SDKs support a number of destinations that cannot be supported through the RudderStack server-to-server integration. Another intention for device-mode support was to leverage the power of Native SDKs specifically for push notifications, attributions, and more.

    最后,应该有一种处理设备模式SDK的机制。 设备模式SDK支持RudderStack服务器到服务器集成无法支持的许多目标。 设备模式支持的另一个意图是利用本机SDK的功能专门用于推送通知,归因等。

挑战 (The Challenges)

为Unity选择正确的C#版本 (Choosing the correct C# version for Unity)

The first challenge we encountered was choosing the correct C# version. Unity supports .Net 3.5, .Net 4.x, and .Net Standard. However, there are some major changes and incompatibilities between versions 3.5 and 4.x. Many popular games are built on version 3.5. Migrating to 4.x is such a painful process that studios prefer staying on older versions.

我们遇到的第一个挑战是选择正确的C#版本。 Unity支持.Net 3.5,.Net 4.x和.Net标准。 但是,版本3.5和4.x之间有一些重大更改和不兼容。 许多流行的游戏均基于3.5版。 迁移到4.x是一个痛苦的过程,工作室更喜欢使用旧版本。

We started developing the Unity SDKs with .Net 4.x. Soon after the release, we realized that many games couldn’t use our SDK because of being developed in version 3.5. In the end, we ended up rewriting around 80 percent of the codebase to go back to version 3.5.

我们开始使用.Net 4.x开发Unity SDK。 发行后不久,我们意识到由于在3.5版中进行了开发,因此许多游戏无法使用我们的SDK。 最后,我们最终重写了大约80%的代码库以返回到3.5版。

从中吸取的教训 (Lesson learned from this)

Always research well on the following BEFORE you start developing:

在开始开发之前,请务必对以下方面进行深入研究:

  1. Which is the most adopted software version in the industry?

    哪个行业中采用最广泛的软件版本?
  2. What’s the adoption of the SDKs relevant to our project? Learn and prioritize by identifying what developers are using and why.

    与我们的项目相关的SDK的采用情况如何? 通过确定开发人员正在使用什么以及为什么使用来确定优先级。

These are important considerations, both from the product and engineering points of view.

从产品和工程的角度来看,这些都是重要的考虑因素。

安全一致地将Unity数据交付到所选目的地 (Delivering Unity data securely and consistently to the chosen destination)

Now came the main challenge, which is also the reason we wrote this blog post. Capturing and transmitting data over unreliable networks is a difficult task. It’s futile to capture data if you don’t manage to deliver it securely and consistently to the chosen destination. One way to deliver the data securely and consistently is to have some kind of caching mechanism to store the events. This mechanism stores the events until you receive the acknowledgment that the data is delivered correctly to the destination.

现在是主要的挑战,这也是我们撰写此博客文章的原因。 在不可靠的网络上捕获和传输数据是一项艰巨的任务。 如果您无法安全,一致地将数据传送到所选目的地,则捕获数据是徒劳的。 安全且一致地传递数据的一种方法是具有某种缓存机制来存储事件。 此机制将存储事件,直到您收到确认数据已正确传递到目标的确认为止。

You can store events on Unity using PlayerPrefs. However, we had three major constraints:

您可以使用PlayerPrefs将事件存储在Unity上。 但是,我们有三个主要限制:

  1. We should be able to store an event buffer of arbitrary size. The default buffer size we use in Rudder is 10K events. However, for this case, we wanted to be able to scale it up and down.

    我们应该能够存储任意大小的事件缓冲区。 我们在Rudder中使用的默认缓冲区大小是10K事件。 但是,对于这种情况,我们希望能够将其放大和缩小。
  2. There should be the flexibility of defining and enforcing a schema on the events that are stored. PlayerPrefs is a simple key-value store supporting a very limited set of data types and no hierarchy.

    应该具有灵活性,可以在存储的事件上定义和实施模式。 PlayerPrefs是一个简单的键值存储,仅支持非常有限的一组数据类型,并且没有层次结构。
  3. Managing the buffer should not happen in the main thread. Working with PlayerPrefs happens in the main thread, which is not a viable option in most cases.

    管理缓冲区不应在主线程中发生。 在主线程中使用PlayerPrefs,这在大多数情况下不是可行的选择。

Instead of PlayerPrefs, we decided to use SQLite for this purpose. SQLite is a lightweight, embeddable, and highly reliable SQL database engine that works incredibly well for mobile devices. However, embedding SQLite in Unity is not straightforward.

为此,我们决定使用SQLite代替PlayerPrefs。 SQLite是一种轻量级,可嵌入且高度可靠SQL数据库引擎,非常适合移动设备使用。 但是,将SQLite嵌入Unity并不容易。

实施 (The Implementation)

入门 (Getting started)

.Net and Unity do not support SQLite out of the box. We also wanted to minimize the number of third-party libraries used as much as possible to keep the SDK as lightweight as possible. Collecting events should be extremely lightweight without introducing bugs and a large number of dependencies.

.Net和Unity不支持SQLite。 我们还希望尽可能减少使用的第三方库的数量,以使SDK尽可能轻巧。 收集事件应该是非常轻量级的,而不会引入错误和大量依赖关系。

Initially, we tried to build separate binaries for supporting SQLite and integrating with the Unity SDK. The problem here is that this design is painful to deploy and maintain in production as it is not thread-safe.

最初,我们尝试构建单独的二进制文件以支持SQLite并与Unity SDK集成。 这里的问题是,由于这种设计不是线程安全的,因此很难在生产中进行部署和维护。

Unity插件支持Android和iOS (Unity plugins support for Android and iOS)

Thus, we finally decided to follow a popular Unity pattern that includes the exploitation of the excellent plugins supports that Unity offers.

因此,我们最终决定遵循一种流行的Unity模式,其中包括利用Unity提供的出色插件支持。

Unity supports code for Android and iOS simultaneously using preprocessor directives. It controls the lines of code that will be executed depending on the platform. We built Android SDK and iOS plugins separately for Unity by exploiting this Unity offering.

Unity使用预处理器指令同时支持Android和iOS的代码。 它控制将根据平台执行的代码行。 通过利用此Unity产品,我们分别为Unity构建了Android SDK和iOS插件。

For example, our iOS plugin for Unity has a method for initialization of the RudderClient that looks like the following code snippet:

例如,我们的适用于Unity的iOS插件提供了一种初始化RudderClient的方法,该方法类似于以下代码片段:

+ (void) _initiateInstance: (NSString*) _anonymousId
writeKey: (NSString*) _writeKey
endPointUrl: (NSString*) _endPointUrl
flushQueueSize: (int) _flushQueueSize
dbCountThreshold: (int) _dbCountThreshold
sleepTimeOut: (int) _sleepTimeout
configRefreshInterval: (int) _configRefreshInterval
trackLifecycleEvents: (BOOL) _trackLifecycleEvents
recordScreenViews: (BOOL) _recordScreenViews
logLevel: (int) _logLevel;

This takes all the necessary parameters from the Unity bridge and initiates the RudderClient. We have a similar method for Android as follows:

这将从Unity桥中获取所有必要的参数,并启动RudderClient 。 我们为Android提供了一种类似的方法,如下所示:

public static void _initiateInstance(
Context _context,
String _anonymousId,
String _writeKey,
String _endPointUrl,
int _flushQueueSize,
int _dbCountThreshold,
int _sleepTimeout,
int _configRefreshInterval,
boolean _trackLifecycleEvents,
boolean _recordScreenViews,
int _logLevel
)

Now, when we want to call these methods from our C# code, we can control the calling of these methods as well as the platform using the preprocessor directives at run time. The code in C# looks like this:

现在,当我们想从C#代码中调用这些方法时,我们可以在运行时使用预处理器指令来控制这些方法以及平台的调用。 C#中的代码如下所示:

#if UNITY_IPHONE
[DllImport("__Internal")]
private static extern void _initiateInstance(
string _anonymousId,
string _writeKey,
string _endPointUrl,
int _flushQueueSize,
int _dbCountThreshold,
int _sleepTimeout,
int _configRefreshInterval,
bool _trackLifecycleEvents,
bool _recordScreenViews,
int _logLevel
);
#endif

This code defines the method to be used for the iPhone environment. When we need to call a method, we can check the current runtime and call this method using the code snippet below:

这段代码定义了用于iPhone环境的方法。 当我们需要调用方法时,我们可以检查当前的运行时并使用下面的代码片段调用此方法:

#if UNITY_IPHONE
if (Application.platform == RuntimePlatform.IPhonePlayer)
{
_initiateInstance(
RudderCache.GetAnonymousId(),
_writeKey,
_endPointUrl,
_flushQueueSize,
_dbCountThreshold,
_sleepTimeout,
_configRefreshInterval,
_trackLifecycleEvents,
_recordScreenViews,
_logLevel
);
}
#endif

Similarly, for Android, we have to define an AndroidJavaClass object inside our Unity code so that we can access its methods in our code. To do this, please use the following code:

同样,对于Android,我们必须在Unity代码中定义一个AndroidJavaClass对象,以便我们可以在代码中访问其方法。 为此,请使用以下代码:

#if UNITY_ANDROID
private static readonly string androidClientName = "com.rudderstack.android.sdk.wrapper.RudderClientWrapper";private static AndroidJavaClass androidClientClass;AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject context = activity.Call<AndroidJavaObject>("getApplicationContext");
androidClientClass = new AndroidJavaClass(androidClientName);
#endif

Finally, to call the initiateInstance method from our Android Plugin, we will check the runtime and call using the callStatic method of the AndroidJavaClass object as shown below:

最后,调用initiateInstance从我们的Android插件的方法,我们将检查运行,并调用使用callStatic的方法AndroidJavaClass对象如下图所示:

#if UNITY_ANDROID
if (Application.platform == RuntimePlatform.Android)
{
androidClientClass.CallStatic(
"_initiateInstance",
context,
RudderCache.GetAnonymousId(),
_writeKey,
_endPointUrl,
_flushQueueSize,
_dbCountThreshold,
_sleepTimeout,
_configRefreshInterval,
_trackLifecycleEvents,
_recordScreenViews,
_logLevel
);
}
#endif

使用Unity Plugins目录 (Using the Unity Plugins directory)

In addition, Unity supports a special kind of directory under its Assets directory named Plugins. This directory holds all the platform-specific libraries required for the application. All it takes is to create two folders, one named Android and another one named iOS inside the Plugins folder. Unity will automatically add those files with the final product/build of your project for the corresponding platform.

另外,Unity在其Assets目录下支持一种特殊的目录,名为Plugins 。 该目录包含该应用程序所需的所有特定于平台的库。 要做的就是在Plugins文件夹中创建两个文件夹,一个名为Android ,另一个名为iOS 。 Unity会自动将这些文件与您项目的最终产品/内部版本添加到相应平台上。

We leveraged this mechanism and built plugins for both Android and iOS. We added these plugins to the project following the directory structure mentioned above. This way, Unity is able to interop with both JAVA for Android and Object-C for iOS. Due to this, we get a lot of flexibility in terms of the technologies we can interact with. In our case, we could embed SQLite for both Android and iOS in Unity.

我们利用这种机制,为Android和iOS构建了插件。 我们按照上述目录结构将这些插件添加到项目中。 这样,Unity可以与Android的JAVA和iOS的Object-C互操作。 因此,我们可以与之交互的技术具有很大的灵活性。 在我们的案例中,我们可以在Unity中同时为Android和iOS嵌入SQLite。

Following this approach, we ended up having two plugins, one for each platform and a wrapper Unity app that interops with these plugins to expose the functionality we need.

按照这种方法,我们最终得到了两个插件,每个平台一个,还有一个包装器Unity应用程序,与这些插件互操作以展示我们所需的功能。

What just happened:

刚才发生了什么:

To quickly summarize, the wrapper is written in C# and it takes care of our first requirement of being able to manage the size of the event cache on the client-side. The plugins take care of the rest of the requirements we mentioned at the beginning of the post.

为了快速总结,包装器是用C#编写的,它满足了我们能够管理客户端事件缓存大小的第一个要求。 插件将满足我们在文章开头提到的其余要求。

添加新目的地 (Adding new destinations)

Finally, we wanted to offer as many destinations as possible that are relevant to Unity and mobile development. The most relevant destinations for Unity are Adjust and Firebase. For these two, we support what we call device-mode SDKs. The device-mode SDKs allow events to be pushed to destinations without the need to go through the RadderStack Server.

最后,我们希望提供与Unity和移动开发相关的尽可能多的目的地。 与Unity最为相关的目的地是Adjust和Firebase。 对于这两个,我们支持所谓的设备模式SDK。 设备模式SDK允许将事件推送到目的地,而无需通过RadderStack Server。

We decided to build the Device-Mode mechanism in Unity itself and use the Unity SDKs of the platforms. In this way, you can use the platform-supported native code, which offers greater reliability and support for the long term.

我们决定在Unity本身中构建Device-Mode机制 ,并使用平台的Unity SDK。 这样,您可以使用平台支持的本机代码,从而提供更高的可靠性和长期支持。

结论 (Conclusion)

Unity is a feature-rich and versatile game development platform. We wanted to offer gamer developers functionalities around data that are commonly accessible to mobile and server-side developers. Although supporting an embedded database is not trivial in Unity, the platform allows you to do it in an elegant way through the plugin system.RudderStack and all the SDKs are open-source and available on GitHub. You can also find the detailed documentation for this Unity SDK here. We’d love to hear your feedback on how to improve our SDKs.

Unity是功能丰富且用途广泛的游戏开发平台。 我们想为游戏开发人员提供围绕移动和服务器端开发人员通常可以访问的数据的功能。 尽管在Unity中支持嵌入式数据库并不是一件容易的事,但该平台允许您通过插件系统以优雅的方式完成它.RudderStack和所有SDK都是开源的,可在GitHub上获得 。 您还可以在此处找到有关此Unity SDK的详细文档。 我们很高兴听到您对如何改进我们的SDK的反馈。

翻译自: https://medium.com/@RudderStack/using-sqlite-on-unity-for-android-and-ios-16c3da6ba1bf

ios sqlite

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值