当该应用被杀死时,在Android中运行背景任务的问题

发布于 2025-02-01 19:55:00 字数 11961 浏览 1 评论 0 原文

这与问题有关当我几天前发布的应用程序被杀死时,如何在Android中运行背景任务。

阅读了一些博客之后(例如)和代码(电话)我已经完成了一些编码。我的要求是,即使我的应用程序不运行,也要在接收呼叫时向用户展示某种通知(就像Android中的 truecaller 应用程序一样)。我在下面发布代码。

callback_dispatcher.dart

const String HANDLE_INCOMING_CALL_BG = "handleIncomingCallBg";
const String INCOMING_CALL_BG_INITIALIZED = "incomingCallBgInitialized";
const String INCOMING_CALL_BG_INIT_SERVICE = "incomingCallBgInitService";
const String backgroundChannelId = "background_channel";
const String foregroundChannelId = "foreground_channel";

void callback_dispatcher() {
    const MethodChannel _backgroundChannel = MethodChannel(backgroundChannelId);
    WidgetsFlutterBinding.ensureInitialized();
    _backgroundChannel.setMethodCallHandler((MethodCall call) =>
    methodCallHandler(call));
    _backgroundChannel.invokeMethod<void>(INCOMING_CALL_BG_INITIALIZED);
}

Future<dynamic> methodCallHandler(MethodCall call) async {
    if(call.method == HANDLE_INCOMING_CALL_BG) {
        final CallbackHandle _handle =
        CallbackHandle.fromRawHandle(call.arguments['handle']);
        final Function _handlerFunc = PluginUtilities.getCallbackFromHandle(_handle)!;
        try {
            await _handlerFunc(call.arguments['message']);
        } catch(e) {
            print('Unable to handle incoming call in background');
            print(e);
        }
    }
    return Future<void>.value();
}

void handleBgMsg(String msg) {
    debugPrint("[handleBgMsg] incoming call number : " + msg);
    // notification to user
}

background_incoming_call.dart

typedef BgMessageHandler(String msg);

class BackgroundIncomingCall {
    MethodChannel? foregroundChannel;

    Future<void> initialize(BgMessageHandler onBgMessage) async {
        foregroundChannel = const MethodChannel(foregroundChannelId);
        final CallbackHandle? bgSetupHandle = 
        PluginUtilities.getCallbackHandle(callback_dispatcher);
        final CallbackHandle? bgMsgHandle = 
        PluginUtilities.getCallbackHandle(onBgMessage);
        await foregroundChannel?.invokeMethod<bool>(
            INCOMING_CALL_BG_INIT_SERVICE,
            <String, dynamic> {
            'bgSetupHandle': bgSetupHandle?.toRawHandle(),
            'bgMsgHandle': bgMsgHandle?.toRawHandle()
            }
        );
    }
}

in main.dart.dart 我正在初始化 background incommingcall()。 。

在Android部分上,我创建了一个 broadcastreceiver 如下(代码主要来自上述电话 package)。

public class CallEventReceiver extends BroadcastReceiver implements
    MethodChannel.MethodCallHandler {
private static final String TAG = "[TEST]";
private static final int NUMBER_LEN = 10;

public static String INCOMING_CALL_BG_INITIALIZED = "incomingCallBgInitialized";
public static String INCOMING_CALL_BG_INIT_SERVICE = "incomingCallBgInitService";
public static String SETUP_HANDLE = "bgSetupHandle";
public static String MESSAGE_HANDLE = "bgMsgHandle";
public static String BG_INCOMING_CALL_CHANNEL_ID = "background_channel";
public static String FG_INCOMING_CALL_CHANNEL_ID = "foreground_channel";
public static String HANDLE_INCOMING_CALL_BG = "handleIncomingCallBg";
public static String SHARED_PREF_NAME = "incomingCallSharedPrefBg";
public static String SHARED_PREF_BG_INCOMING_CALL_HANDLE = "incomingCallSharedPrefBgHandle";
public static String SHARED_PREFS_BACKGROUND_SETUP_HANDLE = "backgroundSetupHandle";

private Activity activity = null;

/* for background isolate*/
private Context appContext = null;
private AtomicBoolean isIsolateRunning = new AtomicBoolean(false);
private List<String> bgIncomingCallQueue = Collections.synchronizedList(new ArrayList<String>());
private MethodChannel bgIncomingCallChannel = null;;
private MethodChannel fgIncomingCallChannel = null;;
private FlutterLoader flutterLoader = null;
private FlutterEngine bgFlutterEngine = null;
private Long bgIncomingCallHandle = null;
/* END */

public CallEventReceiver() {}

public CallEventReceiver(Activity activity) {
    super();
    this.activity = activity;
}

@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
    if(call.method.equals(INCOMING_CALL_BG_INITIALIZED)) {
        if(appContext != null) {
            onChannelInitialized(appContext);
        } else {
            throw new RuntimeException("Application context is not set");
        }
    } else if(call.method.equals(INCOMING_CALL_BG_INIT_SERVICE)) {
        if(appContext != null) {
            if(call.hasArgument(SETUP_HANDLE) && call.hasArgument(MESSAGE_HANDLE)) {
                Long setupHandle = call.<Long>argument(SETUP_HANDLE);
                Long msgHandle = call.<Long>argument(MESSAGE_HANDLE);
                if (setupHandle == null || msgHandle == null) {
                    result.error("ILLEGAL_ARGS", "Setup handle or message handle is missing", null);
                    return;
                }
                setBgSetupHandle(appContext, setupHandle);
                setBgMessageHandle(appContext, msgHandle);
            }
        } else {
            throw new RuntimeException("Application context is not set");
        }
    } else {
        result.notImplemented();
    }
}

@Override
public void onReceive(Context context, Intent intent) {
    this.appContext = context;
    try {
        Log.i(TAG, "[CallEventHandler] Receiver start");
        String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
        String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
        if(state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
            if(incomingNumber != null) {
                Log.i(TAG, "[CallEventHandler] Incoming number : " + incomingNumber);
                if(incomingNumber.length() > NUMBER_LEN) {
                    incomingNumber = incomingNumber.substring(incomingNumber.length() - NUMBER_LEN, incomingNumber.length());
                    Log.i(TAG, "[CallEventHandler] Incoming number after : " + incomingNumber);
                    processIncomingCall(context, incomingNumber);
                }
            }
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

public void setBgMessageHandle(Context ctx, Long handle) {
    bgIncomingCallHandle = handle;
    SharedPreferences pref = ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
    pref.edit().putLong(SHARED_PREF_BG_INCOMING_CALL_HANDLE, handle).apply();
}

public void setBgSetupHandle(Context ctx, Long setupBgHandle) {
    SharedPreferences pref = ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
    pref.edit().putLong(SHARED_PREFS_BACKGROUND_SETUP_HANDLE, setupBgHandle).apply();
}

private void processIncomingCall(Context ctx, String incomingNumber) {
    boolean isForeground = isAppForeground(ctx);
    Log.i(TAG, "[processIncomingCall] is app in foreground : " + isForeground);
    if (isForeground) {
        processIncomingCallInForeground(incomingNumber);
    } else {
        processIncomingCallInBackground(ctx, incomingNumber);
    }
}

private void processIncomingCallInBackground(Context ctx, String incomingNumber) {
    if(!isIsolateRunning.get()) {
        init(ctx);
        SharedPreferences sharedPref = ctx.getSharedPreferences(SHARED_PREF_NAME,
                Context.MODE_PRIVATE);
        Long bgCallbackHandle = sharedPref.getLong(SHARED_PREFS_BACKGROUND_SETUP_HANDLE, 0);
        startBackgroundIsolate(ctx, bgCallbackHandle);
        bgIncomingCallQueue.add(incomingNumber);
    } else {
        executeDartCallbackInBgIsolate(ctx, incomingNumber);
    }
}

private void processIncomingCallInForeground(String incomingNumber) {
    Log.i(TAG, "[processIncomingCallInFg] incoming number : " + incomingNumber);
    if(activity != null) {
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if(eventSink != null) {
                    eventSink.success(incomingNumber);
                }
            }
        });
    }
}

private void onChannelInitialized(Context ctx) {
    isIsolateRunning.set(true);
    synchronized(bgIncomingCallQueue) {
        Iterator<String> it = bgIncomingCallQueue.iterator();
        while(it.hasNext()) {
            executeDartCallbackInBgIsolate(ctx, it.next());
        }
        bgIncomingCallQueue.clear();
    }
}

private void init(Context ctx) {
    FlutterInjector flutterInjector = FlutterInjector.instance();
    flutterLoader = flutterInjector.flutterLoader();
    flutterLoader.startInitialization(ctx);
}

private void executeDartCallbackInBgIsolate(Context ctx, String incomingNumber) {
    if(bgIncomingCallChannel == null) {
        throw new RuntimeException("background channel is not initialized");
    }

    if(bgIncomingCallHandle == null) {
        bgIncomingCallHandle = getBgMessageHandle(ctx);
    }
    HashMap<String, Object> args = new HashMap<String, Object>();
    args.put("handle", bgIncomingCallHandle);
    args.put("message", incomingNumber);
    bgIncomingCallChannel.invokeMethod(HANDLE_INCOMING_CALL_BG, args);
}

private Long getBgMessageHandle(Context ctx) {
    return ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
            .getLong(SHARED_PREF_BG_INCOMING_CALL_HANDLE, 0);
}

private void startBackgroundIsolate(Context ctx, Long callbackHandle) {
    String appBundlePath = flutterLoader.findAppBundlePath();
    FlutterCallbackInformation cbInfo = FlutterCallbackInformation.
            lookupCallbackInformation(callbackHandle);

    DartExecutor.DartCallback dartEntryPoint =
            new DartExecutor.DartCallback(ctx.getAssets(), appBundlePath, cbInfo);
    bgFlutterEngine = new FlutterEngine(ctx, flutterLoader, new FlutterJNI());
    bgFlutterEngine.getDartExecutor().executeDartCallback(dartEntryPoint);

    bgIncomingCallChannel = new MethodChannel(bgFlutterEngine.getDartExecutor(),
            BG_INCOMING_CALL_CHANNEL_ID);
    bgIncomingCallChannel.setMethodCallHandler(this);
}

private boolean isAppForeground(Context ctx) {
    KeyguardManager keyGuardManager = (KeyguardManager) ctx.
            getSystemService(Context.KEYGUARD_SERVICE);
    if(keyGuardManager.isKeyguardLocked()) {
        return false;
    }
    int pid = Process.myPid();
    ActivityManager activityManager = (ActivityManager) ctx.
            getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningAppProcessInfo> list =
            activityManager.getRunningAppProcesses();
    for(ActivityManager.RunningAppProcessInfo proc : list) {
        if(pid == proc.pid) {
            return proc.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
        }
    }
    return false;
}

}

在我的 androidManifest.xml 中,如果我运行我的应用程序并将其最小化,请

<receiver android:name="org.cdot.diu.handler.CallEventReceiver" android:exported="true">
       <intent-filter>
           <action android:name="android.intent.action.PHONE_STATE"/>
       </intent-filter>
</receiver>

立即注册广播接收器, hannebgmsg 已成功地调用。由于当我杀死应用程序时,在终端中没有显示打印或调试消息,因此我使用电话软件包向我发送SMS,该邮件将应用程序最小化时成功发送。但是,如果我杀死了该应用程序,则不会发送SMS。我不确定我在这里做错了什么。我的猜测是,当应用程序被杀死时,广播接收器被杀死。但是,在电话软件包中,没有什么可以表明在杀死应用程序时要保持接收器的特殊护理。

This is in relation to the question How to run a background task in android when the app is killed which I posted a few days ago.

After reading some blogs (e.g this) and code (of telephony) I have done some coding. My requirement is to show the user some sort of notification when a call is received, even when my app is not running (much like the Truecaller app in android). I am posting the code below.

callback_dispatcher.dart

const String HANDLE_INCOMING_CALL_BG = "handleIncomingCallBg";
const String INCOMING_CALL_BG_INITIALIZED = "incomingCallBgInitialized";
const String INCOMING_CALL_BG_INIT_SERVICE = "incomingCallBgInitService";
const String backgroundChannelId = "background_channel";
const String foregroundChannelId = "foreground_channel";

void callback_dispatcher() {
    const MethodChannel _backgroundChannel = MethodChannel(backgroundChannelId);
    WidgetsFlutterBinding.ensureInitialized();
    _backgroundChannel.setMethodCallHandler((MethodCall call) =>
    methodCallHandler(call));
    _backgroundChannel.invokeMethod<void>(INCOMING_CALL_BG_INITIALIZED);
}

Future<dynamic> methodCallHandler(MethodCall call) async {
    if(call.method == HANDLE_INCOMING_CALL_BG) {
        final CallbackHandle _handle =
        CallbackHandle.fromRawHandle(call.arguments['handle']);
        final Function _handlerFunc = PluginUtilities.getCallbackFromHandle(_handle)!;
        try {
            await _handlerFunc(call.arguments['message']);
        } catch(e) {
            print('Unable to handle incoming call in background');
            print(e);
        }
    }
    return Future<void>.value();
}

void handleBgMsg(String msg) {
    debugPrint("[handleBgMsg] incoming call number : " + msg);
    // notification to user
}

background_incoming_call.dart

typedef BgMessageHandler(String msg);

class BackgroundIncomingCall {
    MethodChannel? foregroundChannel;

    Future<void> initialize(BgMessageHandler onBgMessage) async {
        foregroundChannel = const MethodChannel(foregroundChannelId);
        final CallbackHandle? bgSetupHandle = 
        PluginUtilities.getCallbackHandle(callback_dispatcher);
        final CallbackHandle? bgMsgHandle = 
        PluginUtilities.getCallbackHandle(onBgMessage);
        await foregroundChannel?.invokeMethod<bool>(
            INCOMING_CALL_BG_INIT_SERVICE,
            <String, dynamic> {
            'bgSetupHandle': bgSetupHandle?.toRawHandle(),
            'bgMsgHandle': bgMsgHandle?.toRawHandle()
            }
        );
    }
}

In main.dart I am initializing BackgroundIncomingCall().initialize(handleBgMsg);.

On the Android part I have created a BroadcastReceiver as follows (the code mostly from the aforementioned Telephony package).

public class CallEventReceiver extends BroadcastReceiver implements
    MethodChannel.MethodCallHandler {
private static final String TAG = "[TEST]";
private static final int NUMBER_LEN = 10;

public static String INCOMING_CALL_BG_INITIALIZED = "incomingCallBgInitialized";
public static String INCOMING_CALL_BG_INIT_SERVICE = "incomingCallBgInitService";
public static String SETUP_HANDLE = "bgSetupHandle";
public static String MESSAGE_HANDLE = "bgMsgHandle";
public static String BG_INCOMING_CALL_CHANNEL_ID = "background_channel";
public static String FG_INCOMING_CALL_CHANNEL_ID = "foreground_channel";
public static String HANDLE_INCOMING_CALL_BG = "handleIncomingCallBg";
public static String SHARED_PREF_NAME = "incomingCallSharedPrefBg";
public static String SHARED_PREF_BG_INCOMING_CALL_HANDLE = "incomingCallSharedPrefBgHandle";
public static String SHARED_PREFS_BACKGROUND_SETUP_HANDLE = "backgroundSetupHandle";

private Activity activity = null;

/* for background isolate*/
private Context appContext = null;
private AtomicBoolean isIsolateRunning = new AtomicBoolean(false);
private List<String> bgIncomingCallQueue = Collections.synchronizedList(new ArrayList<String>());
private MethodChannel bgIncomingCallChannel = null;;
private MethodChannel fgIncomingCallChannel = null;;
private FlutterLoader flutterLoader = null;
private FlutterEngine bgFlutterEngine = null;
private Long bgIncomingCallHandle = null;
/* END */

public CallEventReceiver() {}

public CallEventReceiver(Activity activity) {
    super();
    this.activity = activity;
}

@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
    if(call.method.equals(INCOMING_CALL_BG_INITIALIZED)) {
        if(appContext != null) {
            onChannelInitialized(appContext);
        } else {
            throw new RuntimeException("Application context is not set");
        }
    } else if(call.method.equals(INCOMING_CALL_BG_INIT_SERVICE)) {
        if(appContext != null) {
            if(call.hasArgument(SETUP_HANDLE) && call.hasArgument(MESSAGE_HANDLE)) {
                Long setupHandle = call.<Long>argument(SETUP_HANDLE);
                Long msgHandle = call.<Long>argument(MESSAGE_HANDLE);
                if (setupHandle == null || msgHandle == null) {
                    result.error("ILLEGAL_ARGS", "Setup handle or message handle is missing", null);
                    return;
                }
                setBgSetupHandle(appContext, setupHandle);
                setBgMessageHandle(appContext, msgHandle);
            }
        } else {
            throw new RuntimeException("Application context is not set");
        }
    } else {
        result.notImplemented();
    }
}

@Override
public void onReceive(Context context, Intent intent) {
    this.appContext = context;
    try {
        Log.i(TAG, "[CallEventHandler] Receiver start");
        String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
        String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
        if(state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
            if(incomingNumber != null) {
                Log.i(TAG, "[CallEventHandler] Incoming number : " + incomingNumber);
                if(incomingNumber.length() > NUMBER_LEN) {
                    incomingNumber = incomingNumber.substring(incomingNumber.length() - NUMBER_LEN, incomingNumber.length());
                    Log.i(TAG, "[CallEventHandler] Incoming number after : " + incomingNumber);
                    processIncomingCall(context, incomingNumber);
                }
            }
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

public void setBgMessageHandle(Context ctx, Long handle) {
    bgIncomingCallHandle = handle;
    SharedPreferences pref = ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
    pref.edit().putLong(SHARED_PREF_BG_INCOMING_CALL_HANDLE, handle).apply();
}

public void setBgSetupHandle(Context ctx, Long setupBgHandle) {
    SharedPreferences pref = ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
    pref.edit().putLong(SHARED_PREFS_BACKGROUND_SETUP_HANDLE, setupBgHandle).apply();
}

private void processIncomingCall(Context ctx, String incomingNumber) {
    boolean isForeground = isAppForeground(ctx);
    Log.i(TAG, "[processIncomingCall] is app in foreground : " + isForeground);
    if (isForeground) {
        processIncomingCallInForeground(incomingNumber);
    } else {
        processIncomingCallInBackground(ctx, incomingNumber);
    }
}

private void processIncomingCallInBackground(Context ctx, String incomingNumber) {
    if(!isIsolateRunning.get()) {
        init(ctx);
        SharedPreferences sharedPref = ctx.getSharedPreferences(SHARED_PREF_NAME,
                Context.MODE_PRIVATE);
        Long bgCallbackHandle = sharedPref.getLong(SHARED_PREFS_BACKGROUND_SETUP_HANDLE, 0);
        startBackgroundIsolate(ctx, bgCallbackHandle);
        bgIncomingCallQueue.add(incomingNumber);
    } else {
        executeDartCallbackInBgIsolate(ctx, incomingNumber);
    }
}

private void processIncomingCallInForeground(String incomingNumber) {
    Log.i(TAG, "[processIncomingCallInFg] incoming number : " + incomingNumber);
    if(activity != null) {
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if(eventSink != null) {
                    eventSink.success(incomingNumber);
                }
            }
        });
    }
}

private void onChannelInitialized(Context ctx) {
    isIsolateRunning.set(true);
    synchronized(bgIncomingCallQueue) {
        Iterator<String> it = bgIncomingCallQueue.iterator();
        while(it.hasNext()) {
            executeDartCallbackInBgIsolate(ctx, it.next());
        }
        bgIncomingCallQueue.clear();
    }
}

private void init(Context ctx) {
    FlutterInjector flutterInjector = FlutterInjector.instance();
    flutterLoader = flutterInjector.flutterLoader();
    flutterLoader.startInitialization(ctx);
}

private void executeDartCallbackInBgIsolate(Context ctx, String incomingNumber) {
    if(bgIncomingCallChannel == null) {
        throw new RuntimeException("background channel is not initialized");
    }

    if(bgIncomingCallHandle == null) {
        bgIncomingCallHandle = getBgMessageHandle(ctx);
    }
    HashMap<String, Object> args = new HashMap<String, Object>();
    args.put("handle", bgIncomingCallHandle);
    args.put("message", incomingNumber);
    bgIncomingCallChannel.invokeMethod(HANDLE_INCOMING_CALL_BG, args);
}

private Long getBgMessageHandle(Context ctx) {
    return ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
            .getLong(SHARED_PREF_BG_INCOMING_CALL_HANDLE, 0);
}

private void startBackgroundIsolate(Context ctx, Long callbackHandle) {
    String appBundlePath = flutterLoader.findAppBundlePath();
    FlutterCallbackInformation cbInfo = FlutterCallbackInformation.
            lookupCallbackInformation(callbackHandle);

    DartExecutor.DartCallback dartEntryPoint =
            new DartExecutor.DartCallback(ctx.getAssets(), appBundlePath, cbInfo);
    bgFlutterEngine = new FlutterEngine(ctx, flutterLoader, new FlutterJNI());
    bgFlutterEngine.getDartExecutor().executeDartCallback(dartEntryPoint);

    bgIncomingCallChannel = new MethodChannel(bgFlutterEngine.getDartExecutor(),
            BG_INCOMING_CALL_CHANNEL_ID);
    bgIncomingCallChannel.setMethodCallHandler(this);
}

private boolean isAppForeground(Context ctx) {
    KeyguardManager keyGuardManager = (KeyguardManager) ctx.
            getSystemService(Context.KEYGUARD_SERVICE);
    if(keyGuardManager.isKeyguardLocked()) {
        return false;
    }
    int pid = Process.myPid();
    ActivityManager activityManager = (ActivityManager) ctx.
            getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningAppProcessInfo> list =
            activityManager.getRunningAppProcesses();
    for(ActivityManager.RunningAppProcessInfo proc : list) {
        if(pid == proc.pid) {
            return proc.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
        }
    }
    return false;
}

}

In my AndroidManifest.xml I am registering the broadcast receiver

<receiver android:name="org.cdot.diu.handler.CallEventReceiver" android:exported="true">
       <intent-filter>
           <action android:name="android.intent.action.PHONE_STATE"/>
       </intent-filter>
</receiver>

Now If I run my app and minimize it, the handleBgMsg is called successfully. Since no print or debug message is shown in terminal when I kill the app, I am sending an SMS to me using Telephony package, which is sent successfully when the app is minimized. But it does not work if I kill the application, no SMS is sent. I am not sure what I am doing wrong here. My guess is that the Broadcast Receiver is killed when the app is killed. However, in the telephony package there is nothing to indicate that special care is done to keep the receiver alive when the app is killed.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文