1.首先将apk文件放置在工程中的asset文件夹中,选择Project工程结构显示,在app/src/main上右键new->Folder->Assets Folder,在弹出的窗口中不点勾,直接Finish.
2.右键点击Assets,选择Show in Explorer,打开文件夹,把apk文件放在该文件夹中。
3.新建一个类,名为ServiceApkInstaller。内容如下,其中apkPackName需要自行确定:
public class ServiceApkInstaller { public final static String TAG = ServiceApkInstaller.class.getSimpleName(); private final String apkPackName = "cn.wps.moffice_eng"; private String apkName; private String newApkPath = Environment.getExternalStorageDirectory() + File.separator + "apktemp" + File.separator + "tmp.apk"; private Context mContext; private Thread subThread; public ServiceApkInstaller() { } public ServiceApkInstaller(Context context, String name) { mContext = context; apkName = name; } class installTask implements Runnable { @Override public void run() { if (!hasInstalled()) { // setInstallPermission(); // Intent intent = new Intent(Intent.ACTION_VIEW); // intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // intent.setDataAndType(Uri.parse("file://" + newApkPath), // "application/vnd.android.package-archive"); // mContext.startActivity(intent); AssetManager assets = mContext.getAssets(); try { //获取assets资源目录下的himarket.mp3,实际上是himarket.apk,为了避免被编译压缩,修改后缀名。 InputStream stream = assets.open(apkName); if(stream==null){ Log.v(TAG,"no file"); return; } String folder = Environment.getExternalStorageDirectory() + File.separator + "apktemp" + File.separator; File f=new File(folder); if(!f.exists()){ f.mkdir(); } String apkPath = Environment.getExternalStorageDirectory() + File.separator + "apktemp" + File.separator + "tmp.apk"; File file = new File(apkPath); //创建apk文件 file.createNewFile(); //将资源中的文件重写到sdcard中 //<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> writeStreamToFile(stream, file); //安装apk //<uses-permission android:name="android.permission.INSTALL_PACKAGES" /> installApk(apkPath); } catch (IOException e) { e.printStackTrace(); } } } } private void writeStreamToFile(InputStream stream, File file) { try { // OutputStream output = null; try { output = new FileOutputStream(file); } catch (FileNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } try { try { final byte[] buffer = new byte[1024]; int read; while ((read = stream.read(buffer)) != -1) output.write(buffer, 0, read); output.flush(); } finally { output.close(); } } catch (Exception e) { e.printStackTrace(); } } finally { try { stream.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } private void installApk(String apkPath) { Intent intent = new Intent(Intent.ACTION_VIEW); File apkFile = new File(apkPath); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri uri = FileProvider.getUriForFile(mContext, mContext.getPackageName() + ".fileprovider", apkFile); intent.setDataAndType(uri, "application/vnd.android.package-archive"); } else { intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive"); } mContext.startActivity(intent); } private void installApk(){ Intent intent = new Intent(Intent.ACTION_VIEW); File apkFile = new File(newApkPath); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri uri = FileProvider.getUriForFile(mContext, mContext.getPackageName() + ".fileprovider", apkFile); intent.setDataAndType(uri, "application/vnd.android.package-archive"); } else { intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive"); } mContext.startActivity(intent); } public ServiceApkInstaller install() { if (subThread != null && subThread.isAlive()) { return this; } subThread = new Thread(new installTask()); subThread.start(); return this; } public void uninstall() { Uri packageURI = Uri.parse("package:" + apkPackName); Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI); mContext.startActivity(uninstallIntent); } public boolean hasInstalled() { final PackageManager packageManager = mContext.getPackageManager(); List<PackageInfo> installedPackInfoList = packageManager.getInstalledPackages(0); for (int i = 0; installedPackInfoList != null && i < installedPackInfoList.size(); i++) { PackageInfo installedPackInfo = installedPackInfoList.get(i); if (installedPackInfo != null && TextUtils.equals(apkPackName, installedPackInfo.packageName)) { copyApkFromAssets(mContext, apkName, newApkPath); PackageInfo assetPackInfo = packageManager.getPackageArchiveInfo(newApkPath, PackageManager.GET_ACTIVITIES); if (assetPackInfo != null) { ApplicationInfo appInfo = assetPackInfo.applicationInfo; String assetVersionName = assetPackInfo.versionName; int assetVersionCode = assetPackInfo.versionCode; if (!TextUtils.equals(assetVersionName, installedPackInfo.versionName) || installedPackInfo.versionCode < assetVersionCode) { return false; } else { return true; } } return true; } } return false; } public boolean copyApkFromAssets(Context context, String fileName, String path) { boolean copyIsFinish = false; try { InputStream is = context.getAssets().open(fileName); File file = new File(path); file.createNewFile(); FileOutputStream fos = new FileOutputStream(file); byte[] temp = new byte[1024]; int i = 0; while ((i = is.read(temp)) > 0) { fos.write(temp, 0, i); } fos.close(); is.close(); copyIsFinish = true; } catch (IOException e) { e.printStackTrace(); } return copyIsFinish; } }4.在AndroidManifest.xml中,增加权限
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />5.在res中,增加xml文件夹
在xml中增加filepaths.xml文件,内容如下
<paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="apktmp" path="."/> </paths>
再回到androidManifest.xml中,在最下方,增加如下代码
<provider android:name="androidx.core.content.FileProvider" android:authorities="自已的包名.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" /> </provider>
6.在程序入口活动中,在onCreate()方法中增加如下代码
mContext = this; warndialog = new SweetAlertDialog(mContext,SweetAlertDialog.NORMAL_TYPE); warndialog.setTitleText("需要安装文件读取软件,请去设置中开启此权限"); wpsInstaller = new ServiceApkInstaller(this,"wps.apk"); if(!wpsInstaller.hasInstalled()){ setInstallPermission(); }在Activity中,在onCreate()上方,增加全局变量
private SweetAlertDialog warndialog; private ServiceApkInstaller wpsInstaller; private Context mContext;6.设置权限申请代码与结果返回
public void setInstallPermission(){ boolean haveInstallPermission; if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ //先判断是否有安装未知来源应用的权限 haveInstallPermission = mContext.getPackageManager().canRequestPackageInstalls(); if(!haveInstallPermission){ //弹框提示用户手动打开 warndialog.setConfirmClickListener(new SweetAlertDialog.OnSweetClickListener() { @Override public void onClick(SweetAlertDialog sweetAlertDialog) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //此方法需要API>=26才能使用 toInstallPermissionSettingIntent(); } } }); warndialog.show(); // toInstallPermissionSettingIntent(); }else{ warndialog.dismiss(); wpsInstaller.install(); } } } /** * 开启安装未知来源权限 */ @RequiresApi(api = Build.VERSION_CODES.O) private void toInstallPermissionSettingIntent() { Uri packageURI = Uri.parse("package:"+this.getPackageName()); Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,packageURI); startActivityForResult(intent, 10086); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK && requestCode == 10086) { warndialog.dismiss(); wpsInstaller.install(); } }7.这里使用了SweetAlertDialog,可在app的gradle中增加如下代码
添加自定义对话框依赖 implementation 'com.github.f0ris.sweetalert:library:1.5.1'