AppUtils【获取手机的信息和应用版本号、安装apk】
版权声明:本文为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); } } }
在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); } }
混淆配置
无
参考资料
Android 7.0 行为变更 通过FileProvider在应用间共享文件吧