解决android3.0版本以上应用接收不到开机广播问题

现在是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 目录如下

解决android3.0版本以上应用接收不到开机广播问题

首先我们把我们要提升为系统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版本以上应用接收不到开机广播问题,布布扣,bubuko.com

解决android3.0版本以上应用接收不到开机广播问题

上一篇:查看系统的I/O使用iostat命令而使用iotop可以根据I/O统计信息排序,追踪到具体的进程


下一篇:sharepoint 2013 office web app 2013 文档在线浏览 IE11 浏览器不兼容解决方法