在这里,我们将在 Android 端集成相同的 SDK,看看我们如何使用我们的平台通道在 Dart 和 Android 之间传输数据。
在 Android 中,实现将类似于在 IOS 中的实现。在这里,我们将使用MethodChannel和EventChannelClass 来实现我们的平台通道。因此,首先让我们进行设置Comet Chat,然后通过一些示例,我们将看到通道的实现。
example/android使用 Android Studio打开文件夹。对于编写原生 android 代码,我建议使用 Android Studio IDE,因为这样可以更容易地调试代码,而且我们可以在不使用任何外部插件或包的情况下访问类和接口的定义。在左侧的 IDE 中,您可以看到一个包含示例/android 应用程序代码的应用程序文件夹和另一个flutter_comet_chat_sdk包含 android 插件代码的应用程序文件夹。所以如果我们去
flutter_comet_chat_sdk -> java -> com.example.flutter_comet_chat_sdk
在那里我们可以找到类FlutterCometChatSdkPlugin.java文件。在这个文件中,我们将实现我们的平台通道。要设置Comet ChatSDK,请在根目录中android/build.gradle添加 Gradle 依赖项。
. . .
rootProject.allprojects {
repositories {
google()
mavenCentral()
maven {
url "https://dl.cloudsmith.io/public/cometchat/cometchat-pro-android/maven/"
}
}
}
. . .
android {
compileSdkVersion 30
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
minSdkVersion 21
}
}
. . .
. . .
dependencies {
implementation 'com.cometchat:pro-android-chat-sdk:3.0.4'
}
由于minSdkVersion所需的Comet Chat是 21,因此我们example/android/app/build.gradle还需要在其中添加它。
defaultConfig {
applicationId "com.example.flutter_comet_chat_sdk_example"
minSdkVersion 21
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
一切都设置好了,现在将项目与Gradle文件同步。 文件 -> 将项目与 Gradle 文件同步。
我们的设置已准备就绪,您可以在 android 设备或模拟器上运行该应用程序。现在,我们将使用Comet ChatSDK 的一些方法来看看Platform通道是如何工作的。
要定义我们的MethodChannel,我们将创建一个类MethodChannelHandler和 for EventChannel, EventChannelHelper。在FlutterCometChatSdkPlugin课堂上,我们将注册我们的PlatformChannels. 正如我们在 IOS 中所做的那样,这里的FlutterCometChatSdkPlugin类也将实现FlutterPlugin接口。它提供了一种设置和注册插件的方法。
在 android 中,我们必须重写两个方法 -onAttachedToEngine和onDetachedFromEngine. 然后,当被添加到 的实例onAttachedToEngine时被调用。如果被删除或被销毁,则调用。FlutterPluginFlutterEngineFlutterPluginFlutterEngineonDetachedFromEngine
为了注册MethodChannel我们需要, 和BinaryMessenger提供的。用于通过特定的 or 与 Dart 代码进行通信。因此,在方法中,我们将定义我们的和 in ,我们将方法通道设置为 nil。onAttachedToEngineFlutterPlugin.FlutterPluginBindingBinaryMessengerEventChannelMethodChannelonAttachedToEnginePlatformChannelonDetachedFromEngine
package com.example.flutter_comet_chat_sdk;
import android.content.Context;
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodChannel;
/** FlutterCometChatSdkPlugin */
public class FlutterCometChatSdkPlugin implements FlutterPlugin {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private MethodChannel channel;
private EventChannelHelper loginEventTrigger;
private EventChannelHelper loginEventListener;
@Override
public void onAttachedToEngine(@NonNull FlutterPlugin.FlutterPluginBinding flutterPluginBinding) {
setupChannels(flutterPluginBinding.getBinaryMessenger(), flutterPluginBinding.getApplicationContext());
}
private void setupChannels(BinaryMessenger messenger, Context context) {
channel = new MethodChannel(messenger, "plugins.flutter.io/comet_chat_dart");
loginEventTrigger = new EventChannelHelper(messenger, "plugins.flutter.io/login_event_trigger");
loginEventListener = new EventChannelHelper(messenger, "plugins.flutter.io/login_event_listener");
MethodChannelHandler methodChannelHandler = new MethodChannelHandler(context, loginEventTrigger,loginEventListener);
channel.setMethodCallHandler(methodChannelHandler);
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPlugin.FlutterPluginBinding binding) {
channel.setMethodCallHandler(null);
}
}
在这里,我们已经注册了我们的事件触发器和事件监听器以及方法通道。让我们为MethodChannel.
我们的MethodChannelHandler类将实现接口MethodChannel.MethodCallHandler。此接口提供者方法onMethodCall将用于接收来自 Flutter 的方法调用。在onMethodCall函数中,我们将为我们的初始化Comet ChatSDK 方法添加方法调用。此外,在同一个类中,我们定义了初始化方法,并使用MethodChannel.Result.
public class MethodChannelHandler implements MethodChannel.MethodCallHandler {
private Context applicationContext;
private EventChannelHelper loginEventTrigger;
private EventChannelHelper loginEventListener;
private LoginLogoutHelper loginHandler;
private GetValue getValue;
public MethodChannelHandler(Context context, EventChannelHelper loginEventTrigger, EventChannelHelper loginEventListener) {
this.getValue = new GetValue();
this.applicationContext = context;
this.loginEventTrigger = loginEventTrigger;
this.loginEventListener = loginEventListener;
this.loginHandler = new LoginLogoutHelper(loginEventTrigger, loginEventListener);
}
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
switch (call.method) {
case "initialize":
initializeApp(result, getValue.getValue(call, "appId"), getValue.getAppSettings(call, "appSettings"));
break;
default:
result.notImplemented();
}
}
private void initializeApp(MethodChannel.Result result, String appID, AppSettings appSettings) {
CometChat.init(applicationContext, appID, appSettings, new CometChat.CallbackListener() {
@Override
public void onSuccess(String successMessage) {
Log.d("Initilisation", "Initialization completed successfully");
result.success(true);
}
@Override
public void onError(CometChatException e) {
Log.d("Initilisation", "Initialization failed with exception: " + e.getMessage());
result.success(e.toString());
}
});
}
}
该类GetValue具有检查来自 Dart 端的数据对于 String、int、Boolean 等常见数据类型是否为空的方法。对于 JSON,它Comet Chat通过映射键和值将数据转换为类对象。
在上面的代码中,我们使用了getValue检查调用参数是否为空的方法,如果不为空,它会为我们提供String值。
public String getValue(@NonNull MethodCall call, String argument) {
return call.argument(argument) != null ? call.argument(argument).toString() : null;
}
另一个getAppSettings映射出值的方法AppSettings使用AppSettingsBuilder创建一个AppSettings类的对象,然后将其传递给初始化方法。查看thisGitHub 代码以获取 GetValue 类的完整代码。
在 Dart 方面,我们可以将此方法与initialize键一起使用,并使用它们各自的键传递参数。同样正如我们在 IOS 部分看到的,我们也必须AppSettings在 Dart 端创建类。为此,我们将创建一个AppSettingsBuilder类,该类将设置AppSettings类属性的值,然后 build 方法将返回AppSettings我们传递给本机端的类对象。
init(String appId, AppSettings appSettings,
CallbackListener callbackListener) async {
assert(appId != '');
assert(appSettings.region != '');
try {
dynamic res = await FlutterCometChatDart.channel.invokeMethod(
'initialize', {'appId': appId, 'appSettings': appSettings.toJson()});
if (res == true) {
callbackListener.onSuccess(true);
} else {
callbackListener.onError(res);
}
} on PlatformException catch (e) {
throw FlutterCometChatDart.convertException(e);
}
}
我们将创建一个EventChannelHelper类来处理我们所有的EventChannels. 首先在这个类中,我们将创建一个对象EventChannel.EventSink。使用该对象,我们可以访问error和success方法。该error方法使用带有 params error code、error message和错误详细信息的对象的错误事件。另一方面,该success方法接受一个成功事件对象。
之后,我们将定义一个Handler将附加到应用程序主线程的Looper.getMainLooper(). 使用该post方法,我们将Runnable(在我们的例子中是事件接收器错误或成功)添加到消息队列中,这将允许我们发送和Message处理Runnable.
然后在EventChannelHelper构造函数中,我们将创建一个EventChannel类的对象(用于使用异步事件流与 Flutter 进行通信)。EventChannel该类将采用二进制信使进行事件编码和我们要设置的特定名称EventChannel。创建对象后,我们将使用setStreamHandler在我们的EventChannel.
将setStreamHandler提供EventChannel.StreamHandlerwhich 将提供两种方法onListen(Object, EventChannel.EventSink)和onCancel(Object). 在onListen下面,我们将在事件接收器中设置事件,在onCancel我们将取消注册或将事件接收器设置为 nil。该synchronized关键字用于确保一次只有一个线程使用这些synchronized方法。同步块将在同一个对象上同步,即EventSink接口对象。
public class EventChannelHelper {
public Handler handler;
private EventChannel.EventSink eventSink;
public EventChannelHelper(BinaryMessenger messenger, String id) {
handler = new Handler(Looper.getMainLooper());
EventChannel eventChannel = new EventChannel(messenger, id);
eventChannel.setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(final Object arguments, final EventChannel.EventSink eventSink) {
synchronized (EventChannelHelper.this) {
EventChannelHelper.this.eventSink = eventSink;
}
}
@Override
public void onCancel(final Object arguments) {
synchronized (EventChannelHelper.this) {
eventSink = null;
}
}
});
}
public synchronized void error(String errorCode, String errorMessage, Object errorDetails) {
if (eventSink == null)
return;
handler.post(() -> eventSink.error(errorCode, errorMessage, errorDetails));
}
public synchronized void success(Object event) {
if (eventSink == null)
return;
handler.post(() -> eventSink.success(event));
}
}
现在我们的EventChannel 已经设置好了,让我们实现Comet Chatlogin 方法来看看它是如何工作的。
首先,在MethodChannelHandler类中,我们将为登录方法定义我们的方法调用。
. . .
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
switch (call.method) {
case "initialize":
initializeApp(result, getValue.getValue(call, "appId"), getValue.getAppSettings(call, "appSettings"));
break;
case "login":
loginHandler.selectLoginMethod(getValue.getValue(call, "authToken"), getValue.getValue(call, "authKey"),
getValue.getValue(call, "UID"));
result.success("Success");
break;
. . .
这里selectLoginMethod将决定用户是否要使用authKey或登录authToken。(authKey 和 authToken 可以通过在 上创建应用程序来生成Comet Chat Pro website,使用 v3 版本来创建应用程序,因为我们在这个项目中使用它作为参考)。
在这个演示中,我将使用 login 方法authToken来展示通过我们的事件通道传输数据的流程。完整代码可以参考this repo。
我们将创建一个类,我们将在其中定义SDKLoginLogoutHelper的身份验证方法。Comet Chat该类LoginLogoutHelper将获取EventChannelHelper用于将数据传递给流的对象。
public class LoginLogoutHelper {
private String UID;
private String authKey;
private String authToken;
private String uniqueLoginListenerID;
private static final String TAG = "CometChat";
private EventChannelHelper loginEventTrigger;
private EventChannelHelper loginEventListener;
public LoginLogoutHelper(EventChannelHelper loginEventTrigger, EventChannelHelper loginEventListener) {
this.loginEventTrigger = loginEventTrigger;
this.loginEventListener = loginEventListener;
}
. . .
现在我们将定义我们的login方法。在那里,我们将创建一个HashMap变量,该变量将根据成功或错误存储键和值。在 下onSuccess,我们正在获取User我们将转换为 JSON 字符串并传递给success我们EventChannelHelper类的方法的对象。在 中onError,我们将错误对象传递给事件流。
public void loginWithAuthToken() {
Map map = new HashMap<>();
CometChat.login(authToken, new CometChat.CallbackListener() {
@Override
public void onSuccess(User user) {
Log.d(TAG, "Login Successful : " + user);
map.put("loginSuccess", user.toJson().toString());
loginEventTrigger.success(map);
}
@Override
public void onError(CometChatException e) {
Log.d(TAG, "Login failed with exception: " + e.getMessage());
map.put("loginFailure", e.toString());
loginEventTrigger.error(e.getCode(), e.getMessage(), map);
}
});
}
在 Dart 方面,我们将使用这些键来获取用户数据并使用类中fromJson定义的方法对其进行转换User。
定义EventChannel和StreamSubscription在 Dart 方面。
static const EventChannel loginEventTrigger =
EventChannel('plugins.flutter.io/login_event_trigger');
late StreamSubscription _loginEventsubscription;
订阅事件流以接收数据。
Stream loginEventTrigger() {
return FlutterCometChatDart.loginEventTrigger.receiveBroadcastStream();
}
定义登录方法CallbackListener,我们在哪里解码事件流中的 JSON 字符串并将其序列化到 Dart 端的对象。
login(
{String? authKey,
String? uid,
String? authToken,
required CallbackListener callbackListener}) {
assert((uid != '' && authKey != '') || (authToken != ''));
_auth.login(authKey: authKey, uid: uid, authToken: authToken);
_loginEventsubscription = _auth.loginEventTrigger().listen((event) {
if (event.containsKey(CometConstantsKeys.LOGIN_SUCCESS)) {
var data = jsonDecode(event[CometConstantsKeys.LOGIN_SUCCESS]);
callbackListener.onSuccess(User.fromJson(data));
}
if (event.containsKey(CometConstantsKeys.LOGIN_FAILURE)) {
callbackListener.onError(event);
}
});
cancelSubscription(_loginEventsubscription);
}
最后,我们完成了PlatformAndroid和IOS频道的设置。综上,我们通过集成SDK学习了Platform渠道的实现。Comet Chat此外,我们探索了如何在方法调用中传递参数并在 Dart 和原生端之间共享流数据。然后,我们以这样一种方式构建我们的应用程序,即我们可以在我们的示例应用程序或任何其他 Flutter 应用程序中使用实现的 SDK 作为插件。
如果大伙有什么好的学习方法或建议欢迎大家在评论中积极留言哈,希望大家能够共同学习、共同努力、共同进步。
小编在这里祝小伙伴们在未来的日子里都可以 升职加薪,当上总经理,出任CEO,迎娶白富美,走上人生巅峰!!
不论遇到什么困难,都不应该成为我们放弃的理由!
很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从那里入手去学习,需要一份小编整理出来的学习资料的关注我主页或者点击文末微信卡片即可免费领取~
这里是关于我自己的Android 学习,面试文档,视频收集大整理,有兴趣的伙伴们可以看看~
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。