-
Notifications
You must be signed in to change notification settings - Fork 9.7k
[path_provider] started supporting background platform channels #4443
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,51 +9,183 @@ | |
| import android.os.Build.VERSION_CODES; | ||
| import android.os.Handler; | ||
| import android.os.Looper; | ||
| import android.util.Log; | ||
| import androidx.annotation.NonNull; | ||
| import com.google.common.util.concurrent.FutureCallback; | ||
| import com.google.common.util.concurrent.Futures; | ||
| import com.google.common.util.concurrent.SettableFuture; | ||
| import com.google.common.util.concurrent.ThreadFactoryBuilder; | ||
| import io.flutter.embedding.engine.plugins.FlutterPlugin; | ||
| import io.flutter.plugin.common.BinaryMessenger; | ||
| import io.flutter.plugin.common.MethodCall; | ||
| import io.flutter.plugin.common.MethodChannel; | ||
| import io.flutter.plugin.common.MethodChannel.MethodCallHandler; | ||
| import io.flutter.plugin.common.MethodChannel.Result; | ||
| import io.flutter.plugin.common.MethodCodec; | ||
| import io.flutter.plugin.common.StandardMethodCodec; | ||
| import io.flutter.util.PathUtils; | ||
| import java.io.File; | ||
| import java.lang.reflect.Constructor; | ||
| import java.lang.reflect.Method; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.concurrent.Callable; | ||
| import java.util.concurrent.Executor; | ||
| import java.util.concurrent.Executors; | ||
|
|
||
| public class PathProviderPlugin implements FlutterPlugin, MethodCallHandler { | ||
|
|
||
| static final String TAG = "PathProviderPlugin"; | ||
| private Context context; | ||
| private MethodChannel channel; | ||
| private final Executor uiThreadExecutor = new UiThreadExecutor(); | ||
| private final Executor executor = | ||
| Executors.newSingleThreadExecutor( | ||
| new ThreadFactoryBuilder() | ||
| .setNameFormat("path-provider-background-%d") | ||
| .setPriority(Thread.NORM_PRIORITY) | ||
| .build()); | ||
| private PathProviderImpl impl; | ||
|
|
||
| /** | ||
| * An abstraction over how to access the paths in a thread-safe manner. | ||
| * | ||
| * <p>We need this so on versions of Flutter that support Background Platform Channels this plugin | ||
| * can take advantage of it. | ||
| * | ||
| * <p>This can be removed after https://github.com/flutter/engine/pull/29147 becomes available on | ||
| * the stable branch. | ||
| */ | ||
| private interface PathProviderImpl { | ||
| void getTemporaryDirectory(@NonNull Result result); | ||
|
|
||
| void getApplicationDocumentsDirectory(@NonNull Result result); | ||
|
|
||
| void getStorageDirectory(@NonNull Result result); | ||
|
|
||
| void getExternalCacheDirectories(@NonNull Result result); | ||
|
|
||
| void getExternalStorageDirectories(@NonNull String directoryName, @NonNull Result result); | ||
|
|
||
| void getApplicationSupportDirectory(@NonNull Result result); | ||
| } | ||
|
|
||
| /** The implementation for getting system paths that executes from the platform */ | ||
| private class PathProviderPlatformThread implements PathProviderImpl { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is the shim, could it have a name that is easier to map to the reason why this class exists?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added docstring to clarify its purpose. I couldn't pack all that information into a name. This is something that exists temporarily so between the documentation and the good enough name, hopefully that's satisfactory. |
||
| private final Executor uiThreadExecutor = new UiThreadExecutor(); | ||
| private final Executor executor = | ||
| Executors.newSingleThreadExecutor( | ||
| new ThreadFactoryBuilder() | ||
| .setNameFormat("path-provider-background-%d") | ||
| .setPriority(Thread.NORM_PRIORITY) | ||
| .build()); | ||
|
|
||
| public void getTemporaryDirectory(@NonNull Result result) { | ||
| executeInBackground(() -> getPathProviderTemporaryDirectory(), result); | ||
| } | ||
|
|
||
| public void getApplicationDocumentsDirectory(@NonNull Result result) { | ||
| executeInBackground(() -> getPathProviderApplicationDocumentsDirectory(), result); | ||
| } | ||
|
|
||
| public void getStorageDirectory(@NonNull Result result) { | ||
| executeInBackground(() -> getPathProviderStorageDirectory(), result); | ||
| } | ||
|
|
||
| public void getExternalCacheDirectories(@NonNull Result result) { | ||
| executeInBackground(() -> getPathProviderExternalCacheDirectories(), result); | ||
| } | ||
|
|
||
| public void getExternalStorageDirectories( | ||
| @NonNull String directoryName, @NonNull Result result) { | ||
| executeInBackground(() -> getPathProviderExternalStorageDirectories(directoryName), result); | ||
| } | ||
|
|
||
| public void getApplicationSupportDirectory(@NonNull Result result) { | ||
| executeInBackground(() -> PathProviderPlugin.this.getApplicationSupportDirectory(), result); | ||
| } | ||
|
|
||
| private <T> void executeInBackground(Callable<T> task, Result result) { | ||
| final SettableFuture<T> future = SettableFuture.create(); | ||
| Futures.addCallback( | ||
| future, | ||
| new FutureCallback<T>() { | ||
| public void onSuccess(T answer) { | ||
| result.success(answer); | ||
| } | ||
|
|
||
| public void onFailure(Throwable t) { | ||
| result.error(t.getClass().getName(), t.getMessage(), null); | ||
| } | ||
| }, | ||
| uiThreadExecutor); | ||
| executor.execute( | ||
| () -> { | ||
| try { | ||
| future.set(task.call()); | ||
| } catch (Throwable t) { | ||
| future.setException(t); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| /** The implementation for getting system paths that executes from a background thread. */ | ||
| private class PathProviderBackgroundThread implements PathProviderImpl { | ||
| public void getTemporaryDirectory(@NonNull Result result) { | ||
| result.success(getPathProviderTemporaryDirectory()); | ||
| } | ||
|
|
||
| public void getApplicationDocumentsDirectory(@NonNull Result result) { | ||
| result.success(getPathProviderApplicationDocumentsDirectory()); | ||
| } | ||
|
|
||
| public void getStorageDirectory(@NonNull Result result) { | ||
| result.success(getPathProviderStorageDirectory()); | ||
| } | ||
|
|
||
| public void getExternalCacheDirectories(@NonNull Result result) { | ||
| result.success(getPathProviderExternalCacheDirectories()); | ||
| } | ||
|
|
||
| public void getExternalStorageDirectories( | ||
| @NonNull String directoryName, @NonNull Result result) { | ||
| result.success(getPathProviderExternalStorageDirectories(directoryName)); | ||
| } | ||
|
|
||
| public void getApplicationSupportDirectory(@NonNull Result result) { | ||
| result.success(PathProviderPlugin.this.getApplicationSupportDirectory()); | ||
| } | ||
| } | ||
|
|
||
| public PathProviderPlugin() {} | ||
|
|
||
| private void setup(BinaryMessenger messenger, Context context) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remember when naming methods that "setup" is a noun, and "set up" is a verb, so a method (which should be named with a verb) should be called "setUp". I'm aware that Pigeon has this issue, but we shouldn't spread it beyond there. |
||
| String channelName = "plugins.flutter.io/path_provider"; | ||
| // TODO(gaaclarke): Remove reflection guard when https://github.com/flutter/engine/pull/29147 | ||
| // becomes available on the stable branch. | ||
| try { | ||
| Class methodChannelClass = Class.forName("io.flutter.plugin.common.MethodChannel"); | ||
| Class taskQueueClass = Class.forName("io.flutter.plugin.common.BinaryMessenger$TaskQueue"); | ||
| Method makeBackgroundTaskQueue = messenger.getClass().getMethod("makeBackgroundTaskQueue"); | ||
| Object taskQueue = makeBackgroundTaskQueue.invoke(messenger); | ||
| Constructor<MethodChannel> constructor = | ||
| methodChannelClass.getConstructor( | ||
| BinaryMessenger.class, String.class, MethodCodec.class, taskQueueClass); | ||
| channel = | ||
| constructor.newInstance(messenger, channelName, StandardMethodCodec.INSTANCE, taskQueue); | ||
| impl = new PathProviderBackgroundThread(); | ||
| Log.d(TAG, "Use TaskQueues."); | ||
| } catch (Exception ex) { | ||
| channel = new MethodChannel(messenger, channelName); | ||
| impl = new PathProviderPlatformThread(); | ||
| Log.d(TAG, "Don't use TaskQueues."); | ||
| } | ||
| this.context = context; | ||
| channel.setMethodCallHandler(this); | ||
| } | ||
|
|
||
| @SuppressWarnings("deprecation") | ||
| public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registrar registrar) { | ||
| PathProviderPlugin instance = new PathProviderPlugin(); | ||
| instance.channel = new MethodChannel(registrar.messenger(), "plugins.flutter.io/path_provider"); | ||
| instance.context = registrar.context(); | ||
| instance.channel.setMethodCallHandler(instance); | ||
| instance.setup(registrar.messenger(), registrar.context()); | ||
| } | ||
|
|
||
| @Override | ||
| public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { | ||
| channel = new MethodChannel(binding.getBinaryMessenger(), "plugins.flutter.io/path_provider"); | ||
| context = binding.getApplicationContext(); | ||
| channel.setMethodCallHandler(this); | ||
| setup(binding.getBinaryMessenger(), binding.getApplicationContext()); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -62,52 +194,28 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { | |
| channel = null; | ||
| } | ||
|
|
||
| private <T> void executeInBackground(Callable<T> task, Result result) { | ||
| final SettableFuture<T> future = SettableFuture.create(); | ||
| Futures.addCallback( | ||
| future, | ||
| new FutureCallback<T>() { | ||
| public void onSuccess(T answer) { | ||
| result.success(answer); | ||
| } | ||
|
|
||
| public void onFailure(Throwable t) { | ||
| result.error(t.getClass().getName(), t.getMessage(), null); | ||
| } | ||
| }, | ||
| uiThreadExecutor); | ||
| executor.execute( | ||
| () -> { | ||
| try { | ||
| future.set(task.call()); | ||
| } catch (Throwable t) { | ||
| future.setException(t); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| @Override | ||
| public void onMethodCall(MethodCall call, @NonNull Result result) { | ||
| switch (call.method) { | ||
| case "getTemporaryDirectory": | ||
| executeInBackground(() -> getPathProviderTemporaryDirectory(), result); | ||
| impl.getTemporaryDirectory(result); | ||
| break; | ||
| case "getApplicationDocumentsDirectory": | ||
| executeInBackground(() -> getPathProviderApplicationDocumentsDirectory(), result); | ||
| impl.getApplicationDocumentsDirectory(result); | ||
| break; | ||
| case "getStorageDirectory": | ||
| executeInBackground(() -> getPathProviderStorageDirectory(), result); | ||
| impl.getStorageDirectory(result); | ||
| break; | ||
| case "getExternalCacheDirectories": | ||
| executeInBackground(() -> getPathProviderExternalCacheDirectories(), result); | ||
| impl.getExternalCacheDirectories(result); | ||
| break; | ||
| case "getExternalStorageDirectories": | ||
| final Integer type = call.argument("type"); | ||
| final String directoryName = StorageDirectoryMapper.androidType(type); | ||
| executeInBackground(() -> getPathProviderExternalStorageDirectories(directoryName), result); | ||
| impl.getExternalStorageDirectories(directoryName, result); | ||
| break; | ||
| case "getApplicationSupportDirectory": | ||
| executeInBackground(() -> getApplicationSupportDirectory(), result); | ||
| impl.getApplicationSupportDirectory(result); | ||
| break; | ||
| default: | ||
| result.notImplemented(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,7 @@ name: path_provider | |
| description: Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories. | ||
| repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider | ||
| issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 | ||
| version: 2.0.5 | ||
| version: 2.0.6 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will need a higher version constraint on Flutter SDK. I'm not sure if that means it also needs a minor or major version bump.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahh I see, you're using reflection for this.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should really have been a minor version update anyway, since this is a change to "add functionality in a backwards compatible manner" (the semver spec description for when to make a minor change), rather than a bug fix. |
||
|
|
||
| environment: | ||
| sdk: ">=2.14.0 <3.0.0" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
*Implis usually used for the implementation of an interface.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added docstring to clarify its purpose.