前言:公司最近做了一個項目,一套放在路邊的廣告牌系統,出現問題後不可能實現人工的服務,所以需要從下載到安裝到啓動都需要系統自己操作,這樣就無疑給我帶來了巨大的困難,不過俗話說得好:沒有困難製造困難也要上,哈哈 好了廢話不多說了,直接上代碼。
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必須有廣播監聽功能,否則是不能實現更新軟件後自啓的)
荊軻刺秦王!