You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android无服务器依赖本地VPN内容过滤:网络连通故障排查

问题:Android本地VPN启动后网络连通失效,无法路由流量

我正在开发一款Android应用,需创建本地VPN以路由设备所有网络流量,实现内容过滤功能,无需依赖远程VPN服务器。

核心需求

  • 本地VPN搭建:
    • 使用Kotlin在Android设备上创建并管理本地VPN服务
    • 配置VPN的IP地址、路由及DNS服务器
  • 内容过滤:
    • 拦截并过滤经本地VPN路由的HTTP流量
    • 实现过滤规则以拦截或允许特定内容
  • VPN生命周期管理:
    • 根据用户操作(启用/禁用VPN按钮)启动或停止VPN服务

当前实现代码

MainActivity

package com.example.adblocker

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
import android.net.VpnService
import android.os.Bundle
import android.preference.PreferenceManager
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.adblocker.ui.theme.AdblockerTheme

class MainActivity : ComponentActivity() {
    private var websiteList by mutableStateOf(emptyList<String>())
    private lateinit var sharedPreferences: SharedPreferences

    private val websiteReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val newWebsites = intent?.getStringArrayListExtra("websiteList") ?: emptyList()
            websiteList = newWebsites
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
        setContent {
            AdblockerTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    VpnScreen(
                        websiteList = websiteList,
                        onStartVpnClick = { startVpnService() },
                        onStopVpnClick = { stopVpnService() }
                    )
                }
            }
        }
        registerReceiver(websiteReceiver, IntentFilter("com.example.adblocker.UPDATE_WEBSITES"))
    }

    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(websiteReceiver)
    }

    private fun startVpnService() {
        val vpnIntent = VpnService.prepare(this)
        if (vpnIntent != null) {
            startActivityForResult(vpnIntent, 0)
        } else {
            onActivityResult(0, RESULT_OK, null)
        }
    }

    private fun stopVpnService() {
        val intent = Intent(this, MyVpnService::class.java)
        stopService(intent)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 0 && resultCode == RESULT_OK) {
            val intent = Intent(this, MyVpnService::class.java)
            startService(intent)
        }
    }
}

@Composable
fun VpnScreen(
    websiteList: List<String>,
    onStartVpnClick: () -> Unit,
    onStopVpnClick: () -> Unit
) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = "AdBlocker VPN",
            style = MaterialTheme.typography.titleLarge,
            modifier = Modifier.padding(bottom = 20.dp)
        )
        Button(onClick = onStartVpnClick) {
            Text(text = "Enable VPN")
        }
        Spacer(modifier = Modifier.height(20.dp))
        Button(onClick = onStopVpnClick) {
            Text(text = "Disable VPN")
        }
        Spacer(modifier = Modifier.height(20.dp))
        LazyColumn(
            modifier = Modifier
                .fillMaxWidth()
                .weight(1f)
        ) {
            items(websiteList) { website ->
                Text(text = website)
            }
        }
    }
}

@Preview(showBackground = true)
@Composable
fun VpnScreenPreview() {
    AdblockerTheme {
        VpnScreen(websiteList = emptyList(), onStartVpnClick = {}, onStopVpnClick = {})
    }
}

MyVpnService

package com.example.adblocker

import android.net.VpnService
import android.os.ParcelFileDescriptor
import android.util.Log

class MyVpnService : VpnService() {
    private var vpnInterface: ParcelFileDescriptor? = null

    override fun onCreate() {
        super.onCreate()
        startVpn()
    }

    override fun onDestroy() {
        super.onDestroy()
        vpnInterface?.close()
    }

    private fun startVpn() {
        try {
            val builder = Builder()
            builder.setSession("My VPN Service")
            builder.addAddress("10.0.0.2", 24)   // Local VPN IP address
            builder.addRoute("0.0.0.0", 0)       // Route all traffic through the VPN
            builder.addDnsServer("8.8.8.8")      // DNS server for name resolution
            builder.addDnsServer("8.8.4.4")      // Optional additional DNS server

            vpnInterface = builder.establish()
            Log.d("MyVpnService", "VPN started with address 10.0.0.2")
        } catch (e: Exception) {
            e.printStackTrace()
            Log.e("MyVpnService", "Error starting VPN: ${e.message}")
        }
    }
}

AndroidManifest.xml

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.Adblocker">
    <activity android:name=".MainActivity"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <service android:name=".MyVpnService"
        android:permission="android.permission.BIND_VPN_SERVICE"
        android:exported="true">
        <intent-filter>
            <action android:name="android.net.VpnService"/>
        </intent-filter>
    </service>
</application>

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


<uses-feature android:name="android.hardware.faketouch" android:required="false" />

问题现象

启动VPN服务后,设备网络连通性失效,提示“无网络连接”,VPN服务虽启动但无法正确路由流量。

已采取的排查步骤

  • 验证VPN配置(IP地址、路由、DNS)
  • 检查VPN启动相关日志错误
  • 测试极简VPN配置以定位问题
  • 确认VPN激活时设备网络设置正确

提问

如何解决VPN启动后网络连通失效的问题?需哪些特定配置或额外步骤确保VPN正确路由并过滤设备流量?


内容的提问来源于stack exchange,提问作者Raj Tripathi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 06:18:10