当前位置:首页 > 经验 >

安卓手机如何截长屏(安卓最新手机截长屏的软件)

来源:原点资讯(www.yd166.com)时间:2022-10-26 22:50:20作者:YD166手机阅读>>

点击测试校验代码如下

bool _hitTest(RenderView? root, RenderViewportBase? result) {
if (root == || result == ) {
return false;
}
Size rootSize = size(root, Size.zero);
HitTestResult hitResult = HitTestResult;
root.hitTest(hitResult, position: Offset(rootSize.width/2, rootSize.height/2));
for (HitTestEntry entry in hitResult.path) {
if (entry.target == result) {
return true;
}
}


/**
* 处理如下 case
* RenderPointerListener 2749d135
RenderSemanticsAnnotations 1cd639bf
RenderIgnorePointer 7e33fff
RenderShrinkWrappingViewport 1167ca33
*/
RenderPointerListener? pointerListenerParent;
AbstractNode? parent = result.parent;
const int lookUpLimit = 5;
int lookupCount = 0;
while (parent != &&
lookupCount < lookUpLimit &&
parent.runtimeType.toString != '_RenderTheatre') {
lookupCount ;
if (parent is RenderPointerListener) {
pointerListenerParent = parent;
}
parent = parent.parent;
}
if (pointerListenerParent != ) {
for (HitTestEntry entry in hitResult.path) {
if (entry.target == pointerListenerParent) {
return true;
}
}
}
return false;
}

异步 Channel 通信方案

安卓手机如何截长屏,安卓最新手机截长屏的软件(13)

Flutter channel 通信方案如上图所示,其中 EventChannel 和 MethodChannel 运行在 Java 主线程,同 Dart Platform Isolate,而 Dart 层事件处理逻辑在 UI Isolate,为此并不在同一线程。可以发现,Java → Dart → Java 发生了 2 次线程切换。使用小米 K50 测试性能,从 EventChannel 发送事件 到 MethodChannel 接收返回值,记录耗时。可见,首次 canScrollVertically (由截屏广播触发)需要递归查找滚动组件,耗时为 10-30ms,之后耗时均在 5ms 以内。

08-08 16:15:56.060 11079 11079 E longscreenshot: canScrollVertically use_time=25
08-08 16:15:56.278 11079 11079 E longscreenshot: canScrollVertically use_time=2
08-08 16:16:05.342 11079 11079 E longscreenshot: canScrollVertically use_time=10
08-08 16:16:05.562 11079 11079 E longscreenshot: canScrollVertically use_time=1

为保证在异步调用的情况下,MIUI ContentPort 下发命令均能获取到最新值,这里做以下特殊处理

  1. 1. 截屏广播提前计算 canScrollVerticallly 并缓存结果

  2. 2. MIUI ContentPort 调用 canScrollVerticallly 直接返回最新缓存值,异步触发计算

  3. 3. MIUI ContentPort 调用 scrollBy 后,及时更新 canScrollVerticallly 和 getScrollY 缓存值

同步 FFI 通信方案

异步调用方案,在高端机且 App 任务队列无阻塞情况下,能正确且准确运行,但在低端机和 App 任务较重时,可能存在返回 ContentPort 数据非最新的情况,为此我们考虑使用 FFI 同步通信的方案。

安卓手机如何截长屏,安卓最新手机截长屏的软件(14)

以上同步方案,一次同步调用性能分析,基本在 5ms 以内:

安卓手机如何截长屏,安卓最新手机截长屏的软件(15)

关键实现代码如下:

@Keep
public class NativeLongScreenshotJni implements Serializable {
static {
System.loadLibrary("flutter_longscreenshot");
}

public static native void nativeCanScrollVertically(int direction,
boolean startScreenshot,
int callbackId);
public static native void nativeGetScrollY(int screenWidth, int callbackId);
public static native void nativeScrollBy(int screenWidth, int x, int y);

public static boolean canScrollVertically(final int direction,
final boolean startScreenshot) {
FlutterLongScreenshotCallbacks.AwaitCallback callback =
FlutterLongScreenshotCallbacks.newCallback;
nativeCanScrollVertically(direction, startScreenshot, callback.id);
int result = callback.waitCallback.getResult;
return result == 1;
}

public static int getScrollY(final int screenWidth) {
FlutterLongScreenshotCallbacks.AwaitCallback callback =
FlutterLongScreenshotCallbacks.newCallback;
nativeGetScrollY(screenWidth, callback.id);
// waitCallback 同步等待 C 调用 FlutterLongScreenshotCallbacks.handleDartCall
int result = callback.waitCallback.getResult;
return result;
}

public static void scrollBy(int screenWidth, int x, int y) {
nativeScrollBy(screenWidth, x, y);
}
}


@Keep
public class FlutterLongScreenshotCallbacks implements Serializable {

public static AwaitCallback newCallback {
AwaitCallback callback = new AwaitCallback;
CALLBACKS.put(callback.id, callback);
return callback;
}

// C DART_EXPORT void resultCallback(int callbackId, int result) 反射调用
public static void handleDartCall(int id, int result) {
AwaitCallback callback = CALLBACKS.get(id);
if (callback != ) {
CALLBACKS.remove(id);
callback.release(result);
}
}

private static final SparseArray<AwaitCallback> CALLBACKS = new SparseArray<>;

@Keep
public static class AwaitCallback {
public static final int RESULT_ERR = -1;
private final CountDownLatch mLatch = new CountDownLatch(1);
private int mResult = RESULT_ERR;

public int id {
return hashCode;
}

public AwaitCallback waitCallback {
try {
mLatch.await(100, TimeUnit.MILLISECONDS);
} catch (Throwable e) {
e.printStackTrace;
}
return this;
}

public void release(int result) {
mResult = result;
mLatch.countDown;
}

public int getResult {
return mResult;
}
}
}

void setDartInt(Dart_CObject& dartObj, int value) {
dartObj.type = Dart_CObject_kInt32;
dartObj.value.as_int32 = value;
}

JNIEXPORT void JNICALL
nativeCanScrollVertically(
JNIEnv *env, jclass cls,
jint direction, jboolean startScreenshot, jint callbackId) {
Dart_CObject* dart_args[4];

Dart_CObject dart_arg0;
Dart_CObject dart_arg1;
Dart_CObject dart_arg2;
Dart_CObject dart_arg3;

setDartString(dart_arg0, strdup("canScrollVertically"));
setDartInt(dart_arg1, direction);
setDartBool(dart_arg2, startScreenshot);
setDartLong(dart_arg3, callbackId);

dart_args[0] = &dart_arg0;
dart_args[1] = &dart_arg1;
dart_args[2] = &dart_arg2;
dart_args[3] = &dart_arg3;

Dart_CObject dart_object;
dart_object.type = Dart_CObject_kArray;
dart_object.value.as_array.length = 4;
dart_object.value.as_array.values = dart_args;

Dart_PostCObject_DL(send_port_, &dart_object);
}

// getScrollY 和 scrollBy 实现类似

DART_EXPORT void resultCallback(int callbackId, int result) {
JNIEnv *env = _getEnv;
if (env != ptr) {
auto cls = _findClass(env, jCallbackClassName);
jmethodID handleDartCallMethod = ptr;
if (cls != ptr) {
// 调用 java 代码 FlutterLongScreenshotCallbacks.handleDartCall(int id, int result)
handleDartCallMethod = env->GetStaticMethodID(cls,
"handleDartCall", "(II)V");
}
if (cls != ptr && handleDartCallMethod != ptr) {
env->CallStaticVoidMethod(cls, handleDartCallMethod,
callbackId, result);
} else {
print("resultCallback. find method handleDartCall is ptr");
}
}
}

class NativeLongScreenshot extends Object {
...

late final NativeLongScreenshotLibrary _nativeLibrary;
late final ReceivePort _receivePort;
late final StreamSubscription _subscription;

NativeLongScreenshot {
...
_nativeLibrary = initLibrary;
_receivePort = ReceivePort;

var nativeInited = _nativeLibrary.initializeApi(
ffi.NativeApi.initializeApiDLData
);
assert(nativeInited == 0, 'DART_API_DL_MAJOR_VERSION != 2');
_subscription = _receivePort.listen(_handleNativeMessage);
_nativeLibrary.registerSendPort(_receivePort.sendPort.nativePort);
}

void _handleNativeMessage(dynamic inArgs) {
List<dynamic> args = inArgs;
String method = args[0];

switch (method) {
case 'canScrollVertically': {
int direction = args[1];
bool startScreenshot = args[2];
int callbackId = args[3];

final bool canScroll = canScrollVertically(direction, startScreenshot);
int result = canScroll ? 1 : 0;
_nativeLibrary.resultCallback(callbackId, result);
} break;
case 'getScrollY': {
int nativeScreenWidth = args[1];
int callbackId = args[2];
int result = getScrollY(nativeScreenWidth);
_nativeLibrary.resultCallback(callbackId, result);
} break;
case 'scrollBy': {
int nativeScreenWidth = args[1];
int nativeX = args[2];
int nativeY = args[3];
scrollBy(nativeY, nativeScreenWidth);
} break;
}
}
}
总结

完成国内主要机型适配,现在线上几乎不再有用户反馈 Flutter 页面不支持长截屏。闲鱼 Android 用户已经能用系统长截屏能力,分享自己喜欢的商品、圈子内容,卖家能使用一张图片推广自己的全部商品,买家能帮助家里不会用 App 的老人找商品。面对系统功能适配,业务 App 侧也并不是完全束手无策。通过以下过程便有可能找到解决之道:

  • • 合理猜想(系统模块会调用业务视图接口)

  • • 工具辅助分析和验证(ASM 代码 hook,日志输出)

  • • 源码查找和截图(代码查找和反编译)

  • • 发散思考(ControlView 顶替 Flutter 容器,瞒天过海)

  • • 方案实现(业务无侵入,一次实现全部业务页面适配)

安卓手机如何截长屏,安卓最新手机截长屏的软件(16)

栏目热文

手机截长屏怎么一次性截完(手机怎么截长屏教程)

手机截长屏怎么一次性截完(手机怎么截长屏教程)

今天在APP上看到一篇长文,觉得蛮不错的,就想用长截屏保存下来,然后试用了无数次截屏,还把手机设置从里到外全都找了一遍,...

2022-10-26 22:41:18查看全文 >>

手机怎么截取长屏(手机怎么剪辑短视频)

手机怎么截取长屏(手机怎么剪辑短视频)

品牌型号:华为P40,iPhone12系统:EMUI10.1.0,IOS14.2软件版本:Safari浏览器手机屏幕截图...

2022-10-26 23:19:26查看全文 >>

苹果手机长截图怎么弄的(苹果手机怎么长截图呢)

苹果手机长截图怎么弄的(苹果手机怎么长截图呢)

来源:环球网截长图是智能手机上一个非常实用的功能,但是,这项功能仅在大多数安卓手机中才提供,难道使用iPhone的用户似...

2022-10-26 23:03:01查看全文 >>

怎样截滚动长屏(手机超长截屏)

怎样截滚动长屏(手机超长截屏)

作为窗口化操作工具,电脑的截图方式往往只局限于矩形截图、自定义形状截图、窗口截图及全屏截图,那么如何才能实现长截图呢?本...

2022-10-26 23:08:15查看全文 >>

手机怎么截长屏最简单的方法(手机怎样截滚动长屏)

手机怎么截长屏最简单的方法(手机怎样截滚动长屏)

平常我们在玩手机的时候,看到比较好看的文章或者是好看的图片,需要用到截屏,智能手机截屏的功能,对于年轻人来说很容易,但是...

2022-10-26 22:39:44查看全文 >>

小米手机怎么截长屏图(小米手机怎么截不了长屏了)

小米手机怎么截长屏图(小米手机怎么截不了长屏了)

根据小米今年Q1财报显示,目前小米MIUI手机系统月活跃率已超过5亿,在这其中无疑有数不尽的“米粉”支持,同时也恰巧说明...

2022-10-26 22:59:07查看全文 >>

手机长图截屏软件(手机手动截屏软件推荐)

手机长图截屏软件(手机手动截屏软件推荐)

看视频的时候,截了很多图想要拼接在一起,或者是多张聊天记录截图想要拼接长图发送给别人。比如这样的电影台词截图,是如何做到...

2022-10-26 23:03:41查看全文 >>

vivo手机截长屏怎么操作(vivo最好的三款手机)

vivo手机截长屏怎么操作(vivo最好的三款手机)

X27 Pro 是 X27 的升级款,目前 X 系列最高端的手机。仅有一个存储版本 8 256,售价¥ 3998。外观上...

2022-10-26 23:14:57查看全文 >>

小米手机怎样截长屏的方法(小米手机怎样截长图设置)

小米手机怎样截长屏的方法(小米手机怎样截长图设置)

我们在日常生活中经常会使用到“截屏”这项功能,特别是疫情期间,我们需要做核酸检查,出示健康码,行程卡等信息,为了快速完成...

2022-10-26 22:38:42查看全文 >>

安卓手机怎样截长屏(安卓手机怎样滚动截长图)

安卓手机怎样截长屏(安卓手机怎样滚动截长图)

Android 端 Microsoft Edge 正在获得一项新功能,允许用户进行长屏幕截图。该功能目前已经在 Cana...

2022-10-26 23:17:56查看全文 >>

文档排行