将意图从工作人员发送到单独应用程序中的活动

Posted

技术标签:

【中文标题】将意图从工作人员发送到单独应用程序中的活动【英文标题】:Sending intents from a worker to an activity in a separate app 【发布时间】:2021-07-28 03:54:18 【问题描述】:

我有一个应用程序根据用户操作写入本地存储;说内容需要 被转发到另一个应用程序。

我的做法:

使用指向本地存储的文件观察器创建工作线程 从应用程序主 Activity 启动工作器 工作线程创建带有更新内容的意图并将其发送到单独的应用程序

我不确定(可能需要打开一个单独的问题),但是当活动停止时,活动中创建的所有内容都会被销毁,对吧?这意味着添加工作人员,文件观察者与他们定义的活动具有相同的寿命,对吗?

代码:

MainActivity.java:

public class MainActivity extends AppCompatActivity 

    private static final String TAG = MainActivity.class.getSimpleName();
    private static final String FILE_OBSERVER_WORK_NAME = "file_observer_work";

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.i(TAG, "Creating file observer worker");
        WorkManager workManager = WorkManager.getInstance(getApplication());

        WorkContinuation continuation = workManager
                .beginUniqueWork(FILE_OBSERVER_WORK_NAME,
                        ExistingWorkPolicy.REPLACE,
                        OneTimeWorkRequest.from(APIWorker.class));

        Log.i(TAG, "Starting worker");
        continuation.enqueue();

        final Button button = findViewById(R.id.button2);
        button.setOnClickListener(new View.OnClickListener() 
            public void onClick(View v) 
                Log.i(TAG, "Button clicked!");

                String stuffToWriteToFile = getStuff();
                String cwd = getApplicationInfo().dataDir;
                String stuffFilePath= cwd + File.separator + "stuff.json";

                PrintWriter stuffFile= null;
                try 
                    stuffFile = new PrintWriter(stuffFilePath, "UTF-8");
                    stuffFile.println(stuffToWriteToFile);
                    stuffFile.close();
                 catch (FileNotFoundException e) 
                    e.printStackTrace();
                 catch (UnsupportedEncodingException e) 
                    e.printStackTrace();
                
            
        );
    

    @Override
    public void onResume()
        super.onResume();
        // start worker here?
    

    @Override
    public void onStart() 
        super.onStart();
    // start worker here?
    

APIWorker.java: 公共类 APIWorker 扩展 Worker

    public APIWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) 
        super(context, workerParams);
    

    private static final String TAG = APIWorker.class.getSimpleName();

    @NonNull
    @Override
    public Result doWork() 
        Context applicationContext = getApplicationContext();
        Log.d(TAG, "Observing stuff file");

        FileObserver fileObserver = new FileObserver(cwd) 
            @Override
            public void onEvent(int event, @Nullable String path) 
                if(event == FileObserver.CREATE ||
                   event == FileObserver.MODIFY) 

                    String cwd = applicationContext.getApplicationInfo().dataDir;
                    String stuffFilePath = cwd + File.separator + "stuff.json";

                    String fileContents;
                    File observedFile = new File(stuffFilePath);
                    long length = observedFile.length();
                    if (length < 1 || length > Integer.MAX_VALUE) 
                        fileContents = "";
                        Log.w(TAG, "Empty file: " + observedFile);
                     else 
                        try (FileReader in = new FileReader(observedFile)) 
                            char[] content = new char[(int)length];

                            int numRead = in.read(content);
                            if (numRead != length) 
                                Log.e(TAG, "Incomplete read of " + observedFile +
                                        ". Read chars " + numRead + " of " + length);
                            
                            fileContents = new String(content, 0, numRead);

                            Log.d(TAG, "Sending intent ");
                            String packageName = "com.cam.differentapp";
                            Intent sendIntent = applicationContext.getPackageManager().
                                    getLaunchIntentForPackage(packageName);
                            if (sendIntent == null) 
                                // Bring user to the market or let them choose an app?
                                sendIntent = new Intent(Intent.ACTION_VIEW);
                                sendIntent.setData(Uri.parse("market://details?id=" + packageName));
                            
                            // sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            sendIntent.setAction(Intent.ACTION_SEND);
                            sendIntent.putExtra(Intent.EXTRA_TEXT, fileContents);
                            sendIntent.setType("application/json");

                            applicationContext.startActivity(sendIntent);

                            Log.d(TAG, "Intent sent ");
                        
                        catch (Exception ex) 
                            Log.e(TAG, "Failed to read file " + path, ex);
                            fileContents = "";
                        
                    
                
            
        ;

        fileObserver.startWatching();

        return null;
    

查看文档:

https://developer.android.com/guide/components/activities/background-starts

活动何时可以从后台启动是有限制的,但也有例外,即:

应用有一个可见的窗口,例如前台的一个活动。

意思是(我认为?)只要用户与应用程序(MainActivity)交互,后台工作人员就应该运行,对吗?如果活动被暂停/销毁,它就会停止,对吗?

【问题讨论】:

【参考方案1】:

如果您有不需要用户交互(显示或用户输入)的后台处理,通常您会使用Service。如果您的应用在前台,那么您的Service 可以使用startActivity() 启动其他活动。

我觉得你的建筑很奇怪。您正在使用Worker,它的生命周期最长为 10 分钟。您正在启动Worker,然后创建FileObserver 来检测文件的创建/修改。然后它读取文件并启动另一个Activity。这是一种非常复杂和迂回的做事方式。我怀疑你能否让这个工作可靠。

您的Activity 正在将数据写入文件系统。它可以在写入文件后调用一个方法(在后台线程上),然后将数据转发到另一个Activity。这会更简单,移动部件也更少。

我不知道Activity 的生命周期如何影响Workers。我会假设它们没有直接链接到Activity,因此当Activity 暂停或销毁时不会停止。

我还注意到您正在写入主 (UI) 线程上的文件(在您的 OnClickListener 中)。这不行,您应该在后台线程中执行文件 I/O,因为文件 I/O 可能会阻塞,而您不想阻塞主 (UI) 线程。

【讨论】:

感谢您的回答!对于更多上下文,应用程序是 kiwi 浏览器,文件编写器是在 kiwi 中运行的扩展程序 (js);因此(我认为?)没有直接的方法可以仅从 android 执行此操作。上面的代码来自一个准系统应用程序,该应用程序仅通过测试输入重现此功能(写入本地存储)。书面内容必须发送到另一个应用程序;在 kiwi 中,活动不会将数据写入文件。它最多可以监听变化。会创建一个服务 在主要活动中工作?从某种意义上说,如果另一个活动被放置在前台,它会被杀死,并且它是否可以将意图转发给外部活动(来自不同的应用程序)? Service 绝对是正确的选择。您可以在后台运行Service,当另一个Activity 在前台运行时不会被杀死。将数据转发到另一个Activity 可能会出现问题,但您为什么要这样做?从后台进程启动 Activity 会立即中断用户,这就是 Android 不鼓励这种行为的原因。 您可以在广播Intent 中发送数据。这可以由目标应用程序读取而无需启动Activity(目标应用程序只需注册一个由发送的广播Intent 触发的BroadcastReceiver。另一种方法是将数据发送到Service 在目标应用程序,但这可能更复杂。取决于目标应用程序在做什么。 那么我建议采用这种方法。

以上是关于将意图从工作人员发送到单独应用程序中的活动的主要内容,如果未能解决你的问题,请参考以下文章

仅在活动运行时将意图从服务发送到活动

将数据从通知发送到活动类而不重新启动意图

onClick 中的第二个意图在第一个之前执行

如何将活动中的意图传递给已打开的片段

传递对象在活动意图中返回 null

如何使用意图将哈希图值发送到另一个活动