版权声明:本文为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"/> <application
android: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的包名}.provider
grantUriPermissions:必须是true,表示授予 URI 临时访问权限
exported:必须是false
resource:中的@xml/provider_paths是我们接下来要添加的文件-->
<provider
android: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() {
@Override
public 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 instance
rxPermissions.request(Manifest.permission.READ_EXTERNAL_STORAGE) //权限名称,多个权限之间逗号分隔开
.subscribe(new Consumer<Boolean>() {
@Override
public 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>() {
@Override
public void accept(Throwable throwable) throws Exception {
Log.e(TAG,"{accept}");//可能是授权异常的情况下的处理
}
}, new Action() {
@Override
public 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 instance
rxPermissions.request(Manifest.permission.REQUEST_INSTALL_PACKAGES) //权限名称,多个权限之间逗号分隔开
.subscribe(new Consumer<Boolean>() {
@Override
public void accept(Boolean granted) throws Exception {
Log.e(TAG, "{accept}granted=" + granted);//执行顺序——1【多个权限的情况,只有所有的权限均允许的情况下granted==true】
if (granted) { // 在android 6.0之前会默认返回true
//安装APP
AppUtils.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>() {
@Override
public void accept(Throwable throwable) throws Exception {
Log.e(TAG,"{accept}");//可能是授权异常的情况下的处理
}
}, new Action() {
@Override
public void run() throws Exception {
Log.e(TAG,"{run}");//执行顺序——2
}
});
} else {
//安装APP
AppUtils.installApk(MainActivity.this,apkFileSavePath);
}
} else {
//安装APP
AppUtils.installApk(MainActivity.this,apkFileSavePath);
}
} @Override
protected 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 = ""; @Override
protected 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() {
@Override
public 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() {
@Override
public 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 instance
rxPermissions.request(Manifest.permission.REQUEST_INSTALL_PACKAGES) //权限名称,多个权限之间逗号分隔开
.subscribe(new Consumer<Boolean>() {
@Override
public void accept(Boolean granted) throws Exception {
Log.e(TAG, "{accept}granted=" + granted);//执行顺序——1【多个权限的情况,只有所有的权限均允许的情况下granted==true】
if (granted) { // 在android 6.0之前会默认返回true
//安装APP
AppUtils.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>() {
@Override
public void accept(Throwable throwable) throws Exception {
Log.e(TAG,"{accept}");//可能是授权异常的情况下的处理
}
}, new Action() {
@Override
public void run() throws Exception {
Log.e(TAG,"{run}");//执行顺序——2
}
});
} else {
//安装APP
AppUtils.installApk(MainActivity.this,apkFileSavePath);
}
} else {
//安装APP
AppUtils.installApk(MainActivity.this,apkFileSavePath);
}
} @Override
protected 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 instance
rxPermissions.request(Manifest.permission.READ_EXTERNAL_STORAGE) //权限名称,多个权限之间逗号分隔开
.subscribe(new Consumer<Boolean>() {
@Override
public 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>() {
@Override
public void accept(Throwable throwable) throws Exception {
Log.e(TAG,"{accept}");//可能是授权异常的情况下的处理
}
}, new Action() {
@Override
public 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. PHP获取手机相关信息

    该PHP操作类实现获取手机号手机头信息,取UA,取得手机类型,判断是否是opera,判断是否是m3gate,取得HA,取得手机IP 代码如下: <?php /** * @desc 手机操作类 获 ...

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

    http://blog.csdn.net/changemyself/article/details/7421476 下面我从安卓开发的角度,简单写一下如何获取手机设备信息和手机号码 准备条件:一部安卓 ...

  3. android利用ContentResolver访问者获取手机联系人信息

    转载自:http://www.jb51.net/article/106379.htm 首先需要在AndroidManifest.xml文件中添加权限: <uses-permission andr ...

  4. Expo大作战(三十九)--expo sdk api之 DocumentPicker,Contacts(获取手机联系人信息),Branch

    简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...

  5. Android 获取手机信息,设置权限,申请权限,查询联系人,获取手机定位信息

    Android 获取手机信息,设置权限,申请权限,查询联系人,获取手机定位信息 本文目录: 获取手机信息 设置权限 申请权限 查询联系人 获取手机定位信息 调用高德地图,设置显示2个坐标点的位置,以及 ...

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

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

  7. iOS开发-Swift获取手机设备信息(UIDevice)

    使用UiDevice获取设备信息 获取设备名称 let name = UIDevice.currentDevice().name 获取设备系统名称 let systemName = UIDevice. ...

  8. iOS获取手机相关信息

    iOS具体的设备型号: #include <sys/types.h> #include <sys/sysctl.h> - (void)test { //手机型号. size_t ...

  9. ios开发-获取手机相关信息

    今天在做客户端的时候,里面有个意见反馈功能. 调用系统带的邮件功能,发送邮件到指定邮箱. 然后我就想,应该在邮件正文部分添加手机相关内容,比如型号,版本,应用程序的版本等等,这样不仅使用者方便,开发者 ...

随机推荐

  1. selenium测试(Java)-- 显式等待(九)

    转自:https://www.cnblogs.com/moonpool/p/5668571.html 显式等待可以使用selenium预置的判断方法,也可以使用自定义的方法. package com. ...

  2. xamarin android网络请求总结

    xamarin android中网络请求的框架非常多,在项目中使用的是第三方的一个网络请求框架restsharp,应该是github上.net网络请求最多star的框架,没有之一.这里就简单汇总了其他 ...

  3. appium 运行报错:...... Attempt to re-install io.appium.settings without first uninstalling解决方案

    报错形式: Failed to install D:\AutoTest\appium\Appium\node_modules\appium\build\settings_apk\settings_ap ...

  4. 计算机17-3,4作业E

    E.complete number Description 完数是指一个整数的因子和等于这个数本身,例如6=1+2+3,所以6是一个完数. 按照给定数据范围,找出期中所有完数并输出. Input 数据 ...

  5. SDRAM读写状态解析

    SDRAM的写状态流程 IDLE状态到WRITE状态 (1)在IDLE状态需要先给ACT命令激活某一行,此时处于Row Active状态. (2)在Row Active状态之后,给Write命令则会进 ...

  6. Unix中的I/O模型

    本文所指的I/O均是网络I/O. 一. POSIX对同步.异步I/O的定义 我们先大致看看POSIX对同步.异步的定义,不用细究,重点看我标红的部分就行. 同步I/O会导致请求进程阻塞,直到I/O操作 ...

  7. go语言调度器源代码情景分析之四:函数调用栈

    本文是<go调度器源代码情景分析>系列 第一章 预备知识的第3小节. 什么是栈 栈是一种“后进先出”的数据结构,它相当于一个容器,当需要往容器里面添加元素时只能放在最上面的一个元素之上,需 ...

  8. java游戏开发杂谈 - 游戏编程浅析

    每个游戏,你所看到的它的一切,都是计算机画出来的! 地图是画出来,人物是画出来的,树木建筑是画出来的,菜单按钮是画出来的,滚动的文字.闪烁的图标.云雾烟火,都是画出来的. 游戏编程,所要做的,就是控制 ...

  9. Netty基础系列(2) --彻底理解阻塞非阻塞与同步异步的区别

    引言 在进行I/O学习的时候,阻塞和非阻塞,同步和异步这几个概念常常被提及,但是很多人对这几个概念一直很模糊.要想学好Netty,这几个概念必须要掌握清楚. 同步和异步 同步与异步的区别在于,异步基于 ...

  10. python——绘制二元高斯分布的三维图像,

    在对数据进行可视化的过程中,可能经常需要对数据进行三维绘图,在python中进行三维绘图其实是比较简单的,下面我们将给出一个二元高斯分布的三维图像案例,并且给出相关函数的参数. 通常,我们绘制三维图像 ...