Android cho phép khởi động quy trình gỡ cài đặt ứng dụng khác thông qua Intent.ACTION_DELETE (hoặc Intent.ACTION_UNINSTALL_PACKAGE từ API 14). Hệ thống sẽ hiển thị hộp thoại xác nhận cho người dùng, ứng dụng không thể gỡ cài đặt thầm lặng trừ khi có quyền hệ thống/root.
Yêu cầu Manifest
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" /> <!-- Từ Android 11 (API 30) cần quyền này để startActivity ACTION_DELETE -->
Bước 1: Layout đơn giản (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="24dp">
<TextView
android:id="@+id/tvInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nhấn nút dưới để gỡ cài đặt ứng dụng demo"
android:textSize="16sp"
android:gravity="center"
android:layout_marginBottom="24dp" />
<Button
android:id="@+id/btnUninstall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Gỡ cài đặt com.example.demo" />
</LinearLayout>
Bước 2: MainActivity.java / Kotlin
package com.example.uninstaller;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String TARGET_PACKAGE = "com.example.demo"; // Package cần gỡ
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tvInfo = findViewById(R.id.tvInfo);
Button btnUninstall = findViewById(R.id.btnUninstall);
// Kiểm tra app đích có tồn tại không
boolean installed = isPackageInstalled(TARGET_PACKAGE);
tvInfo.setText(installed
? "Ứng dụng '" + TARGET_PACKAGE + "' đang cài đặt. Nhấn để gỡ."
: "Ứng dụng '" + TARGET_PACKAGE + "' chưa cài đặt.");
btnUninstall.setEnabled(installed);
btnUninstall.setOnClickListener(v -> uninstallApp(TARGET_PACKAGE));
}
private boolean isPackageInstalled(String packageName) {
try {
getPackageManager().getPackageInfo(packageName, 0);
return true;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
private void uninstallApp(String packageName) {
// Intent.ACTION_DELETE (API 1+) hoặc ACTION_UNINSTALL_PACKAGE (API 14+)
Uri packageUri = Uri.parse("package:" + packageName);
Intent intent = new Intent(Intent.ACTION_DELETE, packageUri);
// Android 11+ cần FLAG_ACTIVITY_NEW_TASK nếu không start từ Activity context
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
// Kiểm tra có activity xử lý intent không
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
Toast.makeText(this, "Không thể mở trình gỡ cài đặt", Toast.LENGTH_SHORT).show();
}
}
}
Kotlin Version (Ngắn gọn)
class MainActivity : AppCompatActivity() {
private val targetPackage = "com.example.demo"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val installed = packageManager.getPackageInfo(targetPackage, 0).let { true } ?: false
tvInfo.text = if (installed) "Đã cài $targetPackage" else "Chưa cài $targetPackage"
btnUninstall.isEnabled = installed
btnUninstall.setOnClickListener { uninstallApp(targetPackage) }
}
private fun uninstallApp(packageName: String) {
val uri = Uri.parse("package:$packageName")
val intent = Intent(Intent.ACTION_DELETE, uri).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
if (intent.resolveActivity(packageManager) != null) startActivity(intent)
else Toast.makeText(this, "Lỗi mở uninstaller", Toast.LENGTH_SHORT).show()
}
}
Lưu ý quan trọng
| Vấn đề | Giải pháp |
|---|---|
| App không gỡ được (Device Admin) | User phải vô hiệu hóa Device Admin trước trong Settings |
| App hệ thống (system app) | Không gỡ được bằng Intent thường, cần root hoặc ADB |
| Android 11+ (API 30) | Cần REQUEST_DELETE_PACKAGES permission trong Manifest |
| Package name sai | Kiểm tra bằng pm list packages hoặc Play Store URL |
| User hủy hộp thoại | onActivityResult không nhận callback, chỉ kiểm tra lại isPackageInstalled() khi resume |
Kiểm tra kết quả gỡ cài đặt
Override onResume() để kiểm tra lại:
@Override
protected void onResume() {
super.onResume();
boolean stillInstalled = isPackageInstalled(TARGET_PACKAGE);
btnUninstall.setEnabled(stillInstalled);
tvInfo.setText(stillInstalled ? "Vẫn cài đặt" : "Đã gỡ thành công");
}
Gỡ cài đặt nhiều app cùng lúc
Android không hỗ trợ gỡ hàng loạt qua Intent đơn lẻ. Cách làm: lặp qua danh sách package, startActivity lần lượt, hoặc dùng PackageInstaller.Session (API 21+) cho app có quyền hệ thống.
Kết luận
Sử dụng Intent.ACTION_DELETE với URI package:ten.goi là cách chuẩn, an toàn, tuân thủ bảo mật Android. App chỉ khởi tạo quy trình, quyết định cuối cùng thuộc về người dùng. Luôn kiểm tra package tồn tại và quyền REQUEST_DELETE_PACKAGES (API 30+).