版权声明:本文为HaiyuKing原创文章,转载请注明出处!

前言

一个获取设备的系统版本号、设备的型号、应用版本号code值、应用版本号name值、包名、是否更新、安装apk的工具类。

其实这个工具类的主要功能是安装apk方法,所以需要搭配《Android6.0运行时权限(基于RxPermission开源库)》、《AppDir【创建缓存目录】》【这个是用来存放下载的apk文件的,可以不用】。

下载apk的过程没有使用网路请求,而是通过模拟的方式:其实就是从assets目录复制apk文件到手机目录中。

效果图

代码分析

需要注意的代码包括以下部分:

1、下载apk之前需要先申请运行时权限(READ_EXTERNAL_STORAGE),我是在项目启动的时候申请的,不是在事件触发的时候申请的;

2、适配7.0FileProvider

3、安装前先适配8.0请求未知来源权限

使用步骤

一、项目组织结构图

注意事项:

1、  导入类文件后需要change包名以及重新import R文件路径

2、  Values目录下的文件(strings.xml、dimens.xml、colors.xml等),如果项目中存在,则复制里面的内容,不要整个覆盖

二、导入步骤

将AppUtils复制到项目中

package com.why.project.apputilsdemo.util;import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.support.v4.content.FileProvider;
import android.text.TextUtils;import java.io.File;/*** 获取手机的信息和应用版本号、安装apk*/
public class AppUtils {/*** 获取设备的系统版本号*/public static String getDeviceVersion() {return android.os.Build.VERSION.RELEASE;}/*** 获取设备的型号*/public static String getDeviceName() {String model = android.os.Build.MODEL;return model;}/*** 应用版本号code值*/public static int getVersionCode(Context context) {return getPackageInfo(context).versionCode;}/*** 应用版本号name值*/public static String getVersionName(Context context){return getPackageInfo(context).versionName;}private static PackageInfo getPackageInfo(Context context) {PackageInfo pi = null;try {PackageManager pm = context.getPackageManager();pi = pm.getPackageInfo(context.getPackageName(),PackageManager.GET_CONFIGURATIONS);return pi;} catch (Exception e) {e.printStackTrace();}return pi;}//获取包名public static String getPackageName(Context context){return context.getPackageName();}//是否更新,根据versionName值进行判断public static boolean getVersionUpdate(Context context, String versionNameServer){//versionNameServer = "3.1";String versionNameLocal = getVersionName(context);if(!TextUtils.isEmpty(versionNameLocal) && !TextUtils.isEmpty(versionNameServer)){String[] splitLocal = versionNameLocal.split("\\.");String[] splitServer = versionNameServer.split("\\.");if(splitLocal.length == splitServer.length){for(int i=0;i<splitLocal.length;i++){int localInt = Integer.parseInt(splitLocal[i]);int serverInt = Integer.parseInt(splitServer[i]);if(serverInt > localInt){return true;}else if(serverInt==localInt){}else {return false;}}}}return false;}/**返回安装apk的Intent*/public static Intent getFileIntent(Context mContext,String fileSavePath) {File apkfile = new File(fileSavePath);if (!apkfile.exists()) {return null;}Intent intent = new Intent();intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.setAction(android.content.Intent.ACTION_VIEW);Uri uri;if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {String authority = mContext.getApplicationInfo().packageName + ".provider";uri = FileProvider.getUriForFile(mContext.getApplicationContext(), authority, apkfile);intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//添加这一句表示对目标应用临时授权该Uri所代表的文件【很重要】} else {uri = Uri.fromFile(apkfile);}intent.setDataAndType(uri, getMIMEType(apkfile));return intent;}public static String getMIMEType(File file) {String type = null;String suffix = file.getName().substring(file.getName().lastIndexOf(".") + 1, file.getName().length());if (suffix.equals("apk")) {type = "application/vnd.android.package-archive";} else {// /*如果无法直接打开,就跳出软件列表给用户选择 */type = "*/*";}return type;}/*** 安装apk【如果项目中需要使用这个方法的话,需要申请运行时权限(读写文件的权限)、需要特出处理Android8.0的请求未知来源权限】*/public static void installApk(Context mContext,String fileSavePath) {Intent intent = getFileIntent(mContext,fileSavePath);if(intent != null){mContext.startActivity(intent);}}
}

AppUtils.java

在AndroidManifest.xml中添加权限以及配置7.0FileProvider【注意:provider中的android:authorities值:${applicationId}.provider,其中${applicationId}代表的真实值就是APP的build.gradle中的applicationId(包名)值】

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.why.project.apputilsdemo"><!-- =================AppDir用到的权限========================== --><!-- 允许程序读取外部存储文件 --><!--<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>--><!-- 允许程序写入外部存储,如SD卡上写文件 --><!--<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>--><!-- =================AppUtils用到的权限========================== --><!-- 允许程序读取外部存储文件 --><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><!-- 允许程序写入外部存储,如SD卡上写文件 --><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission><!-- =================8.0安装apk需要请求未知来源权限========================== --><uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><!-- =================7.0上读取文件========================== --><!--参考资料https://blog.csdn.net/lmj623565791/article/details/72859156--><!--authorities:{app的包名}.providergrantUriPermissions:必须是true,表示授予 URI 临时访问权限exported:必须是falseresource:中的@xml/provider_paths是我们接下来要添加的文件--><providerandroid:name="android.support.v4.content.FileProvider"android:authorities="${applicationId}.provider"android:exported="false"android:grantUriPermissions="true"><meta-data android:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/provider_paths"/></provider><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/></intent-filter></activity></application></manifest>

将provider_paths文件复制到项目的res/xml目录下【适配7.0FileProvider】

<?xml version="1.0" encoding="utf-8"?>
<!--参考资料https://blog.csdn.net/lmj623565791/article/details/72859156-->
<!--<root-path/> 代表设备的根目录new File("/");-->
<!--<files-path/> 代表context.getFilesDir()-->
<!--<cache-path/> 代表context.getCacheDir()-->
<!--<external-path/> 代表Environment.getExternalStorageDirectory()-->
<!--<external-files-path>代表context.getExternalFilesDirs()-->
<!--<external-cache-path>代表getExternalCacheDirs()--><!--path:需要临时授权访问的路径(.代表所有路径)-->
<!--name:就是你给这个访问路径起个名字-->
<paths><root-path name="root" path="." /><files-path name="files" path="." /><cache-path name="cache" path="." /><external-path name="external" path="." /><external-files-path name="external_file_path" path="." /><external-cache-path name="external_cache_path" path="." />
</paths>

参考《Android6.0运行时权限(基于RxPermission开源库)》、《AppDir【创建缓存目录】》导入相关文件。

三、使用方法

常规方法调用【获取设备型号、版本号、应用版本号、包名等】

btn_show.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {String DeviceVersion = AppUtils.getDeviceVersion();String DeviceName = AppUtils.getDeviceName();int VersionCode = AppUtils.getVersionCode(MainActivity.this);String VersionName = AppUtils.getVersionName(MainActivity.this);String PackageName = AppUtils.getPackageName(MainActivity.this);boolean isUpdate = AppUtils.getVersionUpdate(MainActivity.this,"2.0");String showText = "设备的系统版本号:" + DeviceVersion +"\n设备的型号:" + DeviceName +"\n应用版本号code值:" + VersionCode +"\n应用版本号name值:" + VersionName +"\n包名:" + PackageName +"\n是否更新(服务器版本号name值是2.0):" + isUpdate;tv_show.setText(showText);}});

申请运行时权限(存储权限)并模拟下载apk文件

/**只有一个运行时权限申请的情况*/private void onePermission(){RxPermissions rxPermissions = new RxPermissions(MainActivity.this); // where this is an Activity instancerxPermissions.request(Manifest.permission.READ_EXTERNAL_STORAGE) //权限名称,多个权限之间逗号分隔开.subscribe(new Consumer<Boolean>() {@Overridepublic void accept(Boolean granted) throws Exception {Log.e(TAG, "{accept}granted=" + granted);//执行顺序——1【多个权限的情况,只有所有的权限均允许的情况下granted==true】if (granted) { // 在android 6.0之前会默认返回true// 已经获取权限Toast.makeText(MainActivity.this, "已经获取权限", Toast.LENGTH_SHORT).show();downloadApkFile();} else {// 未获取权限Toast.makeText(MainActivity.this, "您没有授权该权限,请在设置中打开授权", Toast.LENGTH_SHORT).show();}}}, new Consumer<Throwable>() {@Overridepublic void accept(Throwable throwable) throws Exception {Log.e(TAG,"{accept}");//可能是授权异常的情况下的处理
                    }}, new Action() {@Overridepublic void run() throws Exception {Log.e(TAG,"{run}");//执行顺序——2
                    }});}/**模拟下载文件到手机本地目录下*/private void downloadApkFile(){String assetsFilePath = "wanandroid.apk";apkFileSavePath = AppDir.getInstance(MainActivity.this).DOWNLOAD + File.separator + "wanandroid.apk";DownloadUtil.copyOneFileFromAssetsToSD(MainActivity.this,assetsFilePath,apkFileSavePath);}

安装apk(先适配Android8.0请求未知来源权限)

/*** 检测版本8.0*/public void checkOreo() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//8.0//判断是否可以直接安装boolean canInstall = getPackageManager().canRequestPackageInstalls();if (!canInstall) {RxPermissions rxPermissions = new RxPermissions(MainActivity.this); // where this is an Activity instancerxPermissions.request(Manifest.permission.REQUEST_INSTALL_PACKAGES) //权限名称,多个权限之间逗号分隔开.subscribe(new Consumer<Boolean>() {@Overridepublic void accept(Boolean granted) throws Exception {Log.e(TAG, "{accept}granted=" + granted);//执行顺序——1【多个权限的情况,只有所有的权限均允许的情况下granted==true】if (granted) { // 在android 6.0之前会默认返回true//安装APPAppUtils.installApk(MainActivity.this, apkFileSavePath);} else {Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:" + getPackageName()));startActivityForResult(intent, 1000);}}}, new Consumer<Throwable>() {@Overridepublic void accept(Throwable throwable) throws Exception {Log.e(TAG,"{accept}");//可能是授权异常的情况下的处理
                            }}, new Action() {@Overridepublic void run() throws Exception {Log.e(TAG,"{run}");//执行顺序——2
                            }});} else {//安装APPAppUtils.installApk(MainActivity.this,apkFileSavePath);}} else {//安装APPAppUtils.installApk(MainActivity.this,apkFileSavePath);}}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);switch (requestCode) {case 1000:checkOreo();break;}}

完整代码:

package com.why.project.apputilsdemo;import android.Manifest;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;import com.tbruyelle.rxpermissions2.RxPermissions;
import com.why.project.apputilsdemo.util.AppDir;
import com.why.project.apputilsdemo.util.AppUtils;import java.io.File;import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;public class MainActivity extends AppCompatActivity {private static final String TAG = MainActivity.class.getSimpleName();private Button btn_show;private Button btn_install;private TextView tv_show;String apkFileSavePath = "";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);onePermission();//申请运行时权限(读写文件的权限)
initViews();initEvents();}private void initViews() {btn_show = findViewById(R.id.btn_show);btn_install = findViewById(R.id.btn_install);tv_show = findViewById(R.id.tv_show);}private void initEvents() {btn_show.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {String DeviceVersion = AppUtils.getDeviceVersion();String DeviceName = AppUtils.getDeviceName();int VersionCode = AppUtils.getVersionCode(MainActivity.this);String VersionName = AppUtils.getVersionName(MainActivity.this);String PackageName = AppUtils.getPackageName(MainActivity.this);boolean isUpdate = AppUtils.getVersionUpdate(MainActivity.this,"2.0");String showText = "设备的系统版本号:" + DeviceVersion +"\n设备的型号:" + DeviceName +"\n应用版本号code值:" + VersionCode +"\n应用版本号name值:" + VersionName +"\n包名:" + PackageName +"\n是否更新(服务器版本号name值是2.0):" + isUpdate;tv_show.setText(showText);}});btn_install.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {checkOreo();}});}/*** 检测版本8.0*/public void checkOreo() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//8.0//判断是否可以直接安装boolean canInstall = getPackageManager().canRequestPackageInstalls();if (!canInstall) {RxPermissions rxPermissions = new RxPermissions(MainActivity.this); // where this is an Activity instancerxPermissions.request(Manifest.permission.REQUEST_INSTALL_PACKAGES) //权限名称,多个权限之间逗号分隔开.subscribe(new Consumer<Boolean>() {@Overridepublic void accept(Boolean granted) throws Exception {Log.e(TAG, "{accept}granted=" + granted);//执行顺序——1【多个权限的情况,只有所有的权限均允许的情况下granted==true】if (granted) { // 在android 6.0之前会默认返回true//安装APPAppUtils.installApk(MainActivity.this, apkFileSavePath);} else {Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:" + getPackageName()));startActivityForResult(intent, 1000);}}}, new Consumer<Throwable>() {@Overridepublic void accept(Throwable throwable) throws Exception {Log.e(TAG,"{accept}");//可能是授权异常的情况下的处理
                            }}, new Action() {@Overridepublic void run() throws Exception {Log.e(TAG,"{run}");//执行顺序——2
                            }});} else {//安装APPAppUtils.installApk(MainActivity.this,apkFileSavePath);}} else {//安装APPAppUtils.installApk(MainActivity.this,apkFileSavePath);}}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);switch (requestCode) {case 1000:checkOreo();break;}}/**只有一个运行时权限申请的情况*/private void onePermission(){RxPermissions rxPermissions = new RxPermissions(MainActivity.this); // where this is an Activity instancerxPermissions.request(Manifest.permission.READ_EXTERNAL_STORAGE) //权限名称,多个权限之间逗号分隔开.subscribe(new Consumer<Boolean>() {@Overridepublic void accept(Boolean granted) throws Exception {Log.e(TAG, "{accept}granted=" + granted);//执行顺序——1【多个权限的情况,只有所有的权限均允许的情况下granted==true】if (granted) { // 在android 6.0之前会默认返回true// 已经获取权限Toast.makeText(MainActivity.this, "已经获取权限", Toast.LENGTH_SHORT).show();downloadApkFile();} else {// 未获取权限Toast.makeText(MainActivity.this, "您没有授权该权限,请在设置中打开授权", Toast.LENGTH_SHORT).show();}}}, new Consumer<Throwable>() {@Overridepublic void accept(Throwable throwable) throws Exception {Log.e(TAG,"{accept}");//可能是授权异常的情况下的处理
                    }}, new Action() {@Overridepublic void run() throws Exception {Log.e(TAG,"{run}");//执行顺序——2
                    }});}/**模拟下载文件到手机本地目录下*/private void downloadApkFile(){String assetsFilePath = "wanandroid.apk";apkFileSavePath = AppDir.getInstance(MainActivity.this).DOWNLOAD + File.separator + "wanandroid.apk";DownloadUtil.copyOneFileFromAssetsToSD(MainActivity.this,assetsFilePath,apkFileSavePath);}}

MainActivity.java

混淆配置

参考资料

Android 7.0 行为变更 通过FileProvider在应用间共享文件吧

项目demo下载地址

https://github.com/haiyuKing/AppUtilsDemo

AppUtils【获取手机的信息和应用版本号、安装apk】相关推荐

  1. Android截图apk,PC获取手机截图、复制文件、安装APK

    PC获取手机截图.复制文件.安装APK 我在eoe上的帖子的链接 PC获取手机截图.复制文件.安装APK http://www.eoeandroid.com/thread-324986-1-1.htm ...

  2. js获取PC设备信息,js获取手机设备信息,最全

    获取设备系统和型号<封装方法> <!DOCTYPE htmlPUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" &qu ...

  3. Android中获取手机设备信息、RAM、ROM存储信息,如宽、高、厂商名、手机品牌

    借鉴:https://www.jianshu.com/p/ca869aa2fd72 今天有两个工具类总结,代码里都有注释,直接看代码. 一.首先第一个,主要获取手机设备信息DeviceInfoUtil ...

  4. html5获取gps坐标,html5获取手机GPS信息的示例代码

    html5获取手机GPS信息的示例代码function getLocation() { if (navigator.geolocation) { navigator.geolocation.getCu ...

  5. 浏览器js 获取手机标识信息_手机软件多次要求获取手机信息,习惯性让其通过有安全隐患?...

    大家在平常用手机新安装一个手机软件时,第一次打开该软件都会提示需要你允许该软件获取你的信息,为了方便我们一般都不会仔细去看究竟是哪一项权限,大多数的APP只是申请要麦克风或者摄像头的权限而已.但是总是 ...

  6. 获取手机联系人信息(姓名 电话号码)

    手机的联系人信息,存放的位置分为两种.一种存放在手机内存里面,一种是存放在sim卡里面. 他们的访问方式都相似,都是把电话号码和联系人姓名是分开存放在数据库中的,只是访问的uri不同. 本文主要讲的是 ...

  7. android 安卓APP获取手机设备信息和手机号码的代码示例

    下面我从安卓开发的角度,简单写一下如何获取手机设备信息和手机号码 准备条件:一部安卓手机.手机SIM卡确保插入手机里.eclipse ADT和android-sdk开发环境 第一步:新建一个andro ...

  8. Java获取iphone手机gps信息_html5获取手机GPS信息的示例代码分享

    html5获取手机GPS信息的示例代码分享:function getLocation() { if (navigator.geolocation) { navigator.geolocation.ge ...

  9. android app 手机号码,android 安卓APP获取手机设备信息和手机号码的代码示例 .

    下面我从安卓开发的角度,简单写一下如何获取手机设备信息和手机号码 准备条件:一部安卓手机.手机SIM卡确保插入手机里.eclipse ADT和android-sdk开发环境 第一步:新建一个andro ...

最新文章

  1. 根据年月来判断月里天数
  2. 为moss添加展现reporting service的webpart
  3. SAP Spartacus State
  4. Django的STATIC_ROOT和STATIC_URL以及STATICFILES_DIRS
  5. 【Elasticsearch】如何使用 Elasticsearch 6.2 搜索中文、日文和韩文文本 - 第 2 部分: 多字段
  6. Vagrant搭建开发环境1--总体介绍
  7. Cocos2d-html5 2.2.2的屏幕适配方案
  8. 关于连接参数-Ttext
  9. 协作多智能体强化学习中的回报函数设计
  10. CNKI知网论文下载工具
  11. 报童问题求解最大利润_矩问题和分布式鲁棒优化:由阿里数学竞赛题说开...
  12. 计算机如何解锁 磁盘,win7电脑磁盘被写保护如何解除,电脑磁盘被写保护解除方法...
  13. 机器学习的13种算法和4种学习方法,推荐给大家
  14. Python 打怪兽游戏
  15. 【业务安全06】接口参数账号修改漏洞——基于metinfov4.0平台
  16. 一篇五分生信临床模型预测文章代码复现——Figure1 差异表达基因及预后基因筛选——火山图,Venn图,热图绘制(二)
  17. IOS如何发送大文件到QQ
  18. 20189320《网络攻防》第六周作业
  19. shell检测硬件状态脚本
  20. 查看tomcat的端口号

热门文章

  1. [趣味分享]纯手工机械的无碳小车
  2. 给文字做涂鸦效果的五种实现方案
  3. Office2007 Beta2简体中文版免费下载
  4. 美国数学建模比赛建议
  5. 考注会会经常用计算机吗,注会机考系统计算器你会用吗?操作技巧抢先看!
  6. WINME/WIN2000/LINUX多重启动详解(4年前写的,不知道现在还有没有用)
  7. 如何检查下载的软件是否带有后门
  8. 【Java】数组:动态初始化 、静态初始化
  9. linux 时间与bios,linux与时间相关的命令
  10. unity 利用NavMeshAgent2D实现怪物追人