標籤:
如今是2014-07-16 下午15:27.
好久沒寫過東西,突然間靈感噴發想寫點東西(事實上是剛剛弄好了一個棘手的問題,自豪中。。呵呵呵呵 我牛掰)。廢話不多說,進入正題。
不知道你們又沒有碰到這問題,本身做的一個應用,可以監聽開機廣播的。但非常奇怪,在android3.0下面的版本號碼 你怎麼跑都沒問題。可是在android3.0以上的版本號碼就恐怕情況不一樣了。你會發現往往非常多時候接收不到開機廣播。這是為什麼呢?嘿 不告訴你! 說笑的 事實上這方面百度非常多人給出為什麼了。我在這就不多廢話了,今天我們要說的是解決方案。
好了,既然說到解決方案,網上給出的有兩種:
1.加入許可權<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
2.把你寫的app升級成為系統app
先說第一種吧,這樣的呢我試過 在android系統4.0下面的貌似實用。但假設你用android版本號碼4.0以上的機子你就會發現還是老樣子,廣播接收不到。
那好吧,看來也就僅僅剩下另外一種方法咯,呵呵呵 正好 我今天要說的也是另外一種方法。開工!!!!
前提準備:
1.一台已經ROOT成功的機子。什嗎?不知道怎麼root? 別問我 市面上有什麼工具。
2.哦 沒有2了 你僅僅要準備一台已經root成功的機子即可。硬要說點什麼的話,java環境吧 eclipse android環境吧。
基本思路:我本來安裝的app是具有接收開機廣播的許可權的,可是系統卻不發送給我們自己寫的應用,可是細心的你會發現,每次開機或者關機 我們的android手機都是會發送開機廣播的(android.intent.action.BOOT_COMPLETED),僅僅是我們做的app接收不到而已。可是,手機的系統層級的app是一定會接受到的。好了,那麼假設我們有方法把我們做的應用提升層級成為系統級的app,是否意味著一樣能夠接收開機廣播了呢?好,我們來嘗試一下。怎樣?
首先,如果我如今寫了一個Test02.apk
讓我們來看看Test02.apk這項目裡面都做了什麼 先來看一下它的AndroidManifest.xml檔案
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.test02"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19"
android:sharedUserId="android.uid.system"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.test02.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="com.example.test02.BrocatTest">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<category android:name="android.intent.category.HOME"/>
</intent-filter>
</receiver>
</application>
</manifest>
聲明了一個Receiver,用於接收開機廣播,好 那我們來看看這個BrocatTest又做了什麼
public class BrocatTest extends BroadcastReceiver{
@Override
public void onReceive(Context arg0, Intent arg1) {
// TODO Auto-generated method stub
Log.i("BrocatTest", "run here.....");
}
}
嘿嘿。。僅僅是列印一句話而已。。。。。。。。。。。
接下來我們拿到Test02.apk 我們把它放到電腦D盤檔案夾下
開啟cmd 進入adb shell 輸入命令 adb push D:\Test02.apk /sdcard/ 把D盤檔案夾下的Test02.apk拷貝到手機的sdcard卡檔案夾下
adb shell
su
mount
看一下你們手機system目錄是否處於可寫入的狀態(rw),預設是僅僅讀(ro)
假設處於僅僅讀的狀態(ro) 那麼就要把狀態改為可寫的狀態
mount -o remount,rw -t -yaffs2 /dev/block/mtdblock3 /system
mount 再次使用這命令,看狀態是否改變了
假設狀態已經改變了 運行
cat /sdcard/Test02.apk > /system/app/Test02.apk
運行完後Test02.apk已經成功的寫入/system/app目錄了,這個目錄就是專門存放系統app的地方,放心。手機會自己主動幫你安裝的,假設你想確認的話
cd /system/app
ls
看有沒有Test02.apk這個apk包
最後別忘了運行mount -o -remount,ro -t -yaffs2 /dev/block/mtdblock3 /system 把狀態值改回來
exit
exit
接著重新啟動一下手機看看。。你會得到以外的驚喜。
好了到這裡 上面的都是要通過adb shell操作完畢的 那麼有沒有說 我寫在代碼裡的 我直接運行就能夠了呢?
呵呵呵呵 有 立即來。
我們在Eclipse建立一個項目Test06 檔案夾例如以下
首先我們把我們要提升為系統app的apk包放到assets目錄下
接著我們先來看一下 SDCardUtil這個類,這個類的工作就是把位於assets檔案夾下的Test02.apk檔案以流的方式寫入手機的sdcard
/**
* 對於SD卡的操作類
* @author YangMo*/
public class SDCardUtil {
private Context context;
private boolean hasCard = false; //推斷是否存在SD卡
private String sdPath = null;
private String filePath = null;
/**
* 建構函式*/
public SDCardUtil(Context context){
this.context = context;
hasCard = Environment.getExternalStorageState().equals( android.os.Environment.MEDIA_MOUNTED );
try {
sdPath = Environment.getExternalStorageDirectory().getCanonicalPath();
filePath = this.context.getFilesDir().getPath();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("sdPath = "+sdPath);
System.out.println("filePath = "+filePath);
}
public boolean isOk(){
return hasCard;
}
/**
* 將檔案寫入SD卡內*/
public int readStreamToSDCard(InputStream is, String fileName){
int state = -1;
try{
FileOutputStream fos = new FileOutputStream(sdPath +"/" +fileName);
byte[] buffer = new byte[8192];
int count = 0;
while( (count = is.read(buffer)) != -1 ){
fos.write(buffer, 0, count);
}
fos.close();
is.close();
state = 0;
return state;
}catch(Exception e){
e.printStackTrace();
return -1;
}
}
}
我們再來看看RootCmd這個類,這個類基本的工作就是取代我們的adb shell
public final class RootCmd {
// 運行linux命令而且輸出結果
protected static String execRootCmd(String paramString) {
String result = "result : ";
try {
Process localProcess = Runtime.getRuntime().exec("su ");
// 經過Root處理的android系統即有su命令
OutputStream localOutputStream = localProcess.getOutputStream();
DataOutputStream localDataOutputStream = new DataOutputStream(localOutputStream);
InputStream localInputStream = localProcess.getInputStream();
DataInputStream localDataInputStream = new DataInputStream(localInputStream);
String str1 = String.valueOf(paramString);
String str2 = str1 + "\n";
localDataOutputStream.writeBytes(str2);
localDataOutputStream.flush();
String str3 = null;
// while ((str3 = localDataInputStream.readLine()) != null) {
// Log.d("result", str3);
// }
localDataOutputStream.writeBytes("exit\n");
localDataOutputStream.flush();
localProcess.waitFor();
return result;
} catch (Exception localException) {
localException.printStackTrace();
return result;
}
}
// 運行linux命令但不關注結果輸出
protected static int execRootCmdSilent(String paramString) {
try {
Process localProcess = Runtime.getRuntime().exec("su");
Object localObject = localProcess.getOutputStream();
DataOutputStream localDataOutputStream = new DataOutputStream((OutputStream) localObject);
String str = String.valueOf(paramString);
localObject = str + "\n";
localDataOutputStream.writeBytes((String) localObject);
localDataOutputStream.flush();
localDataOutputStream.writeBytes("exit\n");
localDataOutputStream.flush();
localProcess.waitFor();
int result = localProcess.exitValue();
return (Integer) result;
} catch (Exception localException) {
localException.printStackTrace();
return -1;
}
}
// 推斷機器Android是否已經root,即是否擷取root許可權
protected static boolean haveRoot() {
int i = execRootCmdSilent("echo test");
// 通過運行測試命令來檢測
if (i != -1) {
return true;
}
return false;
}
}
接著 我們來看看我們的主Activity大人裡面做了什麼工作
public class MainActivity extends ActionBarActivity{
private static String FILENAME = "Test02.apk";
private int state = -1;
//adb shell命令
String paramString= "adb shell" +"\n"+
"su" +"\n"+
"mount -o remount,rw -t yaffs2 /dev/block/mtdblock3 /system" +"\n"+
"cat /sdcard/Test02.apk > /system/app/Test02.apk" +"\n"+
"mount -o remount,ro -t yaffs2 /dev/block/mtdblock3 /system" +"\n"+
"exit" +"\n"+
"exit";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SDCardUtil sdUtil = new SDCardUtil(this);
try {
if(sdUtil.isOk()){
InputStream is = getApplicationContext().getAssets().open(FILENAME);
state = sdUtil.readStreamToSDCard(is, FILENAME);
}else
Toast.makeText(this, getApplicationContext().getString(R.string.sd_title), Toast.LENGTH_SHORT).show();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(state == 0){//一切準備就緒,進行將應用提升為系統層級應用的操作
if(RootCmd.haveRoot()){
if(RootCmd.execRootCmdSilent(paramString)==-1){
Toast.makeText(this, getApplicationContext().getString(R.string.initialiseOk), Toast.LENGTH_LONG).show();
}else{
Toast.makeText(this, getApplicationContext().getString(R.string.initialiseFail), Toast.LENGTH_LONG).show();
}
}else{
Toast.makeText(this, getApplicationContext().getString(R.string.noRoot), Toast.LENGTH_LONG).show();
}
}
}
}
先看這裡
try {
if(sdUtil.isOk()){
InputStream is = getApplicationContext().getAssets().open(FILENAME);
state = sdUtil.readStreamToSDCard(is, FILENAME);
}else
Toast.makeText(this, getApplicationContext().getString(R.string.sd_title), Toast.LENGTH_SHORT).show();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
這部分的代碼先把apk寫到我們的sd卡裡面
if(state == 0){//一切準備就緒,進行將應用提升為系統層級應用的操作
if(RootCmd.haveRoot()){
if(RootCmd.execRootCmdSilent(paramString)==-1){
Toast.makeText(this, getApplicationContext().getString(R.string.initialiseOk), Toast.LENGTH_LONG).show();
}else{
Toast.makeText(this, getApplicationContext().getString(R.string.initialiseFail), Toast.LENGTH_LONG).show();
}
}else{
Toast.makeText(this, getApplicationContext().getString(R.string.noRoot), Toast.LENGTH_LONG).show();
}
}
而這部分就是取代我們手工的adb shell操作了
注意paramString這個屬性的聲明
//adb shell命令
String paramString= "adb shell" +"\n"+
"su" +"\n"+
"mount -o remount,rw -t yaffs2 /dev/block/mtdblock3 /system" +"\n"+
"cat /sdcard/Test02.apk > /system/app/Test02.apk" +"\n"+
"mount -o remount,ro -t yaffs2 /dev/block/mtdblock3 /system" +"\n"+
"exit" +"\n"+
"exit";
恩恩,好了,串連上你的手機 run一下Test06這個項目看看。。。。嘿嘿~~
解決android3.0版本號碼以上應用接收不到開機廣播問題