參考
場(chǎng)景
各種以android硬件平臺(tái)為基礎(chǔ)的【公示屏】杭棵、【廣告屏】等等,雖然很少有升級(jí),但是不可避免的會(huì)遇到魂爪,而此類APP的使用場(chǎng)景先舷,一般沒(méi)人會(huì)去幫助你版本更新,點(diǎn)擊安裝滓侍,故而需要:靜默安裝蒋川。
一、普通升級(jí)(可略過(guò))
1撩笆、確認(rèn)安裝包是否存在捺球,并可讀寫(xiě)
2、隱示啟動(dòng):action和data的schema來(lái)控制彈出安裝工具類APP夕冲,然后點(diǎn)擊安裝...
3氮兵、升級(jí)完:BootReceiver 監(jiān)聽(tīng)到Intent.ACTION_PACKAGE_REPLACED,然后自啟動(dòng)
/**
* 普通升級(jí)
* 1歹鱼、確認(rèn)安裝包是否存在泣栈,并可讀寫(xiě)
* 2、隱示啟動(dòng):action和data的schema來(lái)控制彈出安裝工具類APP弥姻,然后點(diǎn)擊安裝...
* 3南片、升級(jí)完:BootReceiver 監(jiān)聽(tīng)到Intent.ACTION_PACKAGE_REPLACED,然后自啟動(dòng)
*
* vivo彈不出升級(jí)安裝框...
* */
private void installApk(String apkPath,String apkName)
{
LogUtil.i("installApk():-----------------");
File apkfile = new File(apkPath, apkName);
if (!apkfile.exists())
{
LogUtil.e("尚無(wú)APK文件");
return;
}
//add permission
String[] command = {"chmod", "777", apkfile.getPath() };
ProcessBuilder builder = new ProcessBuilder(command);
try {
builder.start();
} catch (IOException e) {
LogUtil.e("ProcessBuilder IOException="+e.getMessage());
e.printStackTrace();
}
Intent i = new Intent(Intent.ACTION_VIEW);
//安裝完成自動(dòng)打開(kāi)
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 判斷版本大于等于7.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
i.setDataAndType(FileProvider.getUriForFile(this, APP_File_Provider,apkfile),
"application/vnd.android.package-archive");
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//臨時(shí)權(quán)限
}else{
i.setDataAndType(Uri.parse("file://" + apkfile.toString()),
"application/vnd.android.package-archive");
}
this.startActivity(i);
}
二庭敦、靜默升級(jí)(系統(tǒng)廠家提供)--- 強(qiáng)烈建議
靜默安裝apk接口疼进,無(wú)需開(kāi)放root,也無(wú)需system權(quán)限秧廉。
Intent cmdIntent = new Intent("com.balabalabalbalbalba.syscmd"); //系統(tǒng)廠家給的action命令
cmdIntent.putExtra("cmd", "appinstall");
cmdIntent.putExtra("parm", "xxxx/xxxx");//這里是apk的路徑
sendBroadcast(cmdIntent);
三伞广、靜默升級(jí)+自啟動(dòng)(必須Root,必須Root定血,必須Root!)
1诞外、BootReceiver.java
/**
* 程序卸載澜沟,安裝,更新的廣播監(jiān)聽(tīng)器
* */
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
String packageName = intent.getDataString().substring(8);
LogUtil.d("BootReceiver onReceive():接收到Intent.getAction() = "+intent.getAction()+" , 包名 = "+intent.getDataString());
/**
* 接收安裝廣播
* android.intent.action.PACKAGE_ADDED
* */
if (action.equals(Intent.ACTION_PACKAGE_ADDED)) {
LogUtil.d("BootReceiver onReceive():安裝了:" + packageName + "包名的程序");
}
/**
* 接收卸載廣播
* android.intent.action.PACKAGE_REMOVED
* */
if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
LogUtil.d("BootReceiver onReceive():卸載了:" + packageName + "包名的程序");
}
/**
* 接收更新廣播
* android.intent.action.PACKAGE_REPLACED
* */
if(action.equals(Intent.ACTION_PACKAGE_REPLACED)){
LogUtil.d("BootReceiver onReceive():更新了:" + packageName + "包名的程序峡谊,context.getPackageName()="+context.getPackageName());
//更新的軟件包名是否和我的工程一致
if(packageName.equals(context.getPackageName())){
//更新完后打開(kāi)
Intent it = new Intent();
it.setClassName(packageName, packageName + ".MainActivity"); //啟動(dòng)類
it.setAction("android.intent.action.MAIN"); //首個(gè)啟動(dòng)類action
it.addCategory("android.intent.category.LAUNCHER"); //放入程序列表
//it.addCategory("android.intent.category.HOME"); //作為桌面茫虽,Home鍵打開(kāi),可做啟動(dòng)默認(rèn)程序
it.addCategory("android.intent.category.DEFAULT"); //隱式打開(kāi)既们,如果沒(méi)main可有濒析,如果main可有可無(wú)
it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(it);
}
}
/**
* 接收重啟廣播
* android.intent.action.PACKAGE_REPLACED
* */
if(action.equals(Intent.ACTION_PACKAGE_RESTARTED)){
LogUtil.d("BootReceiver onReceive():重啟了:" + packageName + "包名的程序");
}
/**
* 接收開(kāi)機(jī)廣播
* android.intent.action.BOOT_COMPLETED
* */
if(action.equals(Intent.ACTION_BOOT_COMPLETED)){
LogUtil.d("BootReceiver onReceive():儀器開(kāi)機(jī),開(kāi)啟了:" + packageName + "包名的程序");
}
}
}
<!--BROADCAST AND RECEIVER-->
<receiver
android:name=".BootReceiver"
android:label="@string/app_name"><!-- app open/delete/upgrade/completed receiver -->
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" /> <!-- add -->
<action android:name="android.intent.action.PACKAGE_REPLACED" /> <!-- update -->
<action android:name="android.intent.action.PACKAGE_REMOVED" /> <!-- delete -->
<action android:name="android.intent.action.PACKAGE_RESTARTED" /> <!-- restart -->
<action android:name="android.intent.action.BOOT_COMPLETED" /> <!-- completed -->
<data android:scheme="package" />
</intent-filter>
</receiver>
2啥纸、靜默安裝:主體代碼(這里沒(méi)用thread去線程安裝号杏,可優(yōu)化的部分...)
- 檢查是否有root權(quán)限,沒(méi)有的話授予
- 確認(rèn)安裝包是否存在,并可讀寫(xiě)
- 用Runtime.getRuntime().exec(“su”)后進(jìn)行root執(zhí)行cmd進(jìn)行安裝
- 安裝成功后也考runtime執(zhí)行cmd進(jìn)行啟動(dòng)盾致,BootReceiver會(huì)接收主经,并打開(kāi)應(yīng)用
/**
* 靜默升級(jí):這里是同步的庭惜,正式要考慮異步thread去做這個(gè)事情U肿ぁ!护赊!
* 1惠遏、檢查系統(tǒng)是否有root權(quán)限
* 2、檢查APP是否有root權(quán)限
* 3骏啰、確認(rèn)安裝包是否存在节吮,并可讀寫(xiě)
* 4、root下安裝apk
* 5器一、root下通知開(kāi)啟apk
*
* 注意:
* 在當(dāng)前的activity必要有個(gè)screenOrientation方向课锌,否則的話runtime運(yùn)行會(huì)把詢問(wèn)授予root權(quán)限的框給占用掉
* */
private void sinlenceInstallApk(String apkPath,String apkName){
LogUtil.i("sinlenceInstallApk():-----------------");
//1
if(!SystemCtrlUtil.sysHasRootPerssion()){
LogUtil.e("系統(tǒng)尚無(wú)Root權(quán)限");
Toast.makeText(this,"系統(tǒng)尚無(wú)Root權(quán)限",Toast.LENGTH_SHORT).show();
return;
}
LogUtil.i("系統(tǒng)有Root權(quán)限");
//2
// if(!SystemCtrlUtil.appHasRootPerssion(this)){
// LogUtil.e("APP尚無(wú)Root權(quán)限");
// Toast.makeText(this,"APP尚無(wú)Root權(quán)限",Toast.LENGTH_SHORT).show();
// return;
// }
// LogUtil.i("APP有Root權(quán)限");
//3
File apkfile = new File(apkPath, apkName);
if (!apkfile.exists())
{
LogUtil.e("尚無(wú)APK文件");
return;
}
//4、靜默安裝
if(SystemCtrlUtil.rootSlienceInstallApk(apkPath+"/"+apkName)){
LogUtil.d("靜默安裝成功");
//5祈秕、執(zhí)行此命令后:BootReceiver 監(jiān)聽(tīng)到Intent.ACTION_PACKAGE_REPLACED渺贤,然后自啟動(dòng)
if(SystemCtrlUtil.rootStartApk("com.wujn.sinceupdat.sinceupdate", "MainActivity")){
LogUtil.d("靜默安裝后啟動(dòng)APP成功");
}else{
LogUtil.e("靜默安裝后啟動(dòng)APP失敗G朊V景啊!");
Toast.makeText(this,"靜默安裝后啟動(dòng)APP失敺椒隆9膛铩!仙蚜!",Toast.LENGTH_SHORT).show();
}
}else{
LogUtil.e("靜默安裝失敶酥蕖!N邸呜师!");
Toast.makeText(this,"靜默安裝失敗<纸凇V埂!",Toast.LENGTH_SHORT).show();
}
}
<activity android:name=".MainActivity" android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
3栗涂、工具類
/**
* Created by wujn on 2018/12/13.
* Version : v1.0
* Function: 系統(tǒng)級(jí)別的控制知牌,包括root
*
* 參考:https://blog.csdn.net/andywuchuanlong/article/details/44150317
*/
public class SystemCtrlUtil {
/**
* root下靜默安裝
* */
public static boolean rootSlienceInstallApk(String apkPath){
PrintWriter PrintWriter = null;
Process process = null;
try {
process = Runtime.getRuntime().exec("su");
PrintWriter = new PrintWriter(process.getOutputStream());
PrintWriter.println("chmod 777 "+apkPath);
PrintWriter.println("export LD_LIBRARY_PATH=/vendor/lib:/system/lib");
PrintWriter.println("pm install -r "+apkPath);
// PrintWriter.println("exit");
PrintWriter.flush();
PrintWriter.close();
int value = process.waitFor();
return returnResult(value);
} catch (Exception e) {
e.printStackTrace();
}finally{
if(process!=null){
process.destroy();
}
}
return false;
}
/**
* root下啟動(dòng)apk
* */
public static boolean rootStartApk(String packageName,String activityName){
boolean isSuccess = false;
String cmd = "am start -n " + packageName + "/" + activityName + " \n";
Process process = null;
try {
process = Runtime.getRuntime().exec(cmd);
int value = process.waitFor();
return returnResult(value);
} catch (Exception e) {
e.printStackTrace();
} finally{
if(process!=null){
process.destroy();
}
}
return isSuccess;
}
/**
* root下靜默刪除
* */
public static boolean rootSlienceUninstallApk(String packageName){
PrintWriter PrintWriter = null;
Process process = null;
try {
process = Runtime.getRuntime().exec("su");
PrintWriter = new PrintWriter(process.getOutputStream());
PrintWriter.println("LD_LIBRARY_PATH=/vendor/lib:/system/lib ");
PrintWriter.println("pm uninstall "+packageName);
PrintWriter.flush();
PrintWriter.close();
int value = process.waitFor();
return returnResult(value);
} catch (Exception e) {
e.printStackTrace();
}finally{
if(process!=null){
process.destroy();
}
}
return false;
}
/**
* 判斷手機(jī)是否有root權(quán)限
*/
public static boolean sysHasRootPerssion(){
PrintWriter PrintWriter = null;
Process process = null;
try {
process = Runtime.getRuntime().exec("su");
PrintWriter = new PrintWriter(process.getOutputStream());
PrintWriter.flush();
PrintWriter.close();
int value = process.waitFor();
return returnResult(value);
} catch (Exception e) {
e.printStackTrace();
}finally{
if(process!=null){
process.destroy();
}
}
return false;
}
/**
* root下執(zhí)行cmd的返回值
* */
private static boolean returnResult(int value){
// 代表成功
if (value == 0) {
return true;
} else if (value == 1) { // 失敗
return false;
} else { // 未知情況
return false;
}
}
/**
* 判斷app是否有root權(quán)限
*/
public static boolean appHasRootPerssion(Context context){
return RootCommand("chmod 777 "+context.getPackageCodePath());
}
/**
* 應(yīng)用程序運(yùn)行命令獲取 Root權(quán)限全庸,設(shè)備必須已破解(獲得ROOT權(quán)限)
* @param command 命令:String apkRoot="chmod 777 "+getPackageCodePath(); RootCommand(apkRoot);
* @return 應(yīng)用程序是/否獲取Root權(quán)限
*/
public static boolean RootCommand(String command)
{
Process process = null;
DataOutputStream os = null;
try
{
process = Runtime.getRuntime().exec("su");
os = new DataOutputStream(process.getOutputStream());
os.writeBytes(command + "\n");
os.writeBytes("exit\n");
os.flush();
return returnResult(process.waitFor());
} catch (Exception e)
{
Log.d("*** DEBUG ***", "ROOT REE" + e.getMessage());
return false;
} finally
{
try
{
if (os != null)
{
os.close();
}
process.destroy();
} catch (Exception e)
{
}
}
}
}