Android 下载安装应用APK封装(适配8.0)

Android 下载安装应用APK封装(适配8.0)_第1张图片

一、简介

二、效果预览

​三、实现步骤

四、功能解析

五、Demo地址

六、内容推荐


一、简介

嘿嘿,这周没缺席,继续给大伙们提供一个工具类。用于下载APK,安装应用。几乎每个APP都带有这个功能,通过链接下载APK后,进行安装升级应用。如果要自己重新写的话 ,可能要花半个或一个多小时。不过写过一遍后,下次实现起来就简单许多了。所以嘛,作者就做了这个简单封装类。你们只需CP,不说三分钟搞定,但也可以少走很多弯路。

Android 下载安装应用APK封装(适配8.0)_第2张图片

二、效果预览

话再多,还不如先看看效果。适不适合自己的胃口,好吃的话可以点个赞。

Android 下载安装应用APK封装(适配8.0)_第3张图片

​三、实现步骤

(1)检查权限/申请权限

这里使用LinPermission个人封装的权限工具类,这里就不解释了,不是本章主要内容。你们可以使用自己的权限进行替换。

(2)提示更新

可以使用Dialog,也可以使用作者封装好的LinCustomDialogFragment

(3)调用工具类进行下载安装

//1.检查存储权限  
if (LinPermission.checkPermission(activity, 7)) {
    //2.弹窗提示 
    LinCustomDialogFragment.init().setTitle("发现新版本")
        .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 {
    //申请存储权限
    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;

/**
 * 下载工具类(开发中一般用于APK应用升级)
 */
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 安装权限
	 */
	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 {
				//请求安装未知应用来源的权限
				startInstallPermissionSettingActivity();
			}
		} else {
			downloadAPK( url, null);
		}
	}

	/**
	 * 开启安装APK权限(适配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
	{
		@Override
		protected void onPreExecute()
		{
			// 发送通知显示升级进度
			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();
				// 设置连接超时时间
				conn.setConnectTimeout(25000);
				// 设置下载数据超时时间
				conn.setReadTimeout(25000);
				if (conn.getResponseCode() != HttpURLConnection.HTTP_OK)
				{
					return null;// 服务端错误响应
				}
				is = conn.getInputStream();
				FILE_LEN = conn.getContentLength();
				File apkFile = new File(APK_UPGRADE);
				// 如果文件夹不存在则创建
				if (!apkFile.getParentFile().exists())
				{
					apkFile.getParentFile().mkdirs();
				}
				fos = new FileOutputStream(apkFile);
				byte[] buffer = new byte[8024];
				int len = 0;
				int loadedLen = 0;// 当前已下载文件大小
				// 更新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)
			{
				// 处理超时异常,提示用户在网络良好情况下重试
			} 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, "下载完成,请点击通知栏完成升级", 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, "下载完成,请点击进行安装");
		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);
	}

	/**
	 * 调往系统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;
	}

	/**
	 * 将文件转换成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通知工具类,因为不是主要内容,所以使用工具类方便继续。你们也可自己写一个通知进行替代。

(2)Android7.0 FileProvider适配

Android 7.0 做了一些权限更改,为了提高私有文件的安全性,面向 Android 7.0 或更高版本的应用私有目录被限制访问。对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode API 政策禁止在您的应用外部公开 file:// URI。如果一项包含文件 URI 的 intent 离开您的应用,则应用出现故障,并出现 FileUriExposedException 异常。(这里就简单介绍实现步骤)

1.声明 FileProvider


    ......
    ......
        
            
        

2.xml配置(res/xml/file_paths.xml)



    
    
    
    
    
    
    
    
    
    
    
    

3.代码实现

	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  增加未知来源应用权限适配

Android8.0的诸多新特性中有一个非常重要的特性:未知来源应用权限

1.在清单文件中增加请求安装权限

2.判断是否开启权限

boolean b = context.getPackageManager().canRequestPackageInstalls();

3.开启安装APK权限

	/**
	 * 开启安装APK权限(适配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);
	}

到这里代码已经全部提供好了,你们只需学会CP大法。1分钟就差不多可以搞定这个功能了

当然清单中还需要添加基础的网络,存储权限。

Android 下载安装应用APK封装(适配8.0)_第4张图片

四、功能解析

(1)android 8.0 适配

1.判断是否是8.0以上 是的话进行8.0适配

2.android8.0以上判断是否已经申请安装权限  没有这进行权限申请

3.满足以上条件则调用 downLoadAPK()方法

	public static void downApk(Context context, String url) {
		mContext = context;
        //8.0需要申请安装权限
		if (Build.VERSION.SDK_INT >= 26) {
			boolean b = context.getPackageManager().canRequestPackageInstalls();
			if (b) {
				downloadAPK( url, null);
			} else {
				//请求安装未知应用来源的权限
				startInstallPermissionSettingActivity();
			}
		} else {
			downloadAPK( url, null);
		}
	}

(2)下载APK链接

new UpgradeTask().execute(url);

这里使用AsyncTask进行异步下载

1.开始下载前,进行下载通知

@Override
protected void onPreExecute()
{
	// 发送通知显示升级进度
	sendNotify();
}

2.更新通知栏进度条方法

publishProgress(loadedLen);

@Override
protected void onProgressUpdate(Integer... values)
{
	// 更新通知
	updateNotify(values[0]);
}

3.下载完成后通知

@Override
protected void onPostExecute(Void result)
{
	Toast.makeText(mContext, "下载完成,请点击通知栏完成升级", Toast.LENGTH_LONG).show();
	finishNotify();
}

4.完成后,点击进行安装。

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, "下载完成,请点击进行安装");
		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

	/**
	 * 调往系统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

/**
	 * 将文件转换成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下载体验地址:https://www.pgyer.com/BLCS

 

Android 下载安装应用APK封装(适配8.0)_第5张图片

六、内容推荐

简书:《Android 下载安装应用APK封装(适配8.0)》

《Android Notification通知简单封装(适配8.0)》

《Android 仿RxDialog自定义DialogFragment》

《Android 获取App应用、缓存、数据等大小适配8.0(仿微信存储空间)》

《Android Rxjava+Retrofit网络请求框架封装(一)》

如果你觉得写的不错或者对您有所帮助的话

不妨顶一个【微笑】,别忘了点赞、收藏、加关注哈

看在花了这么多时间整理写成文章分享给大家的份上,记得手下留情哈

您的每个举动都是对我莫大的支持

Android 下载安装应用APK封装(适配8.0)_第6张图片

 

 

你可能感兴趣的:(Android)