一肆汹、簡介
二愚墓、效果預(yù)覽
?三、實(shí)現(xiàn)步驟
四昂勉、功能解析
五浪册、Demo地址
六、內(nèi)容推薦
一岗照、簡介
嘿嘿村象,這周沒缺席,繼續(xù)給大伙們提供一個(gè)工具類攒至。用于下載APK而线,安裝應(yīng)用旬渠。幾乎每個(gè)APP都帶有這個(gè)功能拨匆,通過鏈接下載APK后整袁,進(jìn)行安裝升級應(yīng)用。如果要自己重新寫的話 志膀,可能要花半個(gè)或一個(gè)多小時(shí)熙宇。不過寫過一遍后鳖擒,下次實(shí)現(xiàn)起來就簡單許多了。所以嘛烫止,作者就做了這個(gè)簡單封裝類蒋荚。你們只需CP,不說三分鐘搞定馆蠕,但也可以少走很多彎路期升。
二、效果預(yù)覽
話再多互躬,還不如先看看效果播赁。適不適合自己的胃口,好吃的話可以點(diǎn)個(gè)贊吼渡。
?三行拢、實(shí)現(xiàn)步驟
(1)檢查權(quán)限/申請權(quán)限
這里使用LinPermission個(gè)人封裝的權(quán)限工具類,這里就不解釋了诞吱,不是本章主要內(nèi)容。你們可以使用自己的權(quán)限進(jìn)行替換竭缝。
(2)提示更新
可以使用Dialog房维,也可以使用作者封裝好的LinCustomDialogFragment
(3)調(diào)用工具類進(jìn)行下載安裝
//1.檢查存儲(chǔ)權(quán)限
if (LinPermission.checkPermission(activity, 7)) {
//2.彈窗提示
LinCustomDialogFragment.init().setTitle("發(fā)現(xiàn)新版本")
.setContent("是否更新?")
.setType(LinCustomDialogFragment.TYPE_CANCLE)
.setOnClickListener(new LinCustomDialogFragment.OnSureCancleListener() {
@Override
public void clickSure(String EdiText) {
//3.下載安裝
LinDownloadAPk.downApk(activity, Constants.InstallApk);
}
@Override
public void clickCancle() {
}
}).show(getFragmentManager());
} else {
//申請存儲(chǔ)權(quán)限
LinPermission.requestPermission(activity, 7);
}
LinDownloadAPk.class
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.provider.Settings;
import android.support.annotation.RequiresApi;
import android.support.v4.content.FileProvider;
import android.view.View;
import android.widget.RemoteViews;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
import blcs.lwb.lwbtool.R;
/**
* 下載工具類(開發(fā)中一般用于APK應(yīng)用升級)
*/
public class LinDownloadAPk
{
private static int FILE_LEN = 0;
private static RemoteViews mNotifiviews;
public static String APK_UPGRADE = Environment
.getExternalStorageDirectory() + "/DownLoad/apk/BLCS.apk";
private static PendingIntent nullIntent;
private static Context mContext;
/**
* 判斷8.0 安裝權(quán)限
*/
public static void downApk(Context context, String url) {
mContext = context;
if (Build.VERSION.SDK_INT >= 26) {
boolean b = context.getPackageManager().canRequestPackageInstalls();
if (b) {
downloadAPK( url, null);
} else {
//請求安裝未知應(yīng)用來源的權(quán)限
startInstallPermissionSettingActivity();
}
} else {
downloadAPK( url, null);
}
}
/**
* 開啟安裝APK權(quán)限(適配8.0)
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public static void startInstallPermissionSettingActivity() {
Uri packageURI = Uri.parse("package:" + mContext.getPackageName());
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);
mContext.startActivity(intent);
}
/**
* 下載APK文件
*/
private static void downloadAPK( String url,String localAddress)
{
// 下載
if (localAddress != null)
{
APK_UPGRADE = localAddress;
}
new UpgradeTask().execute(url);
}
static class UpgradeTask extends AsyncTask<String, Integer, Void>
{
@Override
protected void onPreExecute()
{
// 發(fā)送通知顯示升級進(jìn)度
sendNotify();
}
@Override
protected Void doInBackground(String... params)
{
String apkUrl = params[0];
InputStream is = null;
FileOutputStream fos = null;
try {
URL url = new URL(apkUrl);
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
// 設(shè)置連接超時(shí)時(shí)間
conn.setConnectTimeout(25000);
// 設(shè)置下載數(shù)據(jù)超時(shí)時(shí)間
conn.setReadTimeout(25000);
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK)
{
return null;// 服務(wù)端錯(cuò)誤響應(yīng)
}
is = conn.getInputStream();
FILE_LEN = conn.getContentLength();
File apkFile = new File(APK_UPGRADE);
// 如果文件夾不存在則創(chuàng)建
if (!apkFile.getParentFile().exists())
{
apkFile.getParentFile().mkdirs();
}
fos = new FileOutputStream(apkFile);
byte[] buffer = new byte[8024];
int len = 0;
int loadedLen = 0;// 當(dāng)前已下載文件大小
// 更新10次
int updateSize = FILE_LEN / 10;
int num = 0;
while (-1 != (len = is.read(buffer)))
{
loadedLen += len;
fos.write(buffer, 0, len);
if (loadedLen > updateSize * num)
{
num++;
publishProgress(loadedLen);
}
}
fos.flush();
} catch (MalformedURLException e)
{
e.printStackTrace();
} catch (SocketTimeoutException e)
{
// 處理超時(shí)異常,提示用戶在網(wǎng)絡(luò)良好情況下重試
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} finally
{
if (is != null)
{
try
{
is.close();
} catch (IOException e)
{
e.printStackTrace();
}
}
if (fos != null)
{
try
{
fos.close();
} catch (IOException e)
{
e.printStackTrace();
}
}
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values)
{
// 更新通知
updateNotify(values[0]);
}
@Override
protected void onPostExecute(Void result)
{
Toast.makeText(mContext, "下載完成抬纸,請點(diǎn)擊通知欄完成升級", Toast.LENGTH_LONG)
.show();
finishNotify();
}
}
private static void sendNotify()
{
Intent intent = new Intent();
nullIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
mNotifiviews = new RemoteViews(mContext.getPackageName(),
R.layout.custom_notify);
mNotifiviews.setViewVisibility(R.id.tv_custom_notify_number, View.VISIBLE);
mNotifiviews.setViewVisibility(R.id.pb_custom_notify, View.VISIBLE);
LinNotify.show(mContext,"","",mNotifiviews,LinNotify.NEW_MESSAGE,null);
}
private static void updateNotify(int loadedLen)
{
int progress = loadedLen * 100 / FILE_LEN;
mNotifiviews.setTextViewText(R.id.tv_custom_notify_number, progress + "%");
mNotifiviews.setProgressBar(R.id.pb_custom_notify, FILE_LEN, loadedLen,
false);
LinNotify.show(mContext,"","",mNotifiviews,LinNotify.NEW_MESSAGE,null);
}
private static void finishNotify()
{
mNotifiviews.setTextViewText(R.id.tv_custom_notify_number, "100%");
Intent installAppIntent = getInstallAppIntent(APK_UPGRADE);
PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0,installAppIntent, 0);
mNotifiviews.setTextViewText(R.id.tv_custom_notify_finish, "下載完成咙俩,請點(diǎn)擊進(jìn)行安裝");
mNotifiviews.setViewVisibility(R.id.tv_custom_notify_number, View.INVISIBLE);
mNotifiviews.setViewVisibility(R.id.pb_custom_notify, View.GONE);
mNotifiviews.setViewVisibility(R.id.tv_custom_notify_finish, View.VISIBLE);
LinNotify.show(mContext,"","",mNotifiviews,LinNotify.NEW_MESSAGE,contentIntent);
}
/**
* 調(diào)往系統(tǒng)APK安裝界面(適配7.0)
* @return
*/
public static Intent getInstallAppIntent( String filePath) {
//apk文件的本地路徑
File apkfile = new File(filePath);
if (!apkfile.exists()) {
return null;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri contentUri = getUriForFile(apkfile);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
return intent;
}
/**
* 將文件轉(zhuǎn)換成uri
* @return
*/
public static Uri getUriForFile(File file) {
LogUtils.e(mContext.getPackageName());
Uri fileUri = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
fileUri = FileProvider.getUriForFile(mContext, mContext.getPackageName()+".fileprovider", file);
} else {
fileUri = Uri.fromFile(file);
}
return fileUri;
}
}
(1)這里面還使用了LinNotify通知工具類,因?yàn)椴皇侵饕獌?nèi)容湿故,所以使用工具類方便繼續(xù)阿趁。你們也可自己寫一個(gè)通知進(jìn)行替代。
(2)Android7.0 FileProvider適配
Android 7.0 做了一些權(quán)限更改坛猪,為了提高私有文件的安全性脖阵,面向 Android 7.0 或更高版本的應(yīng)用私有目錄被限制訪問。對于面向 Android 7.0 的應(yīng)用墅茉,Android 框架執(zhí)行的 StrictMode API 政策禁止在您的應(yīng)用外部公開 file:// URI命黔。如果一項(xiàng)包含文件 URI 的 intent 離開您的應(yīng)用,則應(yīng)用出現(xiàn)故障就斤,并出現(xiàn) FileUriExposedException 異常悍募。(這里就簡單介紹實(shí)現(xiàn)步驟)
1.聲明 FileProvider
<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/file_paths" />
</provider>
</application>
2.xml配置(res/xml/file_paths.xml)
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!--代表設(shè)備的根目錄 new File("/")-->
<root-path name="root" path="" />
<!--代表 context.getFileDir()-->
<files-path name="files" path="." />
<!--代表 context.getCacheDir()-->
<cache-path name="cache" path="." />
<!--代表 Environment.getExternalStorageDirectory()-->
<external-path name="external" path="." />
<!--代表 context.getExternalFilesDirs()-->
<external-files-path name="external-files" path="." />
<!--代表 getExternalCacheDirs()-->
<external-cache-path name="external-cache" path="." />
</paths>
3.代碼實(shí)現(xiàn)
public static Uri getUriForFile(File file) {
Uri fileUri = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
fileUri = FileProvider.getUriForFile(mContext, mContext.getPackageName()+".fileprovider", file);
} else {
fileUri = Uri.fromFile(file);
}
return fileUri;
}
(3)Android8.0 增加未知來源應(yīng)用權(quán)限適配
Android8.0的諸多新特性中有一個(gè)非常重要的特性:未知來源應(yīng)用權(quán)限
1.在清單文件中增加請求安裝權(quán)限
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
2.判斷是否開啟權(quán)限
boolean b = context.getPackageManager().canRequestPackageInstalls();
3.開啟安裝APK權(quán)限
/**
* 開啟安裝APK權(quán)限(適配8.0)
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public static void startInstallPermissionSettingActivity() {
Uri packageURI = Uri.parse("package:" + mContext.getPackageName());
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);
mContext.startActivity(intent);
}
到這里代碼已經(jīng)全部提供好了,你們只需學(xué)會(huì)CP大法洋机。1分鐘就差不多可以搞定這個(gè)功能了
當(dāng)然清單中還需要添加基礎(chǔ)的網(wǎng)絡(luò)坠宴,存儲(chǔ)權(quán)限。
四绷旗、功能解析
(1)android 8.0 適配
1.判斷是否是8.0以上 是的話進(jìn)行8.0適配
2.android8.0以上判斷是否已經(jīng)申請安裝權(quán)限 沒有這進(jìn)行權(quán)限申請
3.滿足以上條件則調(diào)用 downLoadAPK()方法
public static void downApk(Context context, String url) {
mContext = context;
//8.0需要申請安裝權(quán)限
if (Build.VERSION.SDK_INT >= 26) {
boolean b = context.getPackageManager().canRequestPackageInstalls();
if (b) {
downloadAPK( url, null);
} else {
//請求安裝未知應(yīng)用來源的權(quán)限
startInstallPermissionSettingActivity();
}
} else {
downloadAPK( url, null);
}
}
(2)下載APK鏈接
new UpgradeTask().execute(url);
這里使用AsyncTask進(jìn)行異步下載
1.開始下載前喜鼓,進(jìn)行下載通知
@Override
protected void onPreExecute()
{
// 發(fā)送通知顯示升級進(jìn)度
sendNotify();
}
2.更新通知欄進(jìn)度條方法
publishProgress(loadedLen);
@Override
protected void onProgressUpdate(Integer... values)
{
// 更新通知
updateNotify(values[0]);
}
3.下載完成后通知
@Override
protected void onPostExecute(Void result)
{
Toast.makeText(mContext, "下載完成副砍,請點(diǎn)擊通知欄完成升級", Toast.LENGTH_LONG).show();
finishNotify();
}
4.完成后,點(diǎn)擊進(jìn)行安裝颠通。
private static void finishNotify()
{
mNotifiviews.setTextViewText(R.id.tv_custom_notify_number, "100%");
Intent installAppIntent = getInstallAppIntent(APK_UPGRADE);
PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0,installAppIntent, 0);
mNotifiviews.setTextViewText(R.id.tv_custom_notify_finish, "下載完成址晕,請點(diǎn)擊進(jìn)行安裝");
mNotifiviews.setViewVisibility(R.id.tv_custom_notify_number, View.INVISIBLE);
mNotifiviews.setViewVisibility(R.id.pb_custom_notify, View.GONE);
mNotifiviews.setViewVisibility(R.id.tv_custom_notify_finish, View.VISIBLE);
LinNotify.show(mContext,"","",mNotifiviews,LinNotify.NEW_MESSAGE,contentIntent);
}
5.安裝APK
/**
* 調(diào)往系統(tǒng)APK安裝界面(適配7.0)
* @return
*/
public static Intent getInstallAppIntent( String filePath) {
//apk文件的本地路徑
File apkfile = new File(filePath);
if (!apkfile.exists()) {
return null;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri contentUri = getUriForFile(apkfile);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
return intent;
}
6.適配7.0
/**
* 將文件轉(zhuǎn)換成uri
* @return
*/
public static Uri getUriForFile(File file) {
Uri fileUri = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
fileUri = FileProvider.getUriForFile(mContext, mContext.getPackageName()+".fileprovider", file);
} else {
fileUri = Uri.fromFile(file);
}
return fileUri;
}
五、Demo地址
源碼地址:https://github.com/DayorNight/BLCS
apk下載體驗(yàn)地址:https://www.pgyer.com/BLCS
六顿锰、內(nèi)容推薦
CSDN:《Android 下載安裝應(yīng)用APK封裝(適配8.0)》
《Android Notification通知簡單封裝(適配8.0)???????》???????
《Android 仿RxDialog自定義DialogFragment》
《Android 獲取App應(yīng)用谨垃、緩存、數(shù)據(jù)等大小適配8.0(仿微信存儲(chǔ)空間)》
?如果你覺得寫的不錯(cuò)或者對您有所幫助的話
不妨頂一個(gè)【微笑】硼控,別忘了點(diǎn)贊刘陶、收藏、加關(guān)注哈
看在花了這么多時(shí)間整理寫成文章分享給大家的份上牢撼,記得手下留情哈
您的每個(gè)舉動(dòng)都是對我莫大的支持