-
Notifications
You must be signed in to change notification settings - Fork 24.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add support for native/downloadable fonts #23865
Conversation
@hey99xx please review |
int style, | ||
int weight, | ||
@Nullable String family, | ||
Context context) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of duplicating the method above will minimal change, why not call apply(paint, style, weight, family, context.getAssets());
.
I know you deprecated the method above, but until you can get rid of CustomStyleSpan(..., AssetManager)
constructor I think it's a more concise solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah nevermind. Then you would call old ReactFontManager.getTypeface
method again. Still, there might be a prettier way to refactor this. For example you can have a single method that takes both args:
private static void apply(
Paint paint,
int style,
int weight,
@Nullable String family,
Context context,
AssetManager assets) {
// ...
if (family != null) {
ReactFontManager fontManager = ReactFontManager.getInstance();
typeface = context != null
? fontManager.getTypeface(family, want, context)
: fontManager.getTypeface(family, want, assets);
} else if (typeface != null) {
String fontFamilyName, | ||
int style, | ||
Context context) { | ||
int fontId = context.getResources().getIdentifier(fontFamilyName, "font", context.getPackageName()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if the first priority should go to mFontCache
(which has advantage of it can be changed programmatically) or to the static assets.
Also for static assets, I'd guess both getIdentifier
and Typeface.create
call can be slow, that's why there are caches in the first place. If you have 100s of textviews, you probably don't want to redo these computations that never change.
ResourcesCompat.getFont
is documented as a blocking call. I don't know if that means it blocks for network or something else, but keep that in mind too. I don't have too much knowledge about the new font system, this is just looking up to javadoc, but I don't know if this would then work for downloadable fonts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe adding cache would be a good idea, like you said it'll improve performance for 100s texts.
Overall I really like the idea though. In our app we have fonts with 3+ different weights and have to hardcode asset names as fontFamily style. This brings us much closer to iOS that does not have this problem. |
@hey99xx yep, feature parity with iOS is the motivation for this PR. We have medium weights in our UI and I have to check platform and specify fontFamily on Android, but fontWeight on iOS. I have a concern that current API might be used somewhere in FB and changing it might break their apps, therefore created almost identical APIs. |
@hey99xx thank you for the comments. I hope that I'll get someone from FB to review and maybe remove deprecated APIs if they're not used at FB. |
That's why I didn't comment on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mdvacca is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hramos has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
This pull request was successfully merged by @dulmandakh in f01c4e2. When will my fix make it into a release? | Upcoming Releases |
Summary: Android API 26 and Android Support Library 26 added support for font resource type and native/downloadable fonts. It allows apps to easily download fonts from online providers, but also use of various font weights other than normal and bold, like medium. So it deprecated APIs for asset fonts, and should be removed in the future. Advantages: - Just copy font files in res/font and use it specifying filename (without extension) in fontFamily - Define custom font-family using XML file (in res/font) and font files, it may have many weights and styles. See PR for example. - Define configuration to download fonts from online font providers, and use it. See https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml and https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts [Android] [Changed] - add support for custom/downloadable fonts Pull Request resolved: facebook#23865 Differential Revision: D14506542 Pulled By: hramos fbshipit-source-id: 67ba3148fb4b548cdbc779213cf6c1b2c3baffd2
Heads up: we're currently analyzing this commit and we suspect it's causing some start up regression for some RN surfaces at Facebook. I'll comment on the code that might be causing it, but in the short term we'd very likely need to revert this commit. I'll provide suggested "fix", but it's actually not that simple. |
); | ||
} | ||
|
||
int fontId = context.getResources().getIdentifier(fontFamilyName, "font", context.getPackageName()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So getIdentifier()
is problematic in a big app that has many resources. This could contribute to non-trivial msec, just trying to crack open the resources, then looking it up from the APK. Rather than calling this for all scenarios, we'd instead build a "provider" pattern where:
- each app decides if it wants to provide a custom fonts
- if no provider is given, avoid asking
getIdentifier()
completely - if given, ask the provider for a resourceId (int), then use it
Now the implementation of the provider needs to be a switch statement for the best performance. Example:
int getCustomFontProvider(name) {
switch(name) {
case "mine":
return R.font.mine;
...
Referencing R.font.*
directly is fast and efficient, because it points exactly to an int resource Id. The only problem is, that each app needs to implement this code themselves, because each app has different set of custom fonts.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about checking custom fonts the old way first, then the new way later, so that it won't affect FB. It's just a workaround for FB. I think that custom font feature parity with iOS is very important for general public. |
What do you mean by the “old way”? Regardless of whether parity is important, we’re trying to mitigate regression as the current priority |
When someone tried to use a custom font it'll check
I'm proposing to reverse it, so FB will find fonts in assets and won't cause regression. |
oh but I don’t think fb uses custom fonts (for RN) today, so we don’t want to look up any font |
to satisfy all use cases, the provider model should work (fb wont have any provider, so fb isn’t affected, and RNTester will provide a provider, so custom fonts will work) |
Please feel free to revert the commit. I'll look for a generic solution and add you as reviewer. |
@fkgozali I would like to have a TypedArray resource where you list custom fonts to make them available in React Native, so it won't lookup for font resources and won't affect performance. iOS does the same, you have to list custom fonts in Info.plist. |
FYI the revert is eb40b09 |
Summary: In #23865, RN introduced support for custom fonts the Android Way. But it introduced performance regression because it'll lookup for a font using getIdentifier() every time fontFamily changed. This PR fixes regression by requiring custom fonts to be listed in **fonts** array, and populating **mTypeCache** at first use using the list. [Android] [Changed] - Require custom fonts to list in **fonts** array. Fixes performance regression. Pull Request resolved: #24595 Reviewed By: mdvacca Differential Revision: D15184590 Pulled By: fkgozali fbshipit-source-id: e3feb2396609583ebc95101130186a1f5af931da
Since this PR was reverted, is there any known issue/PR open about this bug? I'm currently having this problem and I'm not sure if I'm doing something wrong or if this bug still occurs.
Thanks you =) |
Any news on that thread ? Where to follow since revert |
Summary
Android API 26 and Android Support Library 26 added support for font resource type and native/downloadable fonts. It allows apps to easily download fonts from online providers, but also use of various font weights other than normal and bold, like medium. So it deprecated APIs for asset fonts, and should be removed in the future.
Advantages:
See https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml and https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
Changelog
[Android] [Changed] - add support for custom/downloadable fonts
Test Plan
CI is green and RNTester app's Text examples include Srisakdi font example.