Android中的软件的自动更新(包括静默更新,需Root权限)
来源:互联网 发布:windows media pl 编辑:程序博客网 时间:2024/06/05 15:39
编写不易,如有转载,请声明出处: 梦回河口:http://blog.csdn.net/zxc514257857/article/details/77504010
前言
之前实现过基于无障碍服务的自动更新(如想了解请移步 http://blog.csdn.net/zxc514257857/article/details/72667640),但是在测试及使用过程中发现有如下缺陷:
- 无障碍服务只出现在android5.0以上系统中,且有些5.0系统也没有无障碍服务
- 安装的时候会有安装显示
- 安装器上的“安装”字样,无障碍服务可以自动识别并且模拟点击,但安装成功之后的“完成”字样,有些安装器无法自动识别
于是就出现了这篇博客,但需要android设备获取Root权限
全部代码
// 注:注释的代码都是有用且必须的,但需要你仔细去看,并把它搞懂// AndroidManifest中权限配置<uses-permission android:name="android.permission.INTERNET"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/><uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/><uses-permission android:name="android.permission.RESTART_PACKAGES" /><uses-permission android:name="android.permission.INSTALL_PACKAGES" tools:ignore="ProtectedPermissions" />----------------------------------------------------------------------------------------// AndroidManifest中静态注册广播<receiver android:name=".SilenceInstallReceiver"> <intent-filter> <action android:name="android.intent.action.PACKAGE_REPLACED"/> <action android:name="android.intent.action.PACKAGE_ADDED" /> <action android:name="android.intent.action.PACKAGE_REMOVED" /> <data android:scheme="package"/> </intent-filter></receiver>----------------------------------------------------------------------------------------// MainActivityimport android.content.Context;import android.content.Intent;import android.content.pm.ApplicationInfo;import android.content.pm.PackageManager;import android.net.Uri;import android.os.Bundle;import android.os.Environment;import android.support.v7.app.AppCompatActivity;import android.text.TextUtils;import android.util.Log;import com.zhumei.update.R;import java.io.DataOutputStream;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.HttpURLConnection;import java.net.URL;import java.util.List;public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private Context mContext = MainActivity.this; private String updateUrl = "http://192.168.1.24/test/testapk.apk"; private String rootPath = Environment.getExternalStorageDirectory().getAbsolutePath(); private String tempPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/temp.apk"; private List<ApplicationInfo> mListAppcations; private PackageManager mPackageManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mPackageManager = getPackageManager(); mListAppcations = mPackageManager.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES); // 判断是否有Root权限 if(RootUtils.isRooted()){ // 根据包名判断软件是否安装// if(isInstall("com.test.testapk")){// // 有安装// Log.i(TAG, "有安装");// }else{// // 没有安装// Log.i(TAG, "没有安装");//// // 从assets目录中获取并安装//// installFromAssets();// } // 本地版本号如果小于服务器端版本号 此部分逻辑参考 // 从网络中下载并安装 installFromNet(); } } /** * 从assets目录中获取并安装 */ public void installFromAssets(){ // 传统安装 并启动// normalInstall(mContext); // 静默升级 并启动 silenceInstall(); } /** * 从网络中下载并安装 */ public void installFromNet(){ // 判断包名是否相同 再判断版本号,如果本地版本号小于服务器端软件版本号就升级 new Thread(new Runnable() { @Override public void run() { // 从Url中下载 下载至 Environment.getExternalStorageDirectory() 目录的update.apk文件 并安装 install(); } }).start(); } /** * 从Url中下载 下载至 Environment.getExternalStorageDirectory() 目录的update.apk文件 并安装 */ private void install() { // 传统安装 并启动// normalInstall(mContext); // 静默安装 并启动 silenceInstall(); } /** * 下载至 Environment.getExternalStorageDirectory().getPath() + "/update.apk" * * @param httpUrl * @return */ private File downLoadFile(String httpUrl , String filePath) { if (TextUtils.isEmpty(httpUrl)) throw new IllegalArgumentException(); File file = new File(filePath); if (!file.exists()) file.mkdirs(); file = new File(filePath + File.separator + "update.apk"); InputStream inputStream = null; FileOutputStream outputStream = null; HttpURLConnection connection = null; try { URL url = new URL(httpUrl); connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(10 * 1000); connection.setReadTimeout(10 * 1000); connection.connect(); inputStream = connection.getInputStream(); outputStream = new FileOutputStream(file); byte[] buffer = new byte[1024]; int len = 0; while ((len = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, len); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (inputStream != null) inputStream.close(); if (outputStream != null) outputStream.close(); if (connection != null) connection.disconnect(); } catch (IOException e) { inputStream = null; outputStream = null; } } return file; } /** * 传统安装 * * @param context */ public void normalInstall(Context context) { // 进行资源的转移 将assets下的文件转移到可读写文件目录下 // installFromAssets() 时取消注释// createFile();// File file = new File(tempPath); // installFromNet() 时取消注释 File file = downLoadFile(updateUrl, rootPath); Intent intent = new Intent(); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setAction(android.content.Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); context.startActivity(intent); startApp(context); } /** * 安装后自启动apk * * @param context */ private static void startApp(Context context) { execRootShellCmd("am start -S " + context.getPackageName() + "/" + MainActivity.class.getCanonicalName() + " \n"); } /** * 执行shell命令 * * @param cmds * @return */ private static boolean execRootShellCmd(String... cmds) { if (cmds == null || cmds.length == 0) { return false; } DataOutputStream dos = null; InputStream dis = null; Process p = null; try { p = Runtime.getRuntime().exec("su");// 经过Root处理的android系统即有su命令 dos = new DataOutputStream(p.getOutputStream()); for (int i = 0; i < cmds.length; i++) { dos.writeBytes(cmds[i] + " \n"); } dos.writeBytes("exit \n"); int code = p.waitFor(); return code == 0; } catch (Exception e) { e.printStackTrace(); } finally { try { if (dos != null) { dos.close(); } } catch (IOException e) { e.printStackTrace(); } try { if (dis != null) { dis.close(); } } catch (Exception e2) { e2.printStackTrace(); } try { if (p != null) { p.destroy(); p = null; } } catch (Exception e3) { e3.printStackTrace(); } } return false; } /** * 静默安装 并启动 * * @return */ public boolean silenceInstall() { // 进行资源的转移 将assets下的文件转移到可读写文件目录下 // installFromAssets() 时取消注释// createFile();// File file = new File(tempPath); // installFromNet() 时取消注释 File file = downLoadFile(updateUrl, rootPath); boolean result = false; Process process = null; OutputStream out = null; Log.i(TAG , "file.getPath():" + file.getPath()); if (file.exists()) { System.out.println(file.getPath() + "=="); try { process = Runtime.getRuntime().exec("su"); out = process.getOutputStream(); DataOutputStream dataOutputStream = new DataOutputStream(out); // 获取文件所有权限 dataOutputStream.writeBytes("chmod 777 " + file.getPath() + "\n"); // 进行静默安装命令 dataOutputStream .writeBytes("LD_LIBRARY_PATH=/vendor/lib:/system/lib pm install -r " + file.getPath()); dataOutputStream.flush(); // 关闭流操作 dataOutputStream.close(); out.close(); int value = process.waitFor(); // 代表成功 if (value == 0) { Log.i(TAG , "安装成功!"); result = true; // 失败 } else if (value == 1) { Log.i(TAG , "安装失败!"); result = false; // 未知情况 } else { Log.i(TAG , "未知情况!"); result = false; } } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } if (!result) { Log.i(TAG , "root权限获取失败,将进行普通安装"); normalInstall(mContext); result = true; } } return result; } /** * 进行资源的转移 将assets下的文件转移到可读写文件目录下 */ public void createFile() { InputStream is = null; FileOutputStream fos = null; try { // 从assets文件夹中获取testapk.apk文件 is = getAssets().open("testapk.apk"); File file = new File(tempPath); file.createNewFile(); fos = new FileOutputStream(file); byte[] temp = new byte[1024]; int i = 0; while ((i = is.read(temp)) > 0) { fos.write(temp, 0, i); } } catch (IOException e) { 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 */ public boolean isInstall(String name){ for (ApplicationInfo info : mListAppcations) { Log.e(TAG, "getPackagename: ---" + info.packageName); // 匹配QQ的包名 如果手机中安装了QQ if (info.packageName.equals(name)) { return true; } } return false; }}----------------------------------------------------------------------------------------// RootUtilsimport android.util.Log;import java.io.BufferedReader;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;public class RootUtils { public static final String TAG = "RootUtils"; @Deprecated public static boolean isRooted() { Process process = null; try { process = Runtime.getRuntime().exec("su"); OutputStream outputStream = process.getOutputStream(); InputStream inputStream = process.getInputStream(); outputStream.write("id\n".getBytes()); outputStream.flush(); outputStream.write("exit\n".getBytes()); outputStream.flush(); process.waitFor(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String s = bufferedReader.readLine(); if (s.contains("uid=0")) return true; } catch (IOException e) { Log.e(TAG, "没有root权限"); } catch (InterruptedException e) { e.printStackTrace(); } finally { if (process != null) process.destroy(); } return false; } public static boolean checkRooted() { boolean result = false; try { result = new File("/system/bin/su").exists() || new File("/system/xbin/su").exists(); } catch (Exception e) { e.printStackTrace(); } return result; }}----------------------------------------------------------------------------------------// UpdateReceiver import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.widget.Toast;/** * 监测是否升级、安装、卸载成功 */public class UpdateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals("android.intent.action.PACKAGE_REPLACED")){ Toast.makeText(context,"升级了一个安装包,重新启动此程序", Toast.LENGTH_SHORT).show();// startApp(context); } // 接收安装广播 if (intent.getAction().equals("android.intent.action.PACKAGE_ADDED")) { String packageName = intent.getDataString(); System.out.println("安装了:" +packageName + "包名的程序");// startApp(context); } //接收卸载广播 if (intent.getAction().equals("android.intent.action.PACKAGE_REMOVED")) { String packageName = intent.getDataString(); System.out.println("卸载了:" + packageName + "包名的程序"); } } /** * 监测到升级后执行app的启动 如果不是silenceInstall()可以注释掉 */ public void startApp(Context context){ // 打开自身 一般用于软件升级// Intent intent = new Intent(context, MainActivity.class);// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);// context.startActivity(intent); // 根据包名打开安装的apk 一般写你新安装的app包名 Intent resolveIntent = context.getPackageManager().getLaunchIntentForPackage("com.test.testapk"); context.startActivity(resolveIntent); }}
Demo下载请移步:http://download.csdn.net/download/zxc514257857/9946274
———因本人才疏学浅,如博客或Demo中有错误的地方请大家随意指出,与大家一起讨论,共同进步,谢谢!———
阅读全文
2 0
- Android中的软件的自动更新(包括静默更新,需Root权限)
- Android Root权限静默安装
- Android Root权限静默安装
- Android Root权限静默安装
- Android中软件的静默安装,安装完成后再自动打开新安装软件(需Root)
- android开发实现静默安装(root权限)
- android开发实现静默安装(root权限)
- android root权限下静默 安装\卸载
- Android静默安装(需root)
- 主从式App实现静默更新及root权限扩展
- Android软件的自动更新
- Android 获取Root权限之后的静默安装实现 代码示例分析&&源码下载
- Android 获取Root权限之后的静默安装实现 代码示例分析
- Android获取Root权限之后的静默安装实现代码示例分析
- Android获取Root权限之后的静默安装实现代码示例分析
- Android 获取Root权限之后的静默安装实现 代码示例分析&&源码下载
- android通过获取root权限实现静默安装、
- android调用安装和静默安装--要root权限
- mysql查看当前使用的数据库---mysql
- 数据库SQL优化大总结之 百万级数据库优化方案
- 连接到外部sql server工具类
- 半驻留高性能线程池例子
- SQL语句判断记录是否存在
- Android中的软件的自动更新(包括静默更新,需Root权限)
- js获取时间(本周、本季度、本月..)
- JVM类文件结构
- tar使用
- kafka学习(一)
- 畅 怎样通过PDF Transformer+复制PDF文档中的内容
- centos7 防火墙
- angularJS on-hold用法
- 不建议使用JPasswordField.getText()