Tôi muốn biết liệu có thể cài đặt apk được tải xuống một cách linh hoạt từ một ứng dụng Android tùy chỉnh hay không.
Tôi muốn biết liệu có thể cài đặt apk được tải xuống một cách linh hoạt từ một ứng dụng Android tùy chỉnh hay không.
Câu trả lời:
Bạn có thể dễ dàng khởi chạy một liên kết cửa hàng chơi hoặc lời nhắc cài đặt:
Intent promptInstall = new Intent(Intent.ACTION_VIEW)
.setDataAndType(Uri.parse("content:///path/to/your.apk"),
"application/vnd.android.package-archive");
startActivity(promptInstall);
hoặc là
Intent goToMarket = new Intent(Intent.ACTION_VIEW)
.setData(Uri.parse("https://play.google.com/store/apps/details?id=com.package.name"));
startActivity(goToMarket);
Tuy nhiên, bạn không thể cài đặt .apks mà không có sự cho phép rõ ràng của người dùng ; trừ khi thiết bị và chương trình của bạn được root.
/sdcard
, vì đó là sai trên Android 2.2+ và các thiết bị khác. Sử dụng Environment.getExternalStorageDirectory()
thay thế.
File file = new File(dir, "App.apk");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
startActivity(intent);
Tôi đã có cùng một vấn đề và sau nhiều lần thử, nó đã giải quyết được cho tôi theo cách này. Tôi không biết tại sao, nhưng cài đặt dữ liệu và gõ riêng rẽ đã làm hỏng ý định của tôi.
setData()
sẽ khiến tham số loại bị xóa. Bạn PHẢI sử dụng setDataAndType()
nếu bạn muốn đưa ra giá trị cho cả hai. Tại đây: developer.android.com/reference/android/content/
Các giải pháp được cung cấp cho câu hỏi này đều áp dụng cho targetSdkVersion
s từ 23 trở xuống. Tuy nhiên, đối với Android N, tức là API cấp 24 trở lên, chúng không hoạt động và gặp sự cố với Ngoại lệ sau:
android.os.FileUriExposedException: file:///storage/emulated/0/... exposed beyond app through Intent.getData()
Điều này là do thực tế là bắt đầu từ Android 24, việc Uri
giải quyết các tệp đã tải xuống đã thay đổi. Chẳng hạn, một tệp cài đặt có tên appName.apk
được lưu trữ trên hệ thống tệp bên ngoài chính của ứng dụng có tên gói com.example.test
sẽ là
file:///storage/emulated/0/Android/data/com.example.test/files/appName.apk
cho API 23
và bên dưới, trong khi một cái gì đó như
content://com.example.test.authorityStr/pathName/Android/data/com.example.test/files/appName.apk
cho API 24
và trên.
Thông tin chi tiết về điều này có thể được tìm thấy ở đây và tôi sẽ không đi qua nó.
Để trả lời các câu hỏi cho targetSdkVersion
các 24
trở lên, người ta phải làm theo các bước sau: Thêm sau vào AndroidManifest.xml:
<application
android:allowBackup="true"
android:label="@string/app_name">
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.authorityStr"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/paths"/>
</provider>
</application>
2. Thêm paths.xml
tệp sau vào xml
thư mục trên res
trong src, main:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="pathName"
path="pathValue"/>
</paths>
Cái pathName
được thể hiện trong ví dụ uri nội dung mẫu mực ở trên và pathValue
là đường dẫn thực tế trên hệ thống. Nó sẽ là một ý tưởng tốt để đặt một "." (không có dấu ngoặc kép) cho pathValue ở trên nếu bạn không muốn thêm bất kỳ thư mục con phụ nào.
Viết đoạn mã sau để cài đặt apk với tên appName.apk
trên hệ thống tệp bên ngoài chính:
File directory = context.getExternalFilesDir(null);
File file = new File(directory, fileName);
Uri fileUri = Uri.fromFile(file);
if (Build.VERSION.SDK_INT >= 24) {
fileUri = FileProvider.getUriForFile(context, context.getPackageName(),
file);
}
Intent intent = new Intent(Intent.ACTION_VIEW, fileUri);
intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
intent.setDataAndType(fileUri, "application/vnd.android" + ".package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(intent);
activity.finish();
Không có quyền cũng cần thiết khi ghi vào thư mục riêng của ứng dụng của bạn trên hệ thống tệp bên ngoài.
Tôi đã viết một thư viện AutoUpdate ở đây mà tôi đã sử dụng ở trên.
.authorityStr
sau context.getPackageName()
đó thì nó sẽ hoạt động.
Chà, tôi đã đào sâu hơn và tìm thấy các nguồn ứng dụng PackageInstaller từ Nguồn Android.
https://github.com/android/pl platform_packages_apps_packageinstaller
Từ bảng kê khai tôi thấy rằng nó cần có sự cho phép:
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
Và quá trình cài đặt thực tế xảy ra sau khi xác nhận
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallAppProgress.class);
String installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
if (installerPackageName != null) {
newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName);
}
startActivity(newIntent);
Tôi chỉ muốn chia sẻ sự thật rằng tệp apk của tôi đã được lưu vào thư mục "Dữ liệu" của ứng dụng và tôi cần thay đổi các quyền trên tệp apk để có thể đọc được trên thế giới để cho phép cài đặt theo cách đó, nếu không thì hệ thống đã ném "Lỗi phân tích cú pháp: Có một vấn đề phân tích gói"; Vì vậy, sử dụng giải pháp từ @Horaceman mà thực hiện:
File file = new File(dir, "App.apk");
file.setReadable(true, false);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
startActivity(intent);
Điều này có thể giúp đỡ người khác rất nhiều!
Đầu tiên:
private static final String APP_DIR = Environment.getExternalStorageDirectory().getAbsolutePath() + "/MyAppFolderInStorage/";
private void install() {
File file = new File(APP_DIR + fileName);
if (file.exists()) {
Intent intent = new Intent(Intent.ACTION_VIEW);
String type = "application/vnd.android.package-archive";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri downloadedApk = FileProvider.getUriForFile(getContext(), "ir.greencode", file);
intent.setDataAndType(downloadedApk, type);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
intent.setDataAndType(Uri.fromFile(file), type);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
getContext().startActivity(intent);
} else {
Toast.makeText(getContext(), "ّFile not found!", Toast.LENGTH_SHORT).show();
}
}
Thứ hai: Đối với Android 7 trở lên, bạn nên xác định một nhà cung cấp trong bảng kê khai như bên dưới!
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="ir.greencode"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/paths" />
</provider>
Thứ ba: Xác định path.xml trong thư mục res / xml như bên dưới! Tôi đang sử dụng đường dẫn này để lưu trữ nội bộ nếu bạn muốn thay đổi nó thành một thứ khác, có một vài cách! Bạn có thể vào liên kết này: FileProvider
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="your_folder_name" path="MyAppFolderInStorage/"/>
</paths>
Forth: Bạn nên thêm quyền này trong tệp kê khai:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
Cho phép một ứng dụng yêu cầu cài đặt các gói. Các API nhắm mục tiêu ứng dụng lớn hơn 25 phải có quyền này để sử dụng Intent.ACTION_INSTALL_PACKAGE.
Hãy chắc chắn rằng các cơ quan cung cấp là như nhau!
Vâng nó có thể. Nhưng để làm được điều đó, bạn cần điện thoại để cài đặt các nguồn chưa được xác minh. Ví dụ, slideMe làm điều đó. Tôi nghĩ rằng điều tốt nhất bạn có thể làm là kiểm tra xem ứng dụng có mặt hay không và gửi ý định cho Android Market. bạn nên sử dụng một cái gì đó lược đồ url cho Android Market.
market://details?id=package.name
Tôi không biết chính xác làm thế nào để bắt đầu hoạt động nhưng nếu bạn bắt đầu một hoạt động với loại url đó. Nó sẽ mở ra thị trường Android và cung cấp cho bạn sự lựa chọn để cài đặt các ứng dụng.
Điều đáng chú ý là nếu bạn sử dụng DownloadManager
để khởi động quá trình tải xuống của mình, hãy nhớ lưu nó vào một vị trí bên ngoài, vd setDestinationInExternalFilesDir(c, null, "<your name here>).apk";
. Mục đích với loại lưu trữ gói dường như không thích content:
lược đồ được sử dụng với các tải xuống đến một vị trí nội bộ, nhưng thực sự thích file:
. (Cố gắng bọc đường dẫn nội bộ vào một đối tượng Tệp và sau đó nhận đường dẫn cũng không hoạt động, mặc dù nó dẫn đến một file:
url, vì ứng dụng sẽ không phân tích apk; có vẻ như nó phải ở bên ngoài.)
Thí dụ:
int uriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);
String downloadedPackageUriString = cursor.getString(uriIndex);
File mFile = new File(Uri.parse(downloadedPackageUriString).getPath());
Intent promptInstall = new Intent(Intent.ACTION_VIEW)
.setDataAndType(Uri.fromFile(mFile), "application/vnd.android.package-archive")
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
appContext.startActivity(promptInstall);
Đừng quên yêu cầu quyền:
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
android.Manifest.permission.READ_EXTERNAL_STORAGE
Thêm vào AndroidManifest.xml nhà cung cấp và quyền:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
...
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application>
Tạo nhà cung cấp tệp XML res / xml / Carrier_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="external"
path="." />
<external-files-path
name="external_files"
path="." />
<cache-path
name="cache"
path="." />
<external-cache-path
name="external_cache"
path="." />
<files-path
name="files"
path="." />
</paths>
Sử dụng mã ví dụ dưới đây:
public class InstallManagerApk extends AppCompatActivity {
static final String NAME_APK_FILE = "some.apk";
public static final int REQUEST_INSTALL = 0;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// required permission:
// android.Manifest.permission.WRITE_EXTERNAL_STORAGE
// android.Manifest.permission.READ_EXTERNAL_STORAGE
installApk();
}
...
/**
* Install APK File
*/
private void installApk() {
try {
File filePath = Environment.getExternalStorageDirectory();// path to file apk
File file = new File(filePath, LoadManagerApkFile.NAME_APK_FILE);
Uri uri = getApkUri( file.getPath() ); // get Uri for each SDK Android
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData( uri );
intent.setFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_NEW_TASK );
intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, getApplicationInfo().packageName);
if ( getPackageManager().queryIntentActivities(intent, 0 ) != null ) {// checked on start Activity
startActivityForResult(intent, REQUEST_INSTALL);
} else {
throw new Exception("don`t start Activity.");
}
} catch ( Exception e ) {
Log.i(TAG + ":InstallApk", "Failed installl APK file", e);
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG)
.show();
}
}
/**
* Returns a Uri pointing to the APK to install.
*/
private Uri getApkUri(String path) {
// Before N, a MODE_WORLD_READABLE file could be passed via the ACTION_INSTALL_PACKAGE
// Intent. Since N, MODE_WORLD_READABLE files are forbidden, and a FileProvider is
// recommended.
boolean useFileProvider = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
String tempFilename = "tmp.apk";
byte[] buffer = new byte[16384];
int fileMode = useFileProvider ? Context.MODE_PRIVATE : Context.MODE_WORLD_READABLE;
try (InputStream is = new FileInputStream(new File(path));
FileOutputStream fout = openFileOutput(tempFilename, fileMode)) {
int n;
while ((n = is.read(buffer)) >= 0) {
fout.write(buffer, 0, n);
}
} catch (IOException e) {
Log.i(TAG + ":getApkUri", "Failed to write temporary APK file", e);
}
if (useFileProvider) {
File toInstall = new File(this.getFilesDir(), tempFilename);
return FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID, toInstall);
} else {
return Uri.fromFile(getFileStreamPath(tempFilename));
}
}
/**
* Listener event on installation APK file
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == REQUEST_INSTALL) {
if (resultCode == Activity.RESULT_OK) {
Toast.makeText(this,"Install succeeded!", Toast.LENGTH_SHORT).show();
} else if (resultCode == Activity.RESULT_CANCELED) {
Toast.makeText(this,"Install canceled!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this,"Install Failed!", Toast.LENGTH_SHORT).show();
}
}
}
...
}
thử cái này
String filePath = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
String title = filePath.substring( filePath.lastIndexOf('/')+1, filePath.length() );
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(filePath)), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // without this flag android returned a intent error!
MainActivity.this.startActivity(intent);
Trước tiên, thêm dòng sau vào AndroidManifest.xml:
<uses-permission android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions" />
Sau đó sử dụng đoạn mã sau để cài đặt apk:
File sdCard = Environment.getExternalStorageDirectory();
String fileStr = sdCard.getAbsolutePath() + "/MyApp";// + "app-release.apk";
File file = new File(fileStr, "TaghvimShamsi.apk");
Intent promptInstall = new Intent(Intent.ACTION_VIEW).setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
startActivity(promptInstall);
UpdateNode cung cấp API cho Android để cài đặt các gói APK từ bên trong Ứng dụng khác.
Bạn chỉ có thể xác định Cập nhật trực tuyến và tích hợp API vào Ứng dụng của mình - đó là nó.
Hiện tại API đang ở trạng thái Beta, nhưng bạn đã có thể tự mình thực hiện một số thử nghiệm.
Bên cạnh đó, UpdateNode cung cấp cũng hiển thị các thông báo mặc dù hệ thống - khá hữu ích nếu bạn muốn nói điều gì đó quan trọng với người dùng của mình.
Tôi là một phần của nhóm nhà phát triển khách hàng và đang sử dụng ít nhất chức năng tin nhắn cho Ứng dụng Android của riêng tôi.
Hãy thử điều này - Viết trên Bản kê khai:
uses-permission android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions"
Viết mã:
File sdCard = Environment.getExternalStorageDirectory();
String fileStr = sdCard.getAbsolutePath() + "/Download";// + "app-release.apk";
File file = new File(fileStr, "app-release.apk");
Intent promptInstall = new Intent(Intent.ACTION_VIEW).setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
startActivity(promptInstall);