简体   繁体   中英

Add Fragment with No UI to Activity from another Fragment

I am trying to add a worker Fragment to my MainActivity from a Fragment that was added to it. The Fragment implements a DownloadListener for the WebView it contains, is adding the worker Fragment when onDownloadStart() is called:

@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();
}

But when I add the new Fragment , it seems to be stuck in an infinite loop, calling onCreate , onCreateView , onCreateOptionsMenu and onDownloadStart in the current Fragment . Repeating these warnings after about twenty calls to 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

Until it finally stops after running out of memory:

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)

This is my current code for the 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();
    }
}

I also tried adding an onCreateView method, moving all of the code from onCreate there (except for setRetainInstance ), but that lead to the same result. I cannot figure out why it would be repeating the previous Fragment , what am I missing?

What I would suggest you is the following - don't do fragment transactions directly from other fragments. This is a bad practice and limits the flexibility and maintenability of your code. Also, it couples your fragments to one another, which is something you should avoid.

Use a listener interface to notify the hosting activity that it needs to create the headless fragment like this:

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);
    }
}

Then, in your activity, you implement the WebViewFragmentListener interface and handle the creation of your worker fragment and adding it to the activity in the onDownloadStart() method.

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();
    }
}

You can use the same approach to notify the activity when the download finished, so it can remove the download fragment.

This separation will make your code more organised and easy to debug.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM