Some problems and solutions of using fragment components in Android applications _android

Source: Internet
Author: User
Tags message queue

The main meaning of fragment is to provide a lifecycle callback that is bound to the activity.
Fragment does not necessarily add view to the view level of the activity. When a module needs to obtain an activity's lifecycle callback, it can be considered by fragment.
For example: Dialogfragment, call the Show method to display a dialog (this one is not in the view level of the activity), and when the screen is rotating, Dialogfragment uses Ondestroyview callbacks to dismiss Dialog, then after the activity is rebuilt, Dialogfragment uses OnStart callbacks to display Dialog again.
Of course, we can also create a fragment that has no UI at all, like Backgroundworkerfragment, to execute a task when Onresume, and to suspend a task when onpause.

Fragment life cycle
to review the basics first, fragment's life cycle diagram is as follows:

Description: In general, the life cycle of fragment and activity is similar. It should be noted that it is more Onattach (), Ondetch (), Oncreateview () and Ondestroyview () than the activity, but less onrestart ().
The life cycle of fragment is very complex and is divided into the following situations:

    • If it is instantiated through the <fragment/> tag in XML, the first callback received will be the Oninflate
    • If Setretaininstance (true), then when the activity is rebuilt, the fragment OnDestroy and the OnCreate callback fragment after the activity is rebuilt are not invoked. (Whether or not you add it to the return stack)
    • If fragment A is currently displayed and then executes Fragmenttransaction.replace (), then fragment a executes OnPause ()->onstop ()->ondestroyview ()- >ondestroy ()->ondetach (), if you execute Fragmenttransaction.replace (). Addtobackstack (), then fragment A will execute OnPause ()-> OnStop ()->ondestroyview ()
    • Fragmenttransaction.hide () will not cause onpause (), but will trigger onhiddenchanged ()
    • Fragmenttransaction.detach (), which results in OnPause ()->onstop ()->ondestroyview (), note that OnDestroy () and Ondetach () do not call

Fragmenttransaction

    • For fragment operations are done through Fragmenttransaction, a fragmenttransaction can contain one or more operations, Submitted by commit or Commitallowingstateloss. If the fragmenttransaction is added to the stack, all operations in that transaction will be revoked when the stack is dropped.
    • The Commit method is asynchronous (handler post the corresponding message to the Mainlooper associated message queue), and if the transaction operation needs to be performed immediately, You can call Executependingtransactions ()
    • Fragmenttransaction's Commit method and the Fragmentmanager Popbackstack method are all asynchronous, causing a lot of inconvenience to the caller, Although it can be done immediately by calling the Executependingtransactions () method, why is the default asynchronous?? (I think it's because: submitting a transaction can result in the execution of fragment lifecycle methods, even the execution of multiple callbacks, if fragment submits a new transaction in these callbacks, This may damage the current transaction state, for example, it is a pop operation.

Can not perform this action after onsaveinstancestate

In the process of using fragment, it is common to encounter crash caused by the operation of commit or popbackstack after the Onsaveinstancestate method invocation of the activity.
Because the operating state after the Onsaveinstancestate method may be lost, the Android framework throws an exception by default.
For a commit method, simply avoiding the exception is simple, using the Commitallowingstateloss method. But Popbackstack and popbackstackimmediate also check the state ( Checkstateloss), special attention is the onbackpressed method of activity

public void onbackpressed () {
  if (!mfragments.popbackstackimmediate ()) {//Note
    supportfinishaftertransition () ;
  }
}

If onbackpressed is called after onsavedinstancestate, it will crash.
onbackpressed Time of Call:

* Targetsdkversion <= 5, called in onkeydown
* targetsdkversion > 5, called in onkeyup
Onsavedinstancestate invocation Time (if invoked):

* Must be before onstop
* May be before onpause, or between OnPause and OnStop
Note that the Onsavedinstancestate method is not necessarily invoked, it needs to be invoked only if the activity is destroyed by the framework for some reason, and it needs to be recreated later (for example: the spin screen, Or the memory is not enough to reclaim certain activity in the stack.

Example:
* Activity A in the foreground, the screen gradually darkened until the lock screen, then a onsavedinstancestate will be called
* The onsavedinstancestate of activity a start activity b,activity a will be called
* Activity A is returned to the previous interface because of the return key or the finish call, then the onsavedinstancestate of a is not invoked
Therefore, when onbackpressed is called after the Onsavedinstancestate method, it will be crash. There are two main solutions:

Rewrite the Onsavedinstancestate () method of the activity and comment out the super call.
This method avoids crash, but it can cause the whole activity to lose its state. Take Dialogfragment as an example, normally, the displayed dialogfragment does not need to be processed after the spin screen activity is recreated. Dialog is automatically displayed (see Dialogfragment.onstart ()), but after the Onsavedinstancestate () method of the activity is commented out, the fragment status is lost, and after the activity is recreated , dialog will not be shown again.

A better and more common approach is to determine whether the Onsavedinstancestate () method has been executed before invoking Commit,popbackstack and onbackpressed methods, and the Onresume method has not yet been executed, and if not, Then direct operation, or join the pending queue, wait for onresumefragments or onpostresume after the execution.

Note: Do not operate in Onresume, because the mstatesaved in Fragmentmanager may still be true. (If the order of execution is onsavedinstancestate ()->onpause ()->onresume () or OnPause ()->onsavedinstancestate ()->onResume ())

For example:

The public void ondatareceived () {
  if (isstatesaved ()) {//isstatesaved () is provided
    by baseactivity Addpendingfragmentoperation (New Runnable () {
      @Override public
      void Run () {
        Getsupportfragmentmanager (). Popbackstackimmediate ();}}
    );
  else {
    Getsupportfragmentmanager (). Popbackstackimmediate ();
  }
}

@Override
protected void Onpostresume () {
  super.onpostresume ();
  if (pendingfragmentoperation!= null &&!pendingfragmentoperation.isempty ()) {for
    (Runnable operation: pendingfragmentoperation) {
      operation.run ();
    }
    Pendingfragmentoperation.clear ();
  }


Startactivityforresult

Available intervals for Requestcode:

1.Activity: [Integer.min_value, Integer.max_value]
(1) When the Requestcode takes a value in the [Integer.min_value,-1] interval, the effect is the same as startactivity () and does not receive a onactivityresult () callback
(2) Built-in fragment available Requestcode the same range and activity
2.support Library: Fragment, and fragmentactivity:[-1, 65535]
(1) Requestcode = = 1, the effect and startactivity (), will not receive Onactivityresult () callback
(2) Requestcode throws an exception between [Integer.min_value,-2] or [65536, Integer.max_value] (Requestcode can only use 16 bits low)
Recommendation: Requestcode's value is uniformly limited between [-1, 65535]

Nesting fragment

The first thing to say is try not to use nested fragment.
Problems encountered when using Startactivityforresult () in nested fragment:

All the fragment are not Onactivityresult ()
A Level 1 fragment received Onactivityresult ()
In short, the nested fragment that initiates the Startactivityforresult () will not receive the Onactivityresult () callback.

The reasons are as follows: (refer to the requestcode mentioned above)
Fragmentactivity.startactivityfromfragment () will change the Requestcode, with a high 16 bits to store the fragment in Fragmentmanager index, The lower 16 bits are used as fragment usable requestcode. In Fragmentactivity.onactivityresult (), according to the height of 16 bits, find the corresponding fragment from the Fragmentmanager, The lower 16-bit value is then used as a requestcode to invoke Fragment.onactivityresult ().

Then only one index, the fragment index in root Fragmentmanager, can be stored in requestcode. Therefore, the scenario listed above will appear:

    • When the nested fragment index in the Childfragmentmanager is greater than all index in the Rootfragmentmanager, Rootfragmentmanager will not find the fragment corresponding to this index, so no fragment can receive Onactivityresult ()
    • When a nested fragment in the Childfragmentmanager is less than equal to all index in the Rootfragmentmanager, Then a fragment attached to Rootfragmentmanager will receive Onactivityresult ()
    • In short, even if there is a fragment can receive Onactivityresult (), that is the top of a fragment, rather than the launch of the requested nesting fragment

Solution:

    • Do not use nested fragment:)
    • Still use Requestcode, it is low 16 bits split, of which the high 8 bits used to store the index in Childfragmentmanager, low 8 bits left to childfragment use. (If the nesting level is not deep, then this scenario is good, if the level is deep, then leave fragment Requestcode of the available value range will be very limited)
    • After the Android 4.2 (Api 17), you can use the built-in fragment, as well as Childfragmentmanager, The built-in fragment no longer needs to record its index with the help of a requestcode of 16 bits. Instead, when the framework receives the Fragment.startactivityforresult (), it records the fragment identity ( Android:fragment:${parentindex}:${myindex}), when distributing result, Find the fragment according to this sign. So there is no question of childfragment receiving the Onactivityresult () callback. can refer to Activity.dispatchactivityresult ()

Tips

When developing, you can open fragment related debugging information

Fragmentmanager.enabledebuglogging (buildconfig.debug);
When the onresume of the activity is invoked, the fragment Onresume is not yet invoked.
protected void Onpostresume () {
  super.onpostresume ();
  Mhandler.removemessages (msg_resume_pending);
  Onresumefragments ();
  Mfragments.execpendingactions ();
}
protected void Onresumefragments () {
  mfragments.dispatchresume ();
}

If you need to perform an operation after the onresume of the fragment, you can override the Onpostresume () method and make sure to call Super.onpostresume ()

1.IllegalStateException (Fragment not attached to activity) problem
This exception typically occurs when you start an asynchronous task in fragment and then perform a resource-related operation in the callback (GetString (...)), or startactivity (...) But this time fragment may have been detach, so it's mhost==null, so before performing these operations, you need to Judge isadded () first.
Note: Do not use isdetached () here to determine, because after fragment is detach, its isdetached () method may still return false

2. If fragment A is detach by replace, then its isdetached () will return false
3. If fragment a corresponds to the fragmenttransaction is added to the return stack and detach because of the stack, then its isdetached () will return true

Final public Resources getresources () {
  if (mhost = = null) {
    throw new IllegalStateException ("Fragment" + This + "Not attached to activity");
  }
  Return Mhost.getcontext (). Getresources ();
public void StartActivity (Intent Intent, @Nullable Bundle options) {
  if (Mhost = null) {
    throw new Illegalstateex Ception ("Fragment" + this + "not attached to activity");
  }
  Mhost.onstartactivityfromfragment (This/*fragment*/, intent,-1, options);
}

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.