网上有很多种写法,也有很多坑,当然这些坑不是说代码有问题,而是Android的碎片化和各个厂商的定制造成的,
例如最简单的写法DownloadManager在三星等手机上无法使用,原因是他们不支持DownloadManager。
使用Thread或者IntentService或者AnsyTask其实也都可以,各有优劣。
例如使用IntentService的更新UI问题,使用AnsyTask的排队问题,等等还是要看具体的需求选择最合适的实现方法。
另一个问题是Android版本的问题,主要是7.0的兼容问题,不然以前的写法是要奔溃的;
在Manfist清单文件中声明一个内容提供器,当然是在Application节点内部;
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
provider>
这里的applicationId占位其实是buildgradle文件中applicationId,也就是项目的包名,可以直接用项目包名代替。
而这个文件@xml/provider_paths是不存在的,是需要在res下新建的;
接下来给出一个provider_paths.xml的范例:
<paths>
<external-path path="Android/data/自己的项目包名/" name="files_root" />
<external-path path="." name="external_storage_root" />
paths>
最后是android调用Apk安装:
public void installApkFile( String filePath) {
//Log.e("JACK",filePath);
Intent intent = new Intent(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(mContext, "com.caihongto.caihongtoforcustomer.fileprovider", new File(filePath));
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(new File(filePath)), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
mContext.startActivity(intent);
}
这里的命名必须和清单文件的命名一致;
下面是下载与安装代码:
public class DownloadAsyncTask extends AsyncTask {
private Context mContext;
int per = 0;
private ProgressDialog perDialog = null;
private String fullPath = null;
//private File apkFile;
public DownloadAsyncTask(Context context){
mContext=context;
}
@Override
protected Boolean doInBackground(String... params) {
fullPath = params[1] + params[2];
try {
URL url = new URL(params[0]);
HttpURLConnection huc = (HttpURLConnection) url
.openConnection();
huc.setConnectTimeout(10 * 1000);
huc.connect();
if (huc.getResponseCode() == 200) {
perDialog.setMax(huc.getContentLength());
File path = new File(params[1]);
if (!path.exists()) {
path.mkdirs();
}
File apkFile = new File(path, params[2]);
if (!apkFile.exists()) {
apkFile.createNewFile();
}
InputStream is = huc.getInputStream();
FileOutputStream fos = new FileOutputStream(apkFile);
byte[] buf = new byte[1024];
int readSize;
while (true) {
readSize = is.read(buf);
if (readSize <= 0) {
break;
}
per += readSize;
this.publishProgress(per);
fos.write(buf, 0, readSize);
}
fos.close();
is.close();
return true;
} else {
return false;
}
} catch (MalformedURLException e) {
return false;
} catch (IOException e) {
return false;
}
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
perDialog.dismiss();
if (result) {
Toast.makeText(mContext, "下载完成", Toast.LENGTH_SHORT)
.show();
installApkFile(fullPath);
} else {
Toast.makeText(mContext, "下载失败", Toast.LENGTH_SHORT)
.show();
}
}
@Override
protected void onPreExecute() {
super.onPreExecute();
perDialog = new ProgressDialog(mContext);
perDialog.setMessage("正在下载...");
perDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);// 设置水平进度条
perDialog.setCancelable(true);// 设置是否可以通过点击Back键取消
perDialog.setCanceledOnTouchOutside(false);// 设置在点击Dialog外是否取消Dialog进度条
perDialog.show();
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
perDialog.setProgress(values[0]);
}
public void installApkFile( String filePath) {
Intent intent = new Intent(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(mContext,
"com.caihongto.caihongtoforcustomer.fileprovider",
new File(filePath));
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(new File(filePath)), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
mContext.startActivity(intent);
}
}
最后看下怎么调用的吧;
String[] params = new String[] { "http://www.caihongto.com/public/androidapk/6cc043563be7528213bbfa585245a5f9.apk",Environment.getExternalStorageDirectory() + "/pistol/app/", "caihongto_.apk" };
new DownloadAsyncTask(MainActivity.this).execute(params);
就这样吧,如果有什么同类的问题,大家可以留言;
另外附上一种大神的写法:
首先定义一个将Apk的存储File 转换为Uri的方法,代码如下:
public static Uri getFileExternalContentUri(Context context, File externalFile) {
String filePath = externalFile.getAbsolutePath();
Cursor cursor = context.getContentResolver().query(MediaStore.Files.getContentUri("external"),
new String[]{MediaStore.Files.FileColumns._ID},
MediaStore.Files.FileColumns.DATA + "=? ",
new String[]{filePath}, null);
if (cursor != null && cursor.moveToFirst()) {
int id = cursor.getInt(cursor.getColumnIndex(MediaStore.Files.FileColumns._ID));
cursor.close();
return Uri.withAppendedPath(MediaStore.Files.getContentUri("external"), "" + id);
} else {
if (externalFile.exists()) {
ContentValues values = new ContentValues();
values.put(MediaStore.Files.FileColumns.DATA, filePath);
return context.getContentResolver().insert(MediaStore.Files.getContentUri("external"), values);
} else {
return null;
}
}
}
然后在完成下载之后调用这个方法,并实现安装逻辑:
public static boolean installApp(File apkFile, final Context context) {
LogUtil.i(TAG, "开始安装apk 文件包:");
if (apkFile != null && apkFile.exists() && apkFile.isFile()) {
if (context != null) {
// 通过Intent安装APK文件
final Intent instalInstant = new Intent(Intent.ACTION_VIEW);
instalInstant.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
instalInstant.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
instalInstant.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
Uri apkFileURI = ResUtil.getFileExternalContentUri(context,apkFile);
instalInstant.setDataAndType(apkFileURI, "application/vnd.android.package-archive");
if (context != null) {
context.startActivity(instalInstant);
}
} else {
instalInstant.setDataAndType(Uri.parse("file://" + apkFile.getAbsolutePath()), "application/vnd.android.package-archive");
}
return true;
} else {
LogUtil.e(TAG, "installApp() --> context is null !!! ");
}
} else {
LogUtil.e(TAG, "installApp() --> apkFile exists = false !!! ");
}
return false;
}
先这样吧