Android Crawl pit trip is not easy to find bugs

Source: Internet
Author: User
Tags home screen

In the Android app development process, in addition to the model adaptation and other issues, often there will be some special bug, these bugs often need special scene circumstances will occur, here are some of the problems encountered in the usual projects and points of attention.

App Pack apk install after restart root interface problem

This is a very special problem, which is difficult to find in general, and is a bug that has been in the Android system all along.

When we package the app as an APK installer, the installation screen is launched by clicking the apk file.
And after the installation is successful, it jumps to the installation completion screen,

We click on the Open button in the image and we will launch our app.

Here to make it easier for everyone to understand something,

Let's say the app has two interfaces
* Start Interface Splashactivity
* Main interface Mainactivity
* After the app is opened splashactivity,3 seconds after the automatic jump mainactivity, the interface does not make mandatory finish

Next, we need to understand the next task stack and the back stack,
If a classmate is unfamiliar with these two concepts,
You can take a look at the official documents and tell them in detail:

Android task and return stack official documents

Here we quote the Official document in a nutshell:

The device Home screen was the starting place for most tasks. When the user touches an icon in the Application Launcher (or a shortcut on the "Home screen"), that application ' s task come s to the foreground. If No task exists for the application (the application have not been used recently), then a new task is created and the "Ma In "Activity for this application opens as the root activity in the stack.

When we click on the app launch icon in the Home screen (the installation completes the interface, click Open)

If no corresponding task stack exists, a new task stack is created,
and put the first page that the app launches as the root activity on the task stack.

If there is a corresponding task stack, the corresponding task stack will be called directly to the foreground, and the top interface will be displayed to the user.

Then when our app starts to open splashactivity and jumps to the main interface mainactivity, our app's task stack should:

At this point, when we click the Home button to return to the desktop,
The app's task stack goes backstage, and then we click the Start icon on the desktop,

Normally, the app should bring its task stack to the foreground and display the Mainactivity interface just above the top of the stack,

Normal process :

However, the reality is that the app will call its task stack to the foreground,

and re-create the new splashactivity on the task stack, then jump to Mainactivity,

Without reloading the application, it goes through the start-up process again, and we'll see that the activity in the task stack repeats, and splashactivity and Mainactivity become two

To make it clearer to everyone, here are two pictures,
* Incorrect bug Flow
* Task Stack in Error state

Bug Flow :

The newly called Splashactivity will be placed at the top of the app's task stack.

Two more activity.

Of course, this bug is very difficult for the general user to notice that it must meet the following conditions:
* Click the apk file to install the app
* Installation Complete screen click on the Open button
* Click the Home button to enter the system desktop, this time the app back to the background
* Then click on the desktop to launch the icon

So how do we deal with this problem?

* * According to the example above,
The task stack when launching the app into the Mainactivity interface under normal process * *:

In case of bug, the task stack will be redeployed to the foreground and add root Acitivy splashactivity to the top of the stack at this time:

As we can see, when the app is launched in a bug scenario, Splashactivity (the app's root activity) is created again and superimposed on the task stack.

Should only appear in the bottom of the splashactivity appear in other locations, so here we directly determine the location of the app root activity splashactivity

In the app's splashactivity (app root activity) OnCreate method through the Istaskroot () method to determine whether it is the root activity in the task stack, if it is not to do any processing, if not the direct finish off;

publicclass SplashActivity extends BaseActivity {@Override    protectedvoidonCreate(@Nullable Bundle savedInstanceState) {        setTheme(R.style.AppTheme_NoActionBar);        super.onCreate(savedInstanceState);        if (!isTaskRoot()) {            finish();            return;        }    }}

This splashactivity the top of the stack without executing other code, and the finish () is dropped, and the mainactivity at the top of the stack is displayed.

Android contains the fragment interface of the activity interface, after the app is released, back to the front desk, rebuilding activity caused fragment overlap

With the diversification of functional requirements, fragment's application scene is also more and more extensive, in which our home page bottom bar may be the most common scene.

What about the fragment overlap that we're talking about when the app is released from the system and back to the foreground activity?

We know that to use fragment's activity must inherit V7 's appcompatactivity,
And Appcompatactivity inherits from Fragmentactivity.

Our Onsaveinstancestate method is triggered when our app is back in the background in a state that is easy to be reclaimed by the system.

The activity using fragment calls the Onsaveinstancestate method to the parent class fragmentactivity,

Here I intercept the key code of Onsaveinstancestate in Fragmentactivity:

/**     * Save all appropriate fragment state.     */    @Override    protectedvoidonSaveInstanceState(Bundle outState) {        super.onSaveInstanceState(outState);        Parcelable p = mFragments.saveAllState();//获取FragmentManager保存的所有Fragments        ifnull) {            outState.putParcelable(FRAGMENTS_TAG, p);//Fragment不为空,执行保存操作        }       ...        }    }

We see that the code here saves the state of fragment,
In the OnCreate method of fragmentactivity, these fragment are reconstructed:

 /**     * Perform initialization of all fragments and loaders.     */    @SuppressWarnings("deprecation")    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        mFragments.attachHost(null /*parent*/);        super.onCreate(savedInstanceState);        ...        if (savedInstanceState != null) {            Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);            mFragments.restoreAllState(p, nc != null ? nc.fragments : null);        ...           }   ...    }

In other words, the interface is rebuilt after being released by the system, which re-triggers the activity's OnCreate method,

If the developer does not judge OnCreate's saveinstance variable adjustment creation logic and executes the fragment creation code directly, the new fragment will overlap with the system recovery.

This issue is triggered on the one hand (low-end devices such as red rice are normal and often release apps) because of extreme memory shortages.

On the other hand, because the fragment interface is not transparent, the problem may not be discovered even if it is superimposed.

So how do we deal with such a problem, and here are three ways to deal with it:

1. Determine if the savedinstancestate variable is null in the activity's OnCreate,
If savedinstancestate is null to indicate that the interface is new, perform the full fragment tab initialization work;
If savedinstancestate is not NULL, the activity is freed and rebuilt, then the creation of fragment is not performed, the relevant logic code is executed,

The code is as follows:

@Override    protectedvoidonCreate(@Nullable Bundle savedInstanceState) {        ifnull) {        //界面正常情况下create时的逻辑            initTab();        }        else {        //界面在内存不足情况下被强制回收后重新create的逻辑        }}

2. This method is what I call lazy people's practice

Activity that uses fragment will call Super.oncreate () first when calling the OnCreate method

And Super.oncreate will eventually execute the fragmentactivity OnCreate method,

From the code intercepted above, we see that the OnCreate method of fragmentactivity will determine whether the fragment in Saveinstancestate is empty, and the saved fragment will be restored without being empty.

if (savedInstanceState != null) {            Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);            mFragments.restoreAllState(p, nc != null ? nc.fragments : null);        ...           }

That is, we empty the value of the Fragments_tag before executing to this code, so that it does not trigger a recovery of the system rebuild.

Then we just need to add the following code to the OnCreate method using fragment's activity:

@Override    protectedvoidonCreate(@Nullable Bundle savedInstanceState) {        ifnull) {            savedInstanceState.putParcelable("android:support:fragments"null);//清空保存Fragment的状态数据        }        super.onCreate(savedInstanceState);}

Thus, fragments_tag corresponding data is emptied before the oncreate is executed to fragmentactivity.

3. Also lazy method, directly rewrite the Onsaveinstancestate method, Comment out super.onsaveinstancestate, so that will not save fragment data, but the side effects are very obvious, that is, onsaveinstancestate completely lost its role,
So it is not recommended that you do so, only for reference:

  @Override    publicvoidonSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {//        super.onSaveInstanceState(outState, outPersistentState);    }

For the scenario where the simulation app was released, here's a little way to do it, after the app runs, press the home key to go back to the background, and then open the Computer command line tool to run:

  shellkill 包名packagename

The app will then be released, and then the app can be opened via the Task Manager or the launch icon, and the interface will rebuild the onrestoreinstancestate.

After the app calls the system camera, the photo returns to crash

In general, most of our cases are invoked by the way the URI is passed to the system camera:

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);mTakePhotoUri = FileUtils.getOutputMediaFileUri(FileUtils.MEDIA_TYPE_IMAGE);intent.putExtra(MediaStore.EXTRA_OUTPUT, mTakePhotoUri);                startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);

This method of invoking a system camera by specifying the URI store path

At the time of Onactivityresult, the returned intent will have no data

So we usually get the Onactivityresult uri (the Mtakephotouri in the example, the variable is a global variable) variable to get the specific picture file.

Formally due to this issue, resulting in the app being released in the background regardless of the system camera being called
Or is it auto-rotation when you call a camera like Samsung?
Will cause the interface of the calling camera to be freed and rebuilt, resulting in the loss of the global variable value of the Activity interface.

If you do not save this global variable in onsaveinstancestate, and retrieve the value of Mtakephotouri in Onrestoreinstancestate, then the rebuilt interface variable is lost. Therefore, the Mtakephotouri in the Onactivityresult is null, which results in NULL when the picture path variable is fetched.

After the test, after this processing, most of the camera crash problem can be solved.

In fact, not only the camera, a lot of features in the actual development process may encounter due to the interface is released resulting in variable data loss, so we need to in the Onsaveinstancestate method according to the actual situation to save the required variables, Retrieve the variable in the Onrestoreinstancestate method.

Of course, if you feel too much trouble, here we recommend a lazy library, can automatically save our variables, very convenient

Https://github.com/frankiesardo/icepick

Use Eventbus on Android 4.1 and other devices caused by:java.lang.ClassNotFoundException:Didn ' t find class Android.os.PersistableBundle "on Path:dexpathlist

I've only had this problem on Android 4.1 devices and no error on other devices.

The reason for this error is that I inadvertently rewritten the onsaveinstancestate (Bundle outstate, Persistablebundle outpersistentstate) method
(You should normally rewrite Onsaveinstancestate (Bundle outstate))

If you don't have 4.1 of your equipment on hand, the problem may never be found.

The error of is 32-bit instead of 64-bit appears after introducing the picture frame fresco

This problem is mainly caused by the loading mechanism of the Android system for so files.

Mobile phones with different CPU architectures will find their corresponding directories under Libs. From the corresponding directory to find the required. So file, if there is no corresponding directory, will go to armeabi down to look for, if there is a corresponding directory, but if not find the corresponding. So file, will not go to armeabi down to look for.

My project only references the so file for the Armeabi and x86 schemas, where we assume the lib.so file

When I use a mobile phone with a ARM64-V8 architecture, because the ARM64-V8 corresponding directory is not found, the system will downgrade to Armeabi to find the lib.so file.

Fresco picture frame because of the compatibility of so, compile introduced the compile time to bring the arm64-v8 so file, resulting in a arm64-v8 directory.

When the project is packaged and compiled and installed, the ARM64-V8 architecture of the mobile phone because found in the ARM64-V8 directory, so all so files will go to arm64-v8 directory to find, no longer to find Armeabi directory, and in the ARM64-V8 directory, I did not configure the corresponding lib.so file, so the lib.so file was not found, and the is 32-bit instead of 64-bit error was thrown.

So how do we solve this, here are three ways to:

    1. Add the so library corresponding to the ARM64-V8 schema for the so library already referenced by the project, it is difficult to configure the corresponding version of so file without the source code.

    2. Remove the so file from the ARM64-V8 directory of the referenced library;

    3. Set in the Defaultconfig of Gradle

ndk {    // 设置支持的 SO 库构架,注意这里要根据你的实际情况来设置    ‘armeabi‘‘x86‘}

This will fix only the so files that will only pack the Armeabi and x86 directories, and this will prevent app errors by accidentally introducing other directories ' so files when using unfamiliar libraries.

There are all kinds of strange problems in the actual development of Android app, if you also encounter some special or wonderful bugs, welcome to add

Android Crawl pit trip is not easy to find bugs

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.