0

我正在编写一个可以通过接收带有 ACTION_VIEW 或 ACTION_EDIT 的意图从另一个应用程序启动的应用程序。例如,可以通过查看电子邮件附件来打开它。麻烦的是,当您单击主页按钮并再次单击您正在使用的电子邮件应用程序的启动图标时,我的活动被终止,并且已进行的任何用户编辑都将丢失。我想要发生的是,当用户单击主页按钮时,我的活动被重新设置为父项,以便当用户单击我的应用程序的启动图标时它会恢复。我尝试在 manifest.xml 中设置 android:allowTaskReparenting="true" 但这不起作用。有时它根本没有任何效果,有时活动被移动到我的启动图标,但当您再次单击电子邮件应用程序图标时仍然会被杀死。allowTaskReparenting 的文档真的很模糊。它说该属性意味着:

“Activity 是否可以从启动它的任务转移到与其有关联的任务。”</p>

这个词在这里是什么意思?我想要的是保证活动确实移动(并保持在那里)。有什么办法可以做到这一点?

提前感谢任何可以提供帮助的人。

编辑

为了回应下面的评论,我整理了一个婴儿版本来展示我遇到的问题。当您通过单击另一个应用程序中的文件(例如,电子邮件的附件)启动 EditFileActivity 时,您可以编辑该文件。但是单击主页图标,然后再次单击电子邮件应用程序图标会导致您对文件所做的更改丢失。我希望 android 系统仅在用户明确单击返回然后说“是”或“否”时才忘记 EditFileActivity 的实例。理想情况下,我希望 EditFileActivity 的所有实例都堆积在我的应用程序的启动图标上。我可以通过使用 singleTask 或 singleInstance 并编写某种活动来显示选项卡中所有打开的文件来实现类似的东西,但如果我能让 android 系统本身帮助我,那就容易多了。

这是一个演示问题的完整项目。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.example.Example"
         android:versionCode="1"
         android:versionName="1.0">
   <uses-sdk
       android:minSdkVersion="11"
       android:targetSdkVersion="11"/>
   <application
       android:label="Example"
       android:icon="@drawable/ic_launcher">
       <activity
           android:name=".LaunchActivity"
           android:label="LaunchActivity"
           android:screenOrientation="portrait">
           <intent-filter>
               <action android:name="android.intent.action.MAIN"/>
               <category android:name="android.intent.category.LAUNCHER"/>
           </intent-filter>
       </activity>
       <activity
           android:name=".EditFileActivity"
           android:label="EditFileActivity"
           android:screenOrientation="portrait">
           <!-- This is just an example. I wouldn't use this intent filter in a real app! -->
           <intent-filter>
               <action android:name="android.intent.action.VIEW"/>
               <action android:name="android.intent.action.EDIT"/>
               <category android:name="android.intent.category.DEFAULT"/>
               <category android:name="android.intent.category.BROWSABLE"/>
               <data android:scheme="file"/>
               <data android:scheme="content"/>
               <data android:mimeType="*/*"/>
               <data android:host="*"/>
           </intent-filter>
       </activity>
   </application>
</manifest>` 

启动活动:

public class LaunchActivity extends Activity {

   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       TextView textView = new TextView(this);
       textView.setText("This is the activity you see when you click on the application's launch icon. It does absolutely nothing.");
       textView.setTextSize(18);
       setContentView(textView);
   }
}

编辑文件活动:

public class EditFileActivity extends Activity {

   // This String represents the contents of the file.
   // In a "real" app the String would be initialised by reading the data from the Intent that started the activity.
   // However, for the purposes of this example, the initial value is "Default".
   private String fileContents = "Default";
   private boolean editsMade = false;
   private TextView textView;

   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       textView = new TextView(this);
       textView.setText(fileContents);
       textView.setTextSize(18);
       textView.setPadding(10, 10, 10, 10);
       setContentView(textView);
       textView.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               makeEdits();
           }
       });     
   }

   @Override
   public void onBackPressed() {
       if (editsMade) {
           savePrompt();
       } else {
           finish();
       }
   }

   private void savePrompt() {
       DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
           @Override
           public void onClick(DialogInterface dialog, int which) {
               if (which == Dialog.BUTTON_POSITIVE) {
                   // Here is where I would save the edited file.
                   Toast.makeText(EditFileActivity.this, "File saved", Toast.LENGTH_LONG).show();
               }
               finish();
           }
       };
       new AlertDialog.Builder(this)
               .setTitle("Close File")
               .setMessage("Do you want to save the changes you made?")
               .setPositiveButton("Yes", listener)
               .setNegativeButton("No", listener)
               .show();
   }

   private void makeEdits() {
       final EditText editText = new EditText(this);
       editText.setText(fileContents);
       new AlertDialog.Builder(this)
               .setTitle("Edit File")
               .setView(editText)
               .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                   @Override
                   public void onClick(DialogInterface dialog, int whichButton) {
                       Editable editable = editText.getText();
                       assert editable != null;
                       String newContents = editable.toString();
                       if (!fileContents.equals(newContents)) {
                           editsMade = true;
                           fileContents = newContents;
                           textView.setText(fileContents);
                       }
                   }
               })
               .setNegativeButton("Cancel", null)
               .show();
   }
}

2014 年 10 月 12 日更新

遇到的问题是由于使用了 Intent 标志 FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET。幸运的是,从 API 级别 21 开始,Google 已弃用此标志。

4

2 回答 2

1

问题

麻烦的是,当您单击主页按钮并再次单击您正在使用的电子邮件应用程序的启动图标时,我的活动被终止,并且已进行的任何用户编辑都将丢失。

发生这种情况是因为电子邮件应用程序FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET在启动您的活动时设置了意图标志。设置此标志后,下次将任务带到前台时,您的活动将完成,以便用户返回上一个活动。

来自文档

这对于应用程序中存在逻辑中断的情况很有用。例如,一个电子邮件应用程序可能有一个查看附件的命令,该命令启动一个图像查看活动来显示它。这个活动应该是电子邮件应用程序任务的一部分,因为它是用户参与的任务的一部分。但是,如果用户离开该任务,然后从家里选择电子邮件应用程序,我们可能会喜欢他们回到他们正在查看的对话,而不是图片附件,因为那会令人困惑。通过在启动图像查看器时设置此标志,该查看器及其启动的任何活动都将在用户下次返回邮件时被删除。

解决方案:为您的活动使用singleTasklaunchMode。电子邮件应用程序不会终止您的活动,因为该活动现在属于不同的任务。

如果活动实例已经在任务中并且尝试再次启动活动,则不会创建新实例。而是onNewIntent调用。在呈现新内容之前,您可以在此处提示用户保存之前的编辑(如果有)。

于 2014-09-03T02:18:55.443 回答
0

如上所述,系统的默认行为会在活动停止时保留活动的状态。这样,当用户导航回上一个活动时,其用户界面会以他们离开的方式显示。但是,您可以并且应该使用回调方法主动保留活动的状态,以防活动被销毁并且必须重新创建。

当系统停止您的一个活动时(例如当一个新活动启动或任务移至后台时),如果系统需要恢复系统内存,它可能会完全销毁该活动。发生这种情况时,有关活动状态的信息将丢失。如果发生这种情况,系统仍然知道该活动在后堆栈中有一个位置,但是当活动被带到堆栈的顶部时,系统必须重新创建它(而不是恢复它)。为了避免丢失用户的工作,您应该通过在活动中实现 onSaveInstanceState() 回调方法来主动保留它。

资源

于 2014-08-29T04:27:41.407 回答