前言:公司最近做了一个项目,一套放在路边的广告牌系统,出现问题后不可能实现人工的服务,所以需要从下载到安装到启动都需要系统自己操作,这样就无疑给我带来了巨大的困难,不过俗话说得好:没有困难制造困难也要上,哈哈 好了废话不多说了,直接上代码。
1.文件下载与静默安装
public class UpdateActivity extends AppCompatActivity {
/**
* 0 开始下载
* 1.正在下载
* 2.下载完成
*/
private static final int SHOWDOWNLOADDIALOG = 0;
private static final int UPDATEDOWNLOADDIALOG = 1;
private static final int DOWNLOADFINISHED = 2;
//软件下载地址
private String url = "http://ystel.rrtel.com/garbage/interface.php?action=apkDownload";
//软件下载路径
private String rootPath = Environment.getExternalStorageDirectory() + "/androidsolution/download/";
//软件下载保存的名称
private String fileName = "update.apk";
private DecimalFormat df = new DecimalFormat("#0.00");//设置结果保留两位小数
private ProgressBar pb;
//显示软件下载进度
private TextView textView;
private int fileLength;
//静默安装命令
private String cmd_install = "pm install -r ";
/**
* 安装
*/
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case SHOWDOWNLOADDIALOG://显示正在下载的对话框
pb.setMax(fileLength);
break;
case UPDATEDOWNLOADDIALOG://刷新正在下载对话框的内容
int curSize = (int) msg.obj;
pb.setProgress(curSize);
textView.setText(df.format((float) curSize / (float) fileLength * 100) + "%");
break;
case DOWNLOADFINISHED://下载完成后进行的操作
ToastUtils.showLongToastSafe(UpdateActivity.this, "系统正在重新安装启动,请勿操作");
Log.e("tag", hasRootPerssion() + "");
if (hasRootPerssion()) {
String cmd = cmd_install + rootPath + fileName;
Log.e("tag", "静默安装:" + excuteSuCMD(cmd));
} else {
Log.e("tag", "无权限");
}
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_update);
pb = (ProgressBar) findViewById(R.id.progress);
textView = (TextView) findViewById(R.id.tv);
/**
* 开启一个线程进行软件下载
*/
new Thread(new Runnable() {
@Override
public void run() {
downLoadFile(url);
}
}).start();
}
//执行shell命令
protected int excuteSuCMD(String cmd) {
try {
final Process process = Runtime.getRuntime().exec("su");
DataOutputStream dos = new DataOutputStream(
(OutputStream) process.getOutputStream());
// 部分手机Root之后Library path 丢失,导入library path可解决该问题
dos.writeBytes((String) "export LD_LIBRARY_PATH=/vendor/lib:/system/lib\n");
cmd = String.valueOf(cmd);
dos.writeBytes((String) (cmd + "\n"));
dos.flush();
dos.writeBytes("exit\n");
dos.flush();
return 0;
} catch (Exception localException) {
Log.e("tag", localException.getMessage());
localException.printStackTrace();
return -1;
}
}
/**
* 判断手机是否有root权限
*/
public static boolean hasRootPerssion() {
PrintWriter PrintWriter = null;
Process process = null;
try {
process = Runtime.getRuntime().exec("su");
PrintWriter = new PrintWriter(process.getOutputStream());
PrintWriter.flush();
PrintWriter.close();
return true;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (process != null) {
process.destroy();
}
}
return false;
}
//下载apk程序代码
protected void downLoadFile(String httpUrl) {
// TODO Auto-generated method stub
InputStream inputStream = null;
FileOutputStream fileOutputStream = null;
File tmpFile = new File(rootPath);
if (!tmpFile.exists()) {
tmpFile.mkdir();
}
final File file = new File(rootPath + fileName);
try {
URL url = new URL(httpUrl);
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
if (conn.getResponseCode() == 200) {
//获得文件的长度
fileLength = conn.getContentLength();
inputStream = conn.getInputStream();
fileOutputStream = new FileOutputStream(file);
//子线程不能显示和刷新UI
Message msg = Message.obtain();
msg.what = SHOWDOWNLOADDIALOG;
handler.sendMessage(msg);
byte[] buffer = new byte[1024 * 2];
int length = 0;
while ((length = inputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, length);
int curlength = (int) file.length();
Message updateMsg = Message.obtain();
updateMsg.what = UPDATEDOWNLOADDIALOG;
updateMsg.obj = curlength;
handler.sendMessage(updateMsg);
}
if (file.length() == fileLength) {
//下载完成
Message finishedMsg = Message.obtain();
finishedMsg.what = DOWNLOADFINISHED;
finishedMsg.obj = file;
handler.sendMessage(finishedMsg);
}
} else {
Toast.makeText(UpdateActivity.this, "连接超时", Toast.LENGTH_SHORT)
.show();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
if (fileOutputStream != null) {
fileOutputStream.flush();
fileOutputStream.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (Exception e) {
}
}
}
}
注释:判断软件是否有更新的代码就不贴出来了,这里主要是软件的下载、下载后自动安装。关于自动安装我看了很多资料,不过大多数都是用shell命令来执行的,命令是:pm install -r (文件路径)。但是必须要保证设备已经root,不root的设备是无法静默安装的。
2.安装后自启:新建UpdateBroadcast 继承BroadcastReceiver
public class UpdateBroadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.PACKAGE_REPLACED")) {
String packageName = intent.getDataString();
Log.e("tag", "安装了:" + packageName + "包名的程序");
Intent intent2 = new Intent(context, FirstActivity.class);
intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent2);
}
}
}
在AndroidManifest.xml文件中注册广播
<receiver android:name=".broadcast.UpdateBroadcast">
<intent-filter>
<!-- 软件更新广播 -->
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
注释:这里的思路是用广播监听软件的更新,当有软件更新后可以在onReceive中获取到包名,根据包名判断是不是自己的软件更新了,从而进行重启操作。(最重要的一点是:在服务器上的apk必须有广播监听功能,否则是不能实现更新软件后自启的)
荆轲刺秦王!