Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Gỡ cài đặt ứng dụng (APK) lập trình trên Android

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 saiKiểm tra bằng pm list packages hoặc Play Store URL
User hủy hộp thoạionActivityResult 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+).