Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronous API for creating instances and Hot-reload fix #18

Merged
merged 15 commits into from
Jul 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions DeveloperNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,20 @@ Some files in the project are generated to maintain sync between
Generated file paths are configured as values in `bin/codegen.dart` for `toGenerate` Map

[Read about generation of platform specific constant files](bin/README.md)


## Static plugin code analyzer

The new flutter analyzer does a great job at analyzing complete flutter package.

Running `flutter analyze` in project root will analyze dart files in complete project,
i.e., plugin code and example code


Or, use the good old dart analyzer

```bash
dartanalyzer --fatal-warnings lib/**/*.dart

dartanalyzer --fatal-warnings example/lib/**/*.dart
```
30 changes: 14 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,6 @@ site to discuss privately.
import 'package:ably_flutter_plugin/ably.dart' as ably;
```

##### Create an Ably instance

```dart
final ably.Ably ablyPlugin = ably.Ably();
```

##### create a ClientOptions

```dart
Expand All @@ -57,7 +51,7 @@ clientOptions.logLevel = ably.LogLevel.verbose; //optional
##### Rest API

```dart
ably.Rest rest = await ablyPlugin.createRest(options: clientOptions);
ably.Rest rest = ably.Rest(options: clientOptions);
```

Getting a channel instance
Expand All @@ -69,20 +63,24 @@ ably.RestChannel channel = rest.channels.get('test');
Publish rest messages

```dart
//passing both name and data
await channel.publish(name: "Hello", data: "Ably");
//passing just name
await channel.publish(name: "Hello");
//passing just data
await channel.publish(data: "Ably");
//publishing an empty message
await channel.publish();
void publishMessages() async {
//passing both name and data
await channel.publish(name: "Hello", data: "Ably");
//passing just name
await channel.publish(name: "Hello");
//passing just data
await channel.publish(data: "Ably");
//publishing an empty message
await channel.publish();
}

publishMessages();
```

##### Realtime API

```dart
ably.Realtime realtime = await ablyPlugin.createRealtime(options: clientOptions);
ably.Realtime realtime = ably.Realtime(options: clientOptions);
```

Listen to connection state change event
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import android.os.Looper;

import io.ably.flutter.plugin.generated.PlatformConstants;
import io.ably.lib.realtime.ChannelStateListener;
import io.ably.lib.realtime.ConnectionStateListener;
import io.flutter.plugin.common.EventChannel;

Expand All @@ -16,15 +15,13 @@
* ref: https://api.flutter.dev/javadoc/io/flutter/plugin/common/EventChannel.StreamHandler.html
* */
public class AblyEventStreamHandler implements EventChannel.StreamHandler {
private final AblyMethodCallHandler methodCallHandler;

/**
* Constructor requiring methodCallHandler, as it is a singleton and has all instances stored
* Event listening can be started on an instance picked from the stored instances
* Creating an ablyLibrary instance.
* As ablyLibrary is a singleton,
* all ably object instance will be accessible
* */
AblyEventStreamHandler(AblyMethodCallHandler methodCallHandler){
this.methodCallHandler = methodCallHandler;
}
private final AblyLibrary ablyLibrary = AblyLibrary.getInstance();

/**
* Refer to the comments on AblyMethodCallHandler.MethodResultWrapper
Expand Down Expand Up @@ -62,26 +59,35 @@ private class Listener{
}

private class PluginConnectionStateListener extends Listener implements ConnectionStateListener {
PluginConnectionStateListener(EventChannel.EventSink eventSink){super(eventSink);}

PluginConnectionStateListener(EventChannel.EventSink eventSink){
super(eventSink);
}

public void onConnectionStateChanged(ConnectionStateChange stateChange){
eventSink.success(stateChange);
}

}

// Casting stream creation arguments from `Object` into `AblyMessage`
private AblyFlutterMessage<String> getMessage(Object message){
return ((AblyFlutterMessage<AblyFlutterMessage<String>>)message).message;
}

@Override
public void onListen(Object object, EventChannel.EventSink uiThreadEventSink) {
MainThreadEventSink eventSink = new MainThreadEventSink(uiThreadEventSink);
methodCallHandler.<AblyFlutterMessage<String>>ablyDo((AblyFlutterMessage)object, (ablyLibrary, message) -> {
String eventName = message.message;
switch(eventName) {
case PlatformConstants.PlatformMethod.onRealtimeConnectionStateChanged:
connectionStateListener = new PluginConnectionStateListener(eventSink);
ablyLibrary.getRealtime(message.handle).connection.on(connectionStateListener);
return;
default:
eventSink.error("unhandled event", null, null);
}
});
AblyFlutterMessage<String> message = getMessage(object);
String eventName = message.message;
switch(eventName) {
case PlatformConstants.PlatformMethod.onRealtimeConnectionStateChanged:
connectionStateListener = new PluginConnectionStateListener(eventSink);
ablyLibrary.getRealtime(message.handle).connection.on(connectionStateListener);
return;
default:
eventSink.error("unhandled event", null, null);
}
}

@Override
Expand All @@ -90,15 +96,12 @@ public void onCancel(Object object) {
System.out.println("Cannot process null input on cancel");
return;
}
methodCallHandler.<AblyFlutterMessage<String>>ablyDo((AblyFlutterMessage)object, (ablyLibrary, message) -> {
String eventName = message.message;
switch (eventName) {
case PlatformConstants.PlatformMethod.onRealtimeConnectionStateChanged:
ablyLibrary.getRealtime(message.handle).connection.off(connectionStateListener);
case PlatformConstants.PlatformMethod.onRealtimeChannelStateChanged:
// ablyLibrary.getRealtime(handle).connection.off(connectionStateListener);
}
});
AblyFlutterMessage<String> message = getMessage(object);
String eventName = message.message;
switch (eventName) {
QuintinWillison marked this conversation as resolved.
Show resolved Hide resolved
case PlatformConstants.PlatformMethod.onRealtimeConnectionStateChanged:
ablyLibrary.getRealtime(message.handle).connection.off(connectionStateListener);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ class AblyFlutterMessage<T> {
final Long handle;
final T message;

AblyFlutterMessage(final Long handle, final T message) {
if (null == handle) {
throw new NullPointerException("handle cannot be null.");
}
AblyFlutterMessage(final T message, final Long handle) {
if (null == message) {
throw new NullPointerException("message cannot be null.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import androidx.annotation.NonNull;

import app.loup.streams_channel.StreamsChannel;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodChannel;
Expand Down Expand Up @@ -35,14 +34,17 @@ public static void registerWith(Registrar registrar) {

private static void setupChannels(BinaryMessenger messenger){
MethodCodec codec = createCodec();
AblyMethodCallHandler methodCallHandler = AblyMethodCallHandler.getInstance();

final MethodChannel channel = new MethodChannel(messenger, "io.ably.flutter.plugin", codec);
channel.setMethodCallHandler(methodCallHandler);

final StreamsChannel streamsChannel = new StreamsChannel(messenger, "io.ably.flutter.stream", codec);
streamsChannel.setStreamHandlerFactory(arguments -> new AblyEventStreamHandler(methodCallHandler));
streamsChannel.setStreamHandlerFactory(arguments -> new AblyEventStreamHandler());

AblyMethodCallHandler methodCallHandler = AblyMethodCallHandler.getInstance(
// Streams channel will be created on `register` method call
// and also on every hot-reload
streamsChannel::reset
);
final MethodChannel channel = new MethodChannel(messenger, "io.ably.flutter.plugin", codec);
channel.setMethodCallHandler(methodCallHandler);
}

@Override
Expand Down
31 changes: 12 additions & 19 deletions android/src/main/java/io/ably/flutter/plugin/AblyLibrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,53 +8,46 @@
import io.ably.lib.types.ClientOptions;

class AblyLibrary {
private boolean _disposed = false;

private static AblyLibrary _instance;
private long _nextHandle = 1;

// privatizing default constructor to enforce usage of getInstance
private AblyLibrary(){}

static synchronized AblyLibrary getInstance() {
if (null == _instance) {
_instance = new AblyLibrary();
}
return _instance;
}

// using LongSparseArray as suggested by Studio
// and as per this answer https://stackoverflow.com/a/31413003
private final LongSparseArray<AblyRest> _restInstances = new LongSparseArray<>();
private final LongSparseArray<AblyRealtime> _realtimeInstances = new LongSparseArray<>();

private void assertNotDisposed() {
if (_disposed) {
throw new IllegalStateException("Instance disposed.");
}
}

long createRealtime(final ClientOptions clientOptions) throws AblyException {
assertNotDisposed();

final AblyRealtime realtime = new AblyRealtime(clientOptions);
_realtimeInstances.put(_nextHandle, realtime);
return _nextHandle++;
}

long createRest(final ClientOptions clientOptions) throws AblyException {
assertNotDisposed();

final AblyRest rest = new AblyRest(clientOptions);
_restInstances.put(_nextHandle, rest);
return _nextHandle++;
}

AblyRealtime getRealtime(final long handle) {
assertNotDisposed();

return _realtimeInstances.get(handle);
}

AblyRest getRest(final long handle){
assertNotDisposed();

return _restInstances.get(handle);
}

void dispose() {
assertNotDisposed();

_disposed = true;

for(int i=0; i<_realtimeInstances.size(); i++){
long key = _realtimeInstances.keyAt(i);
AblyRealtime r = _realtimeInstances.get(key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ private AblyFlutterMessage readAblyFlutterMessage(Map<String, Object> jsonMap) {
if(type!=null){
message = codecMap.get((byte)(int)type).decode((Map<String, Object>)message);
}
return new AblyFlutterMessage<>(handle, message);
return new AblyFlutterMessage<>(message, handle);
}

private ClientOptions readClientOptions(Map<String, Object> jsonMap) {
Expand Down
Loading