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
原创粉丝点击