0

Я пытаюсь добавить рабочего Fragment в мой MainActivity из добавленного Fragment. Fragment реализует DownloadListener для WebView он содержит, является добавление рабочего Fragment когда onDownloadStart() называется:Добавить фрагмент без интерфейса пользователя к действию из другого фрагмента

@Override 
public void onDownloadStart(String url, String userAgent, String contentDisposition, 
      String mimeType, long contentLength) { 
    getActivity().getSupportFragmentManager().beginTransaction() 
     .add(new DownloadFragment().newInstance(url, contentDisposition, mimeType), null) 
     .commit(); 
} 

Но когда я добавить новый Fragment, он, кажется, застрял в бесконечном цикле, вызывая onCreate, onCreateView , onCreateOptionsMenu и onDownloadStart в текущем Fragment. Повторяя эти предупреждения примерно через двадцать вызовов onCreateOptionsMenu():

12-10 08:33:36.717 25582-25582/com.example.package W/cr.BindingManager: Cannot call determinedVisibility() - never saw a connection for the pid: 25582 
12-10 08:33:36.732 25582-25582/com.example.package W/art: Attempt to remove non-JNI local reference, dumping thread 
12-10 08:33:36.734 25582-25582/com.example.package W/AwContents: onDetachedFromWindow called when already detached. Ignoring 

Пока, наконец, не останавливается после запуска из памяти:

12-10 08:33:37.123 25582-25582/com.example.package I/chromium: [INFO:GrGLUtil.cpp(169)] NULL GL version string. 
12-10 08:33:37.346 25582-25582/com.example.package W/cr.BindingManager: Cannot call determinedVisibility() - never saw a connection for the pid: 25582 
12-10 08:33:37.440 25582-26194/com.example.package E/chromium: [ERROR:gl_in_process_context.cc(208)] Failed to initialize GLES2CmdHelper 
12-10 08:33:37.464 25582-25582/com.example.package W/libc: pthread_create failed: could not allocate 1044480-bytes mapped space: Out of memory 
12-10 08:33:37.464 25582-25582/com.example.package E/chromium: [ERROR:platform_thread_posix.cc(112)] pthread_create: Try again 
12-10 08:33:37.722 25582-25582/com.example.package W/google-breakpad: ### ### ### ### ### ### ### ### ### ### ### ### ### 
12-10 08:33:37.722 25582-25582/com.example.package W/google-breakpad: Chrome build fingerprint: 
12-10 08:33:37.722 25582-25582/com.example.package W/google-breakpad: 1.9.1 
12-10 08:33:37.722 25582-25582/com.example.package W/google-breakpad: 69 
12-10 08:33:37.722 25582-25582/com.example.package W/google-breakpad: 45012863-7d3b-4c30-8ccf-e65394c57d85 
12-10 08:33:37.722 25582-25582/com.example.package W/google-breakpad: ### ### ### ### ### ### ### ### ### ### ### ### ### 
12-10 08:33:37.722 25582-25582/com.example.package A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 25582 (example.package) 

Это мой текущий код DownloadFragment:

public class DownloadFragment extends Fragment { 

    private static final int REQUEST_CODE_STORAGE = 0; 

    private static final String KEY_LINK = "link_key"; 
    private static final String KEY_DISPOSITION = "disposition_key"; 
    private static final String KEY_MIME_TYPE = "mime_type_key"; 

    private String mLink; 
    private String mDisposition; 
    private String mMimeType; 

    public WebFragment newInstance(String link, String contentDisposition, String mimeType) { 
     WebFragment fragment = new WebFragment(); 

     Bundle args = new Bundle(); 
     args.putString(KEY_LINK, link); 
     args.putString(KEY_DISPOSITION, contentDisposition); 
     args.putString(KEY_MIME_TYPE, mimeType); 
     fragment.setArguments(args); 

     return fragment; 
    } 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setRetainInstance(true); 

     mLink = getArguments().getString(KEY_LINK); 
     mDisposition = getArguments().getString(KEY_DISPOSITION); 
     mMimeType = getArguments().getString(KEY_MIME_TYPE); 

     if(ContextCompat.checkSelfPermission(getContext(), 
       Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { 
      if(shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { 
       // TODO: add request permission rationale dialog 
       Timber.d("Should show request permission rationale"); 
      } requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE_STORAGE); 
     } else { 
      downloadFile(mLink, mDisposition, mMimeType); 
     } 
    } 

    @Override 
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { 
     switch (requestCode) { 
      case REQUEST_CODE_STORAGE: 
       if(grantResults[REQUEST_CODE_STORAGE] == PackageManager.PERMISSION_GRANTED) { 
        downloadFile(mLink, mDisposition, mMimeType); 
       } else { 
        startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(mLink))); 
       } break; 
     } 
    } 

    public void downloadFile(String url, String contentDisposition, String mimeType) { 
     String fileName = URLUtil.guessFileName(url, contentDisposition, mimeType); 

     DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); 
     request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); 
     request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName); 
     request.allowScanningByMediaScanner(); 

     DownloadManager manager = (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE); 
     manager.enqueue(request); 

     getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit(); 
    } 
} 

Я также попытался добавить метод onCreateView, переместив весь код с onCreate там (кроме setRetainInstance), но это приводит к такому же результату. Я не могу понять, почему он будет повторять предыдущие Fragment, чего мне не хватает?

ответ

1

Я предлагаю вам следующее - не делайте операции фрагмента непосредственно с других фрагментов. Это плохая практика и ограничивает гибкость и тотальность вашего кода. Кроме того, он соединяет ваши фрагменты друг с другом, чего вам следует избегать.

Используйте интерфейс слушателя уведомить хостинг деятельность, что необходимо для создания обезглавленного фрагмента так:

public class WebViewFragment { 

    private WebViewFragmentListener mListener; 

    public void onAttach(Context context) { 
     if (context instanceof WebViewFragmentListener) { 
      mListener = (WebViewFragmentListener) context; 
     } else { 
      throw new IllegalStateException("Hosting activity doesn't implement the fragment listener interface"); 
     }   
    } 

    // rest of fragment logic skipped 

    @Override 
    public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) { 
     mListener.onDownloadStart(url, contentDisposition, mimeType); 
    } 

    public interface WebViewFragmentListener { 
     void onDownloadStart(String url, String contentDisposition, String mimeType); 
    } 
} 

Тогда в вашей деятельности, вы реализуете интерфейс WebViewFragmentListener и обрабатывать создание вашего фрагмента рабочего и добавление его к активности в методе onDownloadStart().

public class MainActivity extends AppCompatActivity implements WebViewFragmentListener { 

    // rest of the activity logic... 

    @Override 
    public void onDownloadStart(String url, String contentDisposition, String mimeType) { 
     final DownloadFragment downloadFragment = DownloadFragment.newInstance(url, contentDisposition, mimeType); 
     final FragmentManager fm = getSupportFragmentManager(); 
     fm.beginTransaction().add(downloadFragment, DownloadFragment.TAG).commit(); 
    } 
} 

Вы можете использовать тот же подход, чтобы уведомить деятельность, когда загрузка завершена, поэтому он может удалить фрагмент скачать.

Это разделение сделает ваш код более организованным и легким для отладки.

+0

Это не то же самое, что добавить 'implements DownloadListener' в мой' MainActivity' и использовать 'mWebView.setDownloadListener (((MainActivity) getActivity())' (вместо 'mWebView.setDownloadListener (this)')? Вместо того, чтобы создавать свой собственный интерфейс, я просто попробовал это с теми же результатами. Я просто обнаружил, что 'onDownloadStart' вызывается несколько раз, прежде чем снова вызвать' onCreate', что тоже кажется странным. – Bryan

+0

Кроме того, Причина, по которой я хотел использовать «DownloadFragment», состояла в том, чтобы я мог вызвать тот же код в разных местах моего приложения. Не обязательно полагаться на мою «MainActivity». Я планировал добавить «BroadcastReceiver» к моему «манифесту» для обработки 'ACTION_DOWNLOAD_COMPLETE', вместо того, чтобы добавлять его к нескольким действиям.И я думал, что' getActivity(). GetSupportFragmentManager() 'свяжет' DownloadFragment' с 'MainActivity', а не «WebFragment». Даже если 'WebFragment' вызывает метод, нет? – Bryan

+1

Если вы настроили свою деятельность как слушателя непосредственно на свой «WebView», вы снова связываете экземпляр действия с представлением, то есть менеджером совершенно другого компонента, и IMHO этого следует избегать. Подумайте о фрагментах как полностью изолированных модулях, которые должны предоставить API для внешнего мира. Таким образом было бы легче проектировать их в соответствии с лучшими принципами. –