如何为GitHub托管的非Google Play APK实现应用内更新(Android Studio Java)
实现GitHub托管APK的应用内更新检测(Java)
1. 前置准备
- 将APK上传至GitHub仓库的Releases板块,Release标签建议使用带
v前缀的版本号(如v1.0.1),并关联对应的APK文件。 - 记下你的GitHub仓库最新版本API地址:格式为
https://api.github.com/repos/你的用户名/你的仓库名/releases/latest
2. 添加权限与依赖
权限配置(AndroidManifest.xml)
<uses-permission android:name="android.permission.INTERNET" /> <!-- Android 8.0+需要申请安装未知来源应用权限 --> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <!-- Android 12及以下需要存储权限,13+可省略 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
依赖添加(Module级build.gradle)
dependencies { // 网络请求库 implementation 'com.squareup.okhttp3:okhttp:4.12.0' // JSON解析库 implementation 'com.google.code.gson:gson:2.10.1' }
3. 版本检查工具类
创建UpdateChecker.java,负责请求GitHub最新版本并与本地版本对比:
import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.util.Log; import com.google.gson.Gson; import okhttp3.Call; import okhttp3.Callback; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import java.io.IOException; public class UpdateChecker { private static final String TAG = "UpdateChecker"; // 替换为你的GitHub仓库最新版本API地址 private static final String GITHUB_LATEST_RELEASE_URL = "https://api.github.com/repos/你的用户名/你的仓库名/releases/latest"; public interface UpdateListener { void onUpdateAvailable(String latestVersion, String apkDownloadUrl); void onUpdateNotAvailable(); void onCheckFailed(String error); } public static void checkForUpdates(Context context, UpdateListener listener) { String currentVersion = getCurrentAppVersion(context); if (currentVersion == null) { listener.onCheckFailed("无法获取当前应用版本"); return; } OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url(GITHUB_LATEST_RELEASE_URL) .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e(TAG, "更新检查失败: " + e.getMessage()); listener.onCheckFailed(e.getMessage()); } @Override public void onResponse(Call call, Response response) throws IOException { if (!response.isSuccessful()) { listener.onCheckFailed("请求失败,状态码: " + response.code()); return; } String responseBody = response.body().string(); GitHubRelease release = new Gson().fromJson(responseBody, GitHubRelease.class); if (release == null || release.tag_name == null) { listener.onCheckFailed版本信息解析失败"); return; } // 去除版本标签的v前缀(如v1.0.1 → 1.0.1) String latestVersion = release.tag_name.replaceFirst("^v", ""); Log.d(TAG, "当前版本: " + currentVersion + " | 最新版本: " + latestVersion); if (isVersionOlder(currentVersion, latestVersion)) { String apkUrl = getApkDownloadUrl(release); if (apkUrl != null) { listener.onUpdateAvailable(latestVersion, apkUrl); } else { listener.onCheckFailed("未找到APK下载链接"); } } else { listener.onUpdateNotAvailable(); } } }); } // 获取当前应用版本名称 private static String getCurrentAppVersion(Context context) { try { PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); return packageInfo.versionName; } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "获取当前版本失败: " + e.getMessage()); return null; } } // 对比版本号,判断当前版本是否落后 private static boolean isVersionOlder(String currentVersion, String latestVersion) { String[] currentParts = currentVersion.split("\\."); String[] latestParts = latestVersion.split("\\."); int maxLength = Math.max(currentParts.length, latestParts.length); for (int i = 0; i < maxLength; i++) { int currentPart = i < currentParts.length ? Integer.parseInt(currentParts[i]) : 0; int latestPart = i < latestParts.length ? Integer.parseInt(latestParts[i]) : 0; if (currentPart < latestPart) return true; if (currentPart > latestPart) return false; } return false; // 版本相同 } // 从Release信息中提取APK下载链接 private static String getApkDownloadUrl(GitHubRelease release) { if (release.assets == null || release.assets.length == 0) return null; for (GitHubAsset asset : release.assets) { if (asset.name != null && asset.name.endsWith(".apk")) { return asset.browser_download_url; } } return null; } // Gson解析实体类 static class GitHubRelease { String tag_name; GitHubAsset[] assets; } static class GitHubAsset { String name; String browser_download_url; } }
4. 在启动页调用更新检查
在MainActivity或启动页中触发检查,并处理更新提示:
import android.app.AlertDialog; import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.Settings; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 应用启动时检查更新 checkForUpdates(); } private void checkForUpdates() { UpdateChecker.checkForUpdates(this, new UpdateChecker.UpdateListener() { @Override public void onUpdateAvailable(String latestVersion, String apkDownloadUrl) { runOnUiThread(() -> showUpdateDialog(latestVersion, apkDownloadUrl)); } @Override public void onUpdateNotAvailable() { runOnUiThread(() -> Toast.makeText(MainActivity.this, "当前已是最新版本", Toast.LENGTH_SHORT).show()); } @Override public void onCheckFailed(String error) { runOnUiThread(() -> Toast.makeText(MainActivity.this, "更新检查失败: " + error, Toast.LENGTH_SHORT).show()); } }); } private void showUpdateDialog(String latestVersion, String apkDownloadUrl) { new AlertDialog.Builder(this) .setTitle("发现新版本 v" + latestVersion) .setMessage("更新后可获得更多功能与稳定性优化") .setPositiveButton("立即更新", (dialog, which) -> { // 检查Android 8.0+的未知来源安装权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (!getPackageManager().canRequestPackageInstalls()) { Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:" + getPackageName())); startActivityForResult(intent, 1001); return; } } // 调用浏览器下载APK Intent downloadIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(apkDownloadUrl)); startActivity(downloadIntent); }) .setNegativeButton("稍后再说", null) .show(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 1001) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (getPackageManager().canRequestPackageInstalls()) { Toast.makeText(this, "已允许安装,可重新触发更新", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "未授予安装权限,无法更新", Toast.LENGTH_SHORT).show(); } } } } }
5. 注意事项
- 版本号需统一使用
x.y.z格式,确保版本对比逻辑正常工作。 - GitHub API有请求限制:公开仓库未认证请求每小时最多60次,足够普通应用使用;私有仓库需在请求头添加
Authorization: token 你的GitHub令牌。 - 若需更优体验,可自行实现后台下载器(使用
DownloadManager或OkHttp断点续传),替代浏览器下载。
内容的提问来源于stack exchange,提问作者King Grey
相关产品推荐
相关产品推荐

