Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e6dc996b authored by Régis Décamps's avatar Régis Décamps
Browse files

Reuse String literals rather new instance in UriMatcher

Micro-optimization: Instead of using a new instance of "#" and "*", reuse the
String literals. This will save 18 bytes for each "*" and "#" used in Uri
matchers.

Also, use switch/case rather than if/else for string matching.

Finally, add test and move TestClass in same package.

Bug: 32502682
Change-Id: Id672138a2213f68e05cafb4e88ed3c1e61c735a4
Test: Unit test
source build/envsetup.sh
lunch aosp_x86-eng
m -j 8
emulator -system out/target/product/generic_x86/system-qemu.img
make FrameworkCoreTests
adb -e install out/target/product/generic_x86/testcases/FrameworksCoreTests/FrameworksCoreTests.apk
adb -e shell am instrument -w -r -e class android.content.UriMatcherTest  com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
parent 4caeadd6
Loading
Loading
Loading
Loading
+18 −15
Original line number Diff line number Diff line
@@ -134,12 +134,12 @@ public class UriMatcher
        mText = null;
    }

    private UriMatcher()
    private UriMatcher(int which, String text)
    {
        mCode = NO_MATCH;
        mWhich = -1;
        mWhich = which;
        mChildren = new ArrayList<UriMatcher>();
        mText = null;
        mText = text;
    }

    /**
@@ -190,15 +190,7 @@ public class UriMatcher
            }
            if (j == numChildren) {
                // Child not found, create it
                child = new UriMatcher();
                if (token.equals("#")) {
                    child.mWhich = NUMBER;
                } else if (token.equals("*")) {
                    child.mWhich = TEXT;
                } else {
                    child.mWhich = EXACT;
                }
                child.mText = token;
                child = createChild(token);
                node.mChildren.add(child);
                node = child;
            }
@@ -206,6 +198,17 @@ public class UriMatcher
        node.mCode = code;
    }

    private static UriMatcher createChild(String token) {
        switch (token) {
            case "#":
                return new UriMatcher(NUMBER, "#");
            case "*":
                return new UriMatcher(TEXT, "*");
            default:
                return new UriMatcher(EXACT, token);
        }
    }

    /**
     * Try to match against the path in a url.
     *
@@ -273,7 +276,7 @@ public class UriMatcher
    private static final int TEXT = 2;

    private int mCode;
    private int mWhich;
    private String mText;
    private final int mWhich;
    private final String mText;
    private ArrayList<UriMatcher> mChildren;
}
+58 −2
Original line number Diff line number Diff line
@@ -14,14 +14,17 @@
 * limitations under the License.
 */

package android.net;
package android.content;

import android.content.UriMatcher;
import android.net.Uri;
import android.test.suitebuilder.annotation.SmallTest;

import junit.framework.TestCase;

import java.lang.reflect.Field;
import java.util.ArrayList;


public class UriMatcherTest extends TestCase {

    static final int ROOT = 0;
@@ -103,6 +106,59 @@ public class UriMatcherTest extends TestCase {
        checkAll(matcher);
    }

    /**
     * Tests that different {@link UriMatcher}s for {@code "#"} use the same
     * instance of the String {@code "#"}.
     */
    @SmallTest
    public void testTextCreatesNoDuplicateStrings() throws Exception {
        // Change the visibility of fields so that they can be tested without
        // making it non-private.
        Field textField = UriMatcher.class.getDeclaredField("mText");
        textField.setAccessible(true);

        UriMatcher matcher = new UriMatcher(ROOT);
        matcher.addURI("authority", "people/#", PEOPLE_ID);
        matcher.addURI("authority", "calls/#", CALLS_ID);

        UriMatcher authorityChild = getOnlyChild(matcher);
        ArrayList<UriMatcher> mChildren = getChildren(authorityChild);
        UriMatcher peopleChild = mChildren.get(0);
        UriMatcher callsChild = mChildren.get(1);
        assertEquals("people", textField.get(peopleChild));
        assertEquals("calls", textField.get(callsChild));
        UriMatcher peopleSharp = getOnlyChild(peopleChild);
        UriMatcher callsSharp = getOnlyChild(callsChild);
        assertTrue("There should be only one instance of String `#` but `"
                + textField.get(peopleSharp) + "` is not `"
                + textField.get(callsSharp) + "`",
                textField.get(peopleSharp) == textField.get(callsSharp));
    }

    /**
     * Returns {@link UriMatcher#mChildren}.
     */
    private ArrayList<UriMatcher> getChildren(UriMatcher matcher)
            throws IllegalAccessException, NoSuchFieldException {
        // Change the visibility of fields so that they can be tested without
        // making it non-private.
        Field childrenField = UriMatcher.class.getDeclaredField("mChildren");
        childrenField.setAccessible(true);
        return (ArrayList<UriMatcher>) childrenField.get(matcher);

    }

    /**
     * Returns the only element of {@link UriMatcher#mChildren}.
     */
    private UriMatcher getOnlyChild(UriMatcher matcher)
            throws IllegalAccessException, NoSuchFieldException {
        ArrayList<UriMatcher> children = getChildren(matcher);
        assertEquals("There should be one child for " + matcher,
                1, children.size());
        return children.get(0);
    }

    private void checkAll(UriMatcher matcher) {
        check("content://asdf", UriMatcher.NO_MATCH, matcher);
        check("content://people", PEOPLE, matcher);