Fix V2RayNG Subscription Link Import on Android: A Complete Technical Guide
To fix V2RayNG subscription link import failures on Android, extend the URL scheme detection logic in the Fanqiang client to recognize v2ray:// prefixes as valid VMess links, allowing the existing StandardV2RayBean.deserialize() pipeline to process NG-style subscriptions.
The bannedbook/fanqiang repository provides a comprehensive proxy client for Android that stores configurations as SubscriptionBean objects. When importing V2RayNG subscription links, users encounter failures because the app only recognizes legacy vmess:// schemes while NG uses the newer v2ray:// format. This guide explains how to fix V2RayNG subscription link import on Android by modifying the scheme detection logic in the source code.
Understanding the V2RayNG URL Schema Problem
The Legacy VMess vs NG Format
V2RayNG introduced a new URL schema using v2ray:// with query parameters that differ from the classic vmess:// format. While both encode base64 JSON configuration data, the original parser in StandardV2RayBean only handles the legacy prefix, causing NG links to be discarded during import.
StandardV2RayBean Architecture
Proxy configurations inherit from StandardV2RayBean (io.nekohasekai.sagernet.fmt.v2ray.StandardV2RayBean), which provides the core deserialization logic. Concrete implementations like VMessBean extend this base class, but the URL-to-bean dispatch logic must first recognize the scheme to instantiate the correct type.
How the Android Import Pipeline Works
-
URL Reception – The UI component (e.g., Add Subscription screen) captures the raw subscription string from user input or QR scan.
-
Scheme Detection – The adapter checks URL prefixes (
vmess://,trojan://,ss://) to determine which bean type to instantiate. -
Bean Construction – The code calls the static
deserializemethod on the appropriate bean class (e.g.,VMessBean.deserialize()) to populate fields from the base64 payload. -
Subscription Persistence – The populated bean wraps inside a SubscriptionBean and saves via
subscriptionDeserializeinKryoConverters.java.
The NG format fails at step 2 because v2ray:// is not in the dispatch table, halting the pipeline before deserialization occurs.
Implementing the Fix
Extending the Scheme Dispatch Table
Modify the URL-parsing utility to treat v2ray:// as an alias for vmess:// handling. This requires updating the prefix check to include both schemes before invoking VMessBean.deserialize().
Mapping NG Query Parameters
Most NG parameters map directly to existing StandardV2RayBean fields. The optional v version flag in NG links can be ignored for backward compatibility, as the underlying JSON structure remains compatible with the legacy VMess format.
Updating the Deserialization Path
Ensure StandardV2RayBean.deserialize() in StandardV2RayBean.java receives the NG-style byte array without alteration. Since NG links use identical JSON schemas to VMess, no changes are needed to the actual deserialization logic—only the entry point requires modification.
Code Implementation
The following example shows the required change in the URL parsing logic:
// Before fix (simplified)
if (url.startsWith("vmess://")) {
bean = VMessBean.deserialize(base64Decode(url.substring(8)));
}
// After fix
else if (url.startsWith("vmess://") || url.startsWith("v2ray://")) {
// Treat V2Ray NG links exactly like legacy VMess links
bean = VMessBean.deserialize(base64Decode(
url.startsWith("v2ray://") ? url.substring(8) : url.substring(8)));
}
To persist a newly imported NG subscription:
// Example of persisting a newly-imported NG subscription
String ngLink = "v2ray://eyJhZGQiOiJ1c2VyLmV4YW1wbGUuY29tIiwicG9ydCI6IjQ0MyIsImNvb...";
SubscriptionBean sub = new SubscriptionBean();
sub.name = "NG Subscription";
sub.type = SubscriptionBean.TYPE_V2RAY; // same type as VMess
sub.subscriptionUserinfo = "";
sub.bean = StandardV2RayBean.deserialize(Base64.decode(ngLink.substring(8)));
database.save(sub);
Key Source Files in bannedbook/fanqiang
-
StandardV2RayBean.java (
fqnews2/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java) – Base class containing deserialization logic for all V2Ray-derived proxy beans. -
SubscriptionBean.java (
fqnews2/app/src/main/java/io/nekohasekai/sagernet/database/SubscriptionBean.java) – Wrapper storing proxy beans with subscription metadata. -
KryoConverters.java (
fqnews2/app/src/main/java/io/nekohasekai/sagernet/fmt/KryoConverters.java) – Handles persistence viasubscriptionDeserialize. -
VMessBean.java (
fqnews2/app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/VMessBean.java) – Concrete implementation invoked after URL scheme detection.
Summary
- The Fanqiang client rejects
v2ray://links because the scheme dispatcher only recognizesvmess://prefixes. - Fix: Extend the dispatch logic in the URL-parsing utility to treat
v2ray://as equivalent tovmess://. - StandardV2RayBean.deserialize() processes NG links without modification since the underlying JSON format matches VMess.
- Update the scheme check in the UI-to-bean adapter to restore full V2RayNG subscription import functionality on Android.
Frequently Asked Questions
Why do V2RayNG links fail to import in the Fanqiang Android app?
The original dispatcher logic only checks for vmess://, trojan://, and ss:// prefixes. Since V2RayNG uses v2ray://, the app discards these links before reaching the deserialization logic in StandardV2RayBean.
Which file contains the scheme detection logic I need to modify?
The URL-parsing utility typically resides in the UI component handling subscriptions (e.g., AddSubscriptionActivity) or a dedicated URL adapter class. Look for the startsWith("vmess://") check and extend it to include v2ray:// as shown in the code examples above.
Do I need to change the StandardV2RayBean.deserialize() method?
No. The deserialize method in StandardV2RayBean.java handles the base64 JSON payload identically for both formats. You only need to ensure the method receives the payload by updating the scheme detection that calls it.
Will this fix work for all V2RayNG subscription types?
Yes. Since StandardV2RayBean serves as the base for all V2Ray-derived beans (VMessBean, TrojanBean, etc.), recognizing the v2ray:// prefix allows the entire import pipeline to process NG links correctly, persisting them via KryoConverters.java as standard subscriptions.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →