Skip to content

Commit 93f0483

Browse files
authored
[path_provider] started supporting background platform channels (flutter#4443)
* [path_provider] started supporting background platform channels * added docstrings
1 parent 9ac6da7 commit 93f0483

File tree

3 files changed

+158
-45
lines changed

3 files changed

+158
-45
lines changed

packages/path_provider/path_provider/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 2.0.6
2+
3+
* Added support for Background Platform Channels on Android when it is
4+
available.
5+
16
## 2.0.5
27

38
* Update minimum Flutter SDK to 2.5 and iOS deployment target to 9.0.

packages/path_provider/path_provider/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java

Lines changed: 152 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,51 +9,183 @@
99
import android.os.Build.VERSION_CODES;
1010
import android.os.Handler;
1111
import android.os.Looper;
12+
import android.util.Log;
1213
import androidx.annotation.NonNull;
1314
import com.google.common.util.concurrent.FutureCallback;
1415
import com.google.common.util.concurrent.Futures;
1516
import com.google.common.util.concurrent.SettableFuture;
1617
import com.google.common.util.concurrent.ThreadFactoryBuilder;
1718
import io.flutter.embedding.engine.plugins.FlutterPlugin;
19+
import io.flutter.plugin.common.BinaryMessenger;
1820
import io.flutter.plugin.common.MethodCall;
1921
import io.flutter.plugin.common.MethodChannel;
2022
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
2123
import io.flutter.plugin.common.MethodChannel.Result;
24+
import io.flutter.plugin.common.MethodCodec;
25+
import io.flutter.plugin.common.StandardMethodCodec;
2226
import io.flutter.util.PathUtils;
2327
import java.io.File;
28+
import java.lang.reflect.Constructor;
29+
import java.lang.reflect.Method;
2430
import java.util.ArrayList;
2531
import java.util.List;
2632
import java.util.concurrent.Callable;
2733
import java.util.concurrent.Executor;
2834
import java.util.concurrent.Executors;
2935

3036
public class PathProviderPlugin implements FlutterPlugin, MethodCallHandler {
31-
37+
static final String TAG = "PathProviderPlugin";
3238
private Context context;
3339
private MethodChannel channel;
34-
private final Executor uiThreadExecutor = new UiThreadExecutor();
35-
private final Executor executor =
36-
Executors.newSingleThreadExecutor(
37-
new ThreadFactoryBuilder()
38-
.setNameFormat("path-provider-background-%d")
39-
.setPriority(Thread.NORM_PRIORITY)
40-
.build());
40+
private PathProviderImpl impl;
41+
42+
/**
43+
* An abstraction over how to access the paths in a thread-safe manner.
44+
*
45+
* <p>We need this so on versions of Flutter that support Background Platform Channels this plugin
46+
* can take advantage of it.
47+
*
48+
* <p>This can be removed after https://github.com/flutter/engine/pull/29147 becomes available on
49+
* the stable branch.
50+
*/
51+
private interface PathProviderImpl {
52+
void getTemporaryDirectory(@NonNull Result result);
53+
54+
void getApplicationDocumentsDirectory(@NonNull Result result);
55+
56+
void getStorageDirectory(@NonNull Result result);
57+
58+
void getExternalCacheDirectories(@NonNull Result result);
59+
60+
void getExternalStorageDirectories(@NonNull String directoryName, @NonNull Result result);
61+
62+
void getApplicationSupportDirectory(@NonNull Result result);
63+
}
64+
65+
/** The implementation for getting system paths that executes from the platform */
66+
private class PathProviderPlatformThread implements PathProviderImpl {
67+
private final Executor uiThreadExecutor = new UiThreadExecutor();
68+
private final Executor executor =
69+
Executors.newSingleThreadExecutor(
70+
new ThreadFactoryBuilder()
71+
.setNameFormat("path-provider-background-%d")
72+
.setPriority(Thread.NORM_PRIORITY)
73+
.build());
74+
75+
public void getTemporaryDirectory(@NonNull Result result) {
76+
executeInBackground(() -> getPathProviderTemporaryDirectory(), result);
77+
}
78+
79+
public void getApplicationDocumentsDirectory(@NonNull Result result) {
80+
executeInBackground(() -> getPathProviderApplicationDocumentsDirectory(), result);
81+
}
82+
83+
public void getStorageDirectory(@NonNull Result result) {
84+
executeInBackground(() -> getPathProviderStorageDirectory(), result);
85+
}
86+
87+
public void getExternalCacheDirectories(@NonNull Result result) {
88+
executeInBackground(() -> getPathProviderExternalCacheDirectories(), result);
89+
}
90+
91+
public void getExternalStorageDirectories(
92+
@NonNull String directoryName, @NonNull Result result) {
93+
executeInBackground(() -> getPathProviderExternalStorageDirectories(directoryName), result);
94+
}
95+
96+
public void getApplicationSupportDirectory(@NonNull Result result) {
97+
executeInBackground(() -> PathProviderPlugin.this.getApplicationSupportDirectory(), result);
98+
}
99+
100+
private <T> void executeInBackground(Callable<T> task, Result result) {
101+
final SettableFuture<T> future = SettableFuture.create();
102+
Futures.addCallback(
103+
future,
104+
new FutureCallback<T>() {
105+
public void onSuccess(T answer) {
106+
result.success(answer);
107+
}
108+
109+
public void onFailure(Throwable t) {
110+
result.error(t.getClass().getName(), t.getMessage(), null);
111+
}
112+
},
113+
uiThreadExecutor);
114+
executor.execute(
115+
() -> {
116+
try {
117+
future.set(task.call());
118+
} catch (Throwable t) {
119+
future.setException(t);
120+
}
121+
});
122+
}
123+
}
124+
125+
/** The implementation for getting system paths that executes from a background thread. */
126+
private class PathProviderBackgroundThread implements PathProviderImpl {
127+
public void getTemporaryDirectory(@NonNull Result result) {
128+
result.success(getPathProviderTemporaryDirectory());
129+
}
130+
131+
public void getApplicationDocumentsDirectory(@NonNull Result result) {
132+
result.success(getPathProviderApplicationDocumentsDirectory());
133+
}
134+
135+
public void getStorageDirectory(@NonNull Result result) {
136+
result.success(getPathProviderStorageDirectory());
137+
}
138+
139+
public void getExternalCacheDirectories(@NonNull Result result) {
140+
result.success(getPathProviderExternalCacheDirectories());
141+
}
142+
143+
public void getExternalStorageDirectories(
144+
@NonNull String directoryName, @NonNull Result result) {
145+
result.success(getPathProviderExternalStorageDirectories(directoryName));
146+
}
147+
148+
public void getApplicationSupportDirectory(@NonNull Result result) {
149+
result.success(PathProviderPlugin.this.getApplicationSupportDirectory());
150+
}
151+
}
41152

42153
public PathProviderPlugin() {}
43154

155+
private void setup(BinaryMessenger messenger, Context context) {
156+
String channelName = "plugins.flutter.io/path_provider";
157+
// TODO(gaaclarke): Remove reflection guard when https://github.com/flutter/engine/pull/29147
158+
// becomes available on the stable branch.
159+
try {
160+
Class methodChannelClass = Class.forName("io.flutter.plugin.common.MethodChannel");
161+
Class taskQueueClass = Class.forName("io.flutter.plugin.common.BinaryMessenger$TaskQueue");
162+
Method makeBackgroundTaskQueue = messenger.getClass().getMethod("makeBackgroundTaskQueue");
163+
Object taskQueue = makeBackgroundTaskQueue.invoke(messenger);
164+
Constructor<MethodChannel> constructor =
165+
methodChannelClass.getConstructor(
166+
BinaryMessenger.class, String.class, MethodCodec.class, taskQueueClass);
167+
channel =
168+
constructor.newInstance(messenger, channelName, StandardMethodCodec.INSTANCE, taskQueue);
169+
impl = new PathProviderBackgroundThread();
170+
Log.d(TAG, "Use TaskQueues.");
171+
} catch (Exception ex) {
172+
channel = new MethodChannel(messenger, channelName);
173+
impl = new PathProviderPlatformThread();
174+
Log.d(TAG, "Don't use TaskQueues.");
175+
}
176+
this.context = context;
177+
channel.setMethodCallHandler(this);
178+
}
179+
44180
@SuppressWarnings("deprecation")
45181
public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registrar registrar) {
46182
PathProviderPlugin instance = new PathProviderPlugin();
47-
instance.channel = new MethodChannel(registrar.messenger(), "plugins.flutter.io/path_provider");
48-
instance.context = registrar.context();
49-
instance.channel.setMethodCallHandler(instance);
183+
instance.setup(registrar.messenger(), registrar.context());
50184
}
51185

52186
@Override
53187
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
54-
channel = new MethodChannel(binding.getBinaryMessenger(), "plugins.flutter.io/path_provider");
55-
context = binding.getApplicationContext();
56-
channel.setMethodCallHandler(this);
188+
setup(binding.getBinaryMessenger(), binding.getApplicationContext());
57189
}
58190

59191
@Override
@@ -62,52 +194,28 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
62194
channel = null;
63195
}
64196

65-
private <T> void executeInBackground(Callable<T> task, Result result) {
66-
final SettableFuture<T> future = SettableFuture.create();
67-
Futures.addCallback(
68-
future,
69-
new FutureCallback<T>() {
70-
public void onSuccess(T answer) {
71-
result.success(answer);
72-
}
73-
74-
public void onFailure(Throwable t) {
75-
result.error(t.getClass().getName(), t.getMessage(), null);
76-
}
77-
},
78-
uiThreadExecutor);
79-
executor.execute(
80-
() -> {
81-
try {
82-
future.set(task.call());
83-
} catch (Throwable t) {
84-
future.setException(t);
85-
}
86-
});
87-
}
88-
89197
@Override
90198
public void onMethodCall(MethodCall call, @NonNull Result result) {
91199
switch (call.method) {
92200
case "getTemporaryDirectory":
93-
executeInBackground(() -> getPathProviderTemporaryDirectory(), result);
201+
impl.getTemporaryDirectory(result);
94202
break;
95203
case "getApplicationDocumentsDirectory":
96-
executeInBackground(() -> getPathProviderApplicationDocumentsDirectory(), result);
204+
impl.getApplicationDocumentsDirectory(result);
97205
break;
98206
case "getStorageDirectory":
99-
executeInBackground(() -> getPathProviderStorageDirectory(), result);
207+
impl.getStorageDirectory(result);
100208
break;
101209
case "getExternalCacheDirectories":
102-
executeInBackground(() -> getPathProviderExternalCacheDirectories(), result);
210+
impl.getExternalCacheDirectories(result);
103211
break;
104212
case "getExternalStorageDirectories":
105213
final Integer type = call.argument("type");
106214
final String directoryName = StorageDirectoryMapper.androidType(type);
107-
executeInBackground(() -> getPathProviderExternalStorageDirectories(directoryName), result);
215+
impl.getExternalStorageDirectories(directoryName, result);
108216
break;
109217
case "getApplicationSupportDirectory":
110-
executeInBackground(() -> getApplicationSupportDirectory(), result);
218+
impl.getApplicationSupportDirectory(result);
111219
break;
112220
default:
113221
result.notImplemented();

packages/path_provider/path_provider/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: path_provider
22
description: Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories.
33
repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22
5-
version: 2.0.5
5+
version: 2.0.6
66

77
environment:
88
sdk: ">=2.14.0 <3.0.0"

0 commit comments

Comments
 (0)