WakeLock鎖
wake_lock鎖主要是相對(duì)系統(tǒng)的休眠而言的伙狐,意思就是我的程序給CPU加了這個(gè)鎖那系統(tǒng)就不會(huì)休眠了,這樣做的目的是為了全力配合我們程序的運(yùn)行带饱。有的情況如果不這么做就會(huì)出現(xiàn)一些問(wèn)題邻奠,比如微信等及時(shí)通訊的心跳包會(huì)在熄屏不久后停止網(wǎng)絡(luò)訪問(wèn)等問(wèn)題。所以微信里面是有大量使用到了wake_lock鎖辜妓。枯途。
wake_loc:兩種鎖,一種計(jì)數(shù)鎖籍滴;非計(jì)數(shù)鎖(鎖了很多次酪夷,只需要release一次就可以解除了)。源碼:count++ 孽惰。晚岭。
電量?jī)?yōu)化使用時(shí)出現(xiàn)的錯(cuò)誤:
1.json: unsupported value: NaN 有人用了描述: the problem started when reset the battery stats and enabled full-wake-history 解決:重啟手機(jī)再試就好了。
2.打開(kāi)生成的HTML顯示錯(cuò)誤如下:WARNING: Visualizer disabled. If you see this message, download the HTML then open it.解決:需要翻墻訪問(wèn)谷歌服務(wù)勋功。
3.進(jìn)部署好的動(dòng)腦服務(wù)器docker坦报,顯示錯(cuò)誤如下:{"UploadResponse":[{"sdkVersion":23,"historianV2Cs...https://github.com/google/battery-historian/issues/64解決:You need a network connection.需要翻墻。 動(dòng)腦的centos無(wú)法翻墻訪問(wèn)谷歌服務(wù)導(dǎo)致狂鞋。
4.進(jìn)部署好的動(dòng)腦服務(wù)器docker片择,沒(méi)有顯示錯(cuò)誤,但是最上面提示了紅色的顏色塊骚揍,表示訪問(wèn)出錯(cuò)字管。解決:無(wú)法翻墻訪問(wèn)谷歌服務(wù)導(dǎo)致。
重要的參數(shù):WiFi信不、wake_lock纤掸、conn、mobile_ratio(蜂窩信號(hào))
1.省電
有些工作可以放當(dāng)手機(jī)插上電源的時(shí)候去做浑塞。往往這樣的情況非常多借跪。像這些不需要及時(shí)地和用戶交互的操作可以放到后面處理。比如:360手機(jī)助手酌壕,當(dāng)充上電的時(shí)候掏愁,才會(huì)自動(dòng)清理手機(jī)垃圾歇由,自動(dòng)備份上傳圖片、聯(lián)系人等到云端果港。提問(wèn):拍照和圖片的處理沦泌,他們可以做一些電量的優(yōu)化嗎?假如現(xiàn)在沒(méi)有充電辛掠,電量比較低谢谦,拍照動(dòng)作是需要立馬執(zhí)行的,但是圖片處理(需要消耗大量的計(jì)算---電量的大量消耗)是否可以放在用戶手機(jī)插上電源之后來(lái)處理萝衩?如何立即獲取手機(jī)當(dāng)前充電狀態(tài)回挽,我們可以有針對(duì)性地對(duì)一些代碼做優(yōu)化。
2.wake_lock
系統(tǒng)為了節(jié)省電量猩谊,CPU在沒(méi)有任務(wù)忙的時(shí)候就會(huì)自動(dòng)進(jìn)入休眠千劈。有任務(wù)需要喚醒CPU高效執(zhí)行的時(shí)候,就會(huì)給CPU加wake_lock鎖牌捷。大家經(jīng)常犯的錯(cuò)誤墙牌,我們很容易去喚醒CPU來(lái)干貨,但是很容易忘記釋放wake_lock.解決:powerManager的API暗甥,記得添加權(quán)限:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
mWakelock.acquire();//喚醒CPU
mWakelock.release();//記得釋放CPU鎖//判斷網(wǎng)絡(luò)連接
private boolean isNetWorkConnected() {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return (activeNetworkInfo!=null&&activeNetworkInfo.isConnected());
}
3喜滨、CPU喚醒
大量高頻次的CPU喚醒及操作,我們最好把這些操作集中處理撤防。
我們可以采取一些算法來(lái)解決虽风。
借鑒谷歌的精髓,JobScheduler/GCM
WakeLock鎖使用
后臺(tái)任務(wù) - 保持設(shè)備喚醒狀態(tài)
當(dāng)Android設(shè)備空閑時(shí)即碗,屏幕會(huì)變暗,然后關(guān)閉屏幕陌凳,最后會(huì)停止CPU的運(yùn)行剥懒,這樣可以防止電池電量掉的快。在休眠過(guò)程中自定義的Timer合敦、Handler初橘、Thread、Service等都會(huì)暫停充岛。但有些時(shí)候我們需要改變Android系統(tǒng)默認(rèn)的這種狀態(tài):比如玩游戲時(shí)我們需要保持屏幕常亮保檐,比如一些下載操作不需要屏幕常亮但需要CPU一直運(yùn)行直到任務(wù)完成。
如何保持屏幕常亮######
最好的方式是在Activity中使用FLAG_KEEP_SCREEN_ON 的Flag崔梗。
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
這個(gè)方法的好處是不像喚醒鎖(wake locks)夜只,需要一些特定的權(quán)限(permission)。并且能正確管理不同app之間的切換蒜魄,不用擔(dān)心無(wú)用資源的釋放問(wèn)題扔亥。
另一個(gè)方式是在布局文件中使用android:keepScreenOn屬性:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true">
<!-- ... -->
</RelativeLayout>
android:keepScreenOn = ” true “的作用和FLAG_KEEP_SCREEN_ON一樣场躯。使用代碼的好處是你允許你在需要的地方關(guān)閉屏幕。
注意:一般不需要人為的去掉FLAG_KEEP_SCREEN_ON的flag旅挤,windowManager會(huì)管理好程序進(jìn)入后臺(tái)回到前臺(tái)的的操作踢关。如果確實(shí)需要手動(dòng)清掉常亮的flag,使用getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
如何保持CPU運(yùn)行
方法一 PowerManager
需要使用PowerManager這個(gè)系統(tǒng)服務(wù)的喚醒鎖(wake locks)特征來(lái)保持CPU處于喚醒狀態(tài)粘茄。喚醒鎖允許程序控制宿主設(shè)備的電量狀態(tài)签舞。創(chuàng)建和持有喚醒鎖對(duì)電池的續(xù)航有較大的影響,所以柒瓣,除非是真的需要喚醒鎖完成盡可能短的時(shí)間在后臺(tái)完成的任務(wù)時(shí)才使用它儒搭。比如在Acitivity中就沒(méi)必要用了。如果需要關(guān)閉屏幕嘹朗,使用上述的FLAG_KEEP_SCREEN_ON师妙。
只有一種合理的使用場(chǎng)景,是在使用后臺(tái)服務(wù)在屏幕關(guān)閉情況下hold住CPU完成一些工作屹培。 要使用喚醒鎖默穴,如果不使用喚醒鎖來(lái)執(zhí)行后臺(tái)服務(wù),不能保證因CPU休眠未來(lái)的某個(gè)時(shí)刻任務(wù)會(huì)停止褪秀,這不是我們想要的蓄诽。 (有的人可能認(rèn)為我以前寫(xiě)的后臺(tái)服務(wù)就沒(méi)掉過(guò)鏈子呀運(yùn)行得挺好的,
1.可能是你的任務(wù)時(shí)間比較短媒吗;
2.可能CPU被手機(jī)里面很多其他的軟件一直在喚醒狀態(tài)仑氛。)。
下面是很多網(wǎng)友有同樣的問(wèn)題:
喚醒鎖可劃分為并識(shí)別四種用戶喚醒鎖:
| 標(biāo)記值 | CPU | 屏幕 | 鍵盤(pán)
| ------------- |:-------------:| -----:|
| PARTIAL_WAKE_LOCK| 開(kāi)啟 | 關(guān)閉 | 關(guān)閉
| SCREEN_DIM_WAKE_LOCK| 開(kāi)啟 | 變暗 | 關(guān)閉
| SCREEN_BRIGHT_WAKE_LOCK| 開(kāi)啟 | 變亮 | 關(guān)閉
| FULL_WAKE_LOCK| 開(kāi)啟 | 變亮 | 變亮
請(qǐng)注意闸英,自 API 等級(jí) 17 開(kāi)始锯岖,F(xiàn)ULL_WAKE_LOCK 將被棄用。 應(yīng)用應(yīng)使用 FLAG_KEEP_SCREEN_ON甫何。
第一步就是添加喚醒鎖權(quán)限:
<uses-permission android:name="android.permission.WAKE_LOCK" />
直接使用喚醒鎖:
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"MyWakelockTag");
wakeLock.acquire();
注意:在使用該類(lèi)的時(shí)候出吹,必須保證acquire
和release
是成對(duì)出現(xiàn)的。
方法二 WakefulBroadcastReceiver
但推薦的方式是使用WakefulBroadcastReceiver
:使用廣播和Service(典型的IntentService)結(jié)合的方式可以讓你很好地管理后臺(tái)服務(wù)的生命周期辙喂。
WakefulBroadcastReceiver是BroadcastReceiver的一種特例捶牢。它會(huì)為你的APP創(chuàng)建和管理一個(gè)PARTIAL_WAKE_LOCK
類(lèi)型的WakeLock。一個(gè)WakeBroadcastReceiver接收到廣播后將工作傳遞給Service(一個(gè)典型的IntentService)巍耗,直到確保設(shè)備沒(méi)有休眠秋麸。如果你在交接工作給服務(wù)的時(shí)候沒(méi)有保持喚醒鎖,在工作還沒(méi)完成之前就允許設(shè)備休眠的話炬太,將會(huì)出現(xiàn)一些你不愿意看到的情況
灸蟆。
使用WakefulBroadcastReceiver第一步就是在Manifest中注冊(cè):
<receiver android:name=".MyWakefulReceiver"></receiver>
使用startWakefulService()方法來(lái)啟動(dòng)服務(wù),與startService()相比亲族,在啟動(dòng)服務(wù)的同時(shí)次乓,并啟用了喚醒鎖吓歇。
public class MyWakefulReceiver extends WakefulBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// Start the service, keeping the device awake while the service is
// launching. This is the Intent to deliver to the service.
Intent service = new Intent(context, MyIntentService.class);
startWakefulService(context, service);
}
}
當(dāng)后臺(tái)服務(wù)的任務(wù)完成,要調(diào)用MyWakefulReceiver.completeWakefulIntent()
來(lái)釋放喚醒鎖票腰。
public class MyIntentService extends IntentService {
public static final int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
// Do the work that requires your app to keep the CPU running.
// ...
// Release the wake lock provided by the WakefulBroadcastReceiver.
MyWakefulReceiver.completeWakefulIntent(intent);
}
}
網(wǎng)上采集的一些問(wèn)題坑點(diǎn)及解決如下:
1.向服務(wù)器輪詢的代碼不執(zhí)行
曾經(jīng)做一個(gè)應(yīng)用城看,利用Timer和TimerTask,來(lái)設(shè)置對(duì)服務(wù)器進(jìn)行定時(shí)的輪詢杏慰,但是發(fā)現(xiàn)機(jī)器在某段時(shí)間后测柠,輪詢就不再進(jìn)行了。查了很久才發(fā) 現(xiàn)是休眠造成的缘滥。后來(lái)解決的辦法是轰胁,利用系統(tǒng)的AlarmService來(lái)執(zhí)行輪詢。因?yàn)殡m然系統(tǒng)讓機(jī)器休眠朝扼,節(jié)省電量赃阀,但并不是完全的關(guān)機(jī),系統(tǒng)有一部 分優(yōu)先級(jí)很高的程序還是在執(zhí)行的擎颖,比如鬧鐘榛斯,利用AlarmService可以定時(shí)啟動(dòng)自己的程序,讓cpu啟動(dòng)搂捧,執(zhí)行完畢再休眠驮俗。
2.后臺(tái)長(zhǎng)連接斷開(kāi)
最近遇到的問(wèn)題。利用Socket長(zhǎng)連接實(shí)現(xiàn)QQ類(lèi)似的聊天功能允跑,發(fā)現(xiàn)在屏幕熄滅一段時(shí)間后王凑,Socket就被斷開(kāi)。屏幕開(kāi)啟的時(shí)候需進(jìn)行重連聋丝,但 每次看Log的時(shí)候又發(fā)現(xiàn)網(wǎng)絡(luò)是鏈接的索烹,后來(lái)才發(fā)現(xiàn)是cpu休眠導(dǎo)致鏈接被斷開(kāi),當(dāng)你插上數(shù)據(jù)線看log的時(shí)候弱睦,網(wǎng)絡(luò)cpu恢復(fù)百姓,一看網(wǎng)絡(luò)確實(shí)是鏈接的, 坑每篷。最后使用了PARTIAL_WAKE_LOCK瓣戚,保持CPU不休眠端圈。
3.調(diào)試時(shí)是不會(huì)休眠的
讓我非常郁悶的是焦读,在調(diào)試2的時(shí)候,就發(fā)現(xiàn)舱权,有時(shí)Socket會(huì)斷開(kāi)矗晃,有時(shí)不會(huì)斷開(kāi),后來(lái)才搞明白宴倍,因?yàn)槲矣袝r(shí)是插著數(shù)據(jù)線進(jìn)行調(diào)試张症,有時(shí)拔掉數(shù)據(jù)線仓技,這 時(shí)Android的休眠狀態(tài)是不一樣的。而且不同的機(jī)器也有不同的表現(xiàn)俗他,比如有的機(jī)器脖捻,插著數(shù)據(jù)線就會(huì)充電,有的不會(huì)兆衅,有的機(jī)器的設(shè)置的充電時(shí)屏幕不變暗 等等地沮,把自己都搞暈了。其實(shí)搞明白這個(gè)休眠機(jī)制羡亩,一切都好說(shuō)了摩疑。
采用定時(shí)重復(fù)的Service開(kāi)啟:
1、利用Android自帶的定時(shí)器AlarmManager
實(shí)現(xiàn)
Intent intent = new Intent(mContext, ServiceTest.class);
PendingIntent pi = PendingIntent.getService(mContext, 1, intent, 0);
AlarmManager alarm = (AlarmManager) getSystemService(Service.ALARM_SERVICE);
if(alarm != null)
{
alarm.cancel(pi);
// 鬧鐘在系統(tǒng)睡眠狀態(tài)下會(huì)喚醒系統(tǒng)并執(zhí)行提示功能
alarm.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 1000, 2000, pi);
// 確切的時(shí)間鬧鐘//alarm.setExact(…);
//alarm.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), pi);
}
2畏铆、該定時(shí)器可以啟動(dòng)Service服務(wù)雷袋、發(fā)送廣播、跳轉(zhuǎn)Activity辞居,并且會(huì)在系統(tǒng)睡眠狀態(tài)下喚醒系統(tǒng)楷怒。所以該方法不用獲取電源鎖和釋放電源鎖。
注意:在19以上版本速侈,setRepeating中設(shè)置的頻繁只是建議值(6.0 的源碼中最小值是60s)率寡,如果要精確一些的用setWindow或者setExact。
首先Android手機(jī)有兩個(gè)處理器倚搬,一個(gè)叫Application Processor(AP)冶共,一個(gè)叫Baseband Processor(BP)。AP是ARM架構(gòu)的處理器每界,用于運(yùn)行Linux+Android系統(tǒng)捅僵;BP用于運(yùn)行實(shí)時(shí)操作系統(tǒng)(RTOS),通訊協(xié)議棧運(yùn)行于BP的RTOS之上眨层。非通話時(shí)間庙楚,BP的能耗基本上在5mA左右,而AP只要處于非休眠狀態(tài)趴樱,能耗至少在50mA以上馒闷,執(zhí)行圖形運(yùn)算時(shí)會(huì)更高。另外LCD工作時(shí)功耗在100mA左右叁征,WIFI也在100mA左右纳账。一般手機(jī)待機(jī)時(shí),AP捺疼、LCD疏虫、WIFI均進(jìn)入休眠狀態(tài),這時(shí)Android中應(yīng)用程序的代碼也會(huì)停止執(zhí)行。
Android為了確保應(yīng)用程序中關(guān)鍵代碼的正確執(zhí)行卧秘,提供了Wake Lock的API呢袱,使得應(yīng)用程序有權(quán)限通過(guò)代碼阻止AP進(jìn)入休眠狀態(tài)。但如果不領(lǐng)會(huì)Android設(shè)計(jì)者的意圖而濫用Wake Lock API翅敌,為了自身程序在后臺(tái)的正常工作而長(zhǎng)時(shí)間阻止AP進(jìn)入休眠狀態(tài)羞福,就會(huì)成為待機(jī)電池殺手。比如前段時(shí)間的某應(yīng)用蚯涮,比如現(xiàn)在仍然干著這事的某應(yīng)用坯临。
那么Wake Lock API有啥用呢?
比如心跳包從請(qǐng)求到應(yīng)答恋昼,比如斷線重連重新登陸這些關(guān)鍵邏輯的執(zhí)行過(guò)程看靠,就需要Wake Lock來(lái)保護(hù)。而一旦一個(gè)關(guān)鍵邏輯執(zhí)行成功液肌,就應(yīng)該立即釋放掉Wake Lock了挟炬。兩次心跳請(qǐng)求間隔5到10分鐘,基本不會(huì)怎么耗電嗦哆。除非網(wǎng)絡(luò)不穩(wěn)定谤祖,頻繁斷線重連,那種情況辦法不多老速。
AlarmManager
AlarmManager 是Android 系統(tǒng)封裝的用于管理 RTC 的模塊粥喜,RTC (Real Time Clock) 是一個(gè)獨(dú)立的硬件時(shí)鐘,可以在 CPU 休眠時(shí)正常運(yùn)行橘券,在預(yù)設(shè)的時(shí)間到達(dá)時(shí)额湘,通過(guò)中斷喚醒 CPU。(極光推送就是利用這個(gè)來(lái)做的
)
總結(jié):
- 關(guān)鍵邏輯的執(zhí)行過(guò)程旁舰,就需要Wake Lock來(lái)保護(hù)锋华。如斷線重連重新登陸
- 休眠的情況下如何喚醒來(lái)執(zhí)行任務(wù)?用AlarmManager箭窜。如推送消息的獲取
注意:如果請(qǐng)求網(wǎng)絡(luò)很差毯焕,會(huì)要很長(zhǎng)的時(shí)間,一般我們谷歌建議一定要設(shè)置請(qǐng)求超時(shí)時(shí)間磺樱。
其他參考資料:
alarmManager在手機(jī)休眠時(shí)無(wú)法喚醒Service的問(wèn)題?( 為了對(duì)付你們這些個(gè)“”流氓“”的頻繁喚醒的app纳猫,各個(gè)廠家都開(kāi)發(fā)了心跳對(duì)齊道宅。)
https://www.zhihu.com/question/36421849
微信 Android 版 6.2 為什么設(shè)置了大量長(zhǎng)時(shí)間的隨機(jī)喚醒鎖亲茅?
https://www.zhihu.com/question/31136645
批量任務(wù)優(yōu)化JobService
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.support.v4.content.WakefulBroadcastReceiver;
public class MainActivity extends AppCompatActivity {
TextView wakelock_text;
PowerManager pw;
PowerManager.WakeLock mWakelock;
private ComponentName serviceComponent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
wakelock_text = (TextView) findViewById(R.id.wakelock_text);
pw = (PowerManager) getSystemService(POWER_SERVICE);
mWakelock = pw.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "mywakelock");
serviceComponent = new ComponentName(this,MyJobService.class);
}
public void execut(View view) {
wakelock_text.setText("正在下載....");
// for (int i = 0; i < 500; i++) {
// mWakelock.acquire();//喚醒CPU
// wakelock_text.append(i+"連接中……");
//// wakelock_text.append("");
// //下載
// if (isNetWorkConnected()) {
// new SimpleDownloadTask().execute();
// } else {
// wakelock_text.append("沒(méi)有網(wǎng)絡(luò)連接闽撤。");
// }
// }
//優(yōu)化
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
for (int i = 0; i < 500; i++) {
JobInfo jobInfo = new JobInfo.Builder(i,serviceComponent)
.setMinimumLatency(5000)//5秒 最小延時(shí)醇蝴、
.setOverrideDeadline(60000)//maximum最多執(zhí)行時(shí)間
// .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)//免費(fèi)的網(wǎng)絡(luò)---wifi 藍(lán)牙 USB
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)//任意網(wǎng)絡(luò)---wifi
.build();
jobScheduler.schedule(jobInfo);
}
}
private boolean isNetWorkConnected() {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return (activeNetworkInfo != null && activeNetworkInfo.isConnected());
}
/**
* Uses AsyncTask to create a task away from the main UI thread. This task creates a
* HTTPUrlConnection, and then downloads the contents of the webpage as an InputStream.
* The InputStream is then converted to a String, which is displayed in the UI by the
* onPostExecute() method.
*/
private static final String LOG_TAG = "ricky";
private int index=0;
private class SimpleDownloadTask extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... params) {
try {
// Only display the first 50 characters of the retrieved web page content.
int len = 50;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
// URL url = new URL("https://www.google.com");
URL url = new URL("https://www.baidu.com");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000); // 10 seconds
conn.setConnectTimeout(15000); // 15 seconds
conn.setRequestMethod("GET");
//Starts the query
conn.connect();
int response = conn.getResponseCode();
index++;
Log.d(LOG_TAG, index+"The response is: " + response);
InputStream is = conn.getInputStream();
// Convert the input stream to a string
Reader reader = new InputStreamReader(is, "UTF-8");
char[] buffer = new char[len];
reader.read(buffer);
return new String(buffer);
} catch (IOException e) {
return "Unable to retrieve web page.";
}
}
@Override
protected void onPostExecute(String result) {
wakelock_text.append("\n" + result + "\n");
releaseWakeLock();
}
}
private void releaseWakeLock() {
if (mWakelock.isHeld()) {
mWakelock.release();//記得釋放CPU鎖
wakelock_text.append("釋放鎖!");
}
}
}
public class MyJobService extends JobService {
private static final String LOG_TAG = "MyJobService";
@Override
public void onCreate() {
super.onCreate();
Log.i(LOG_TAG, "MyJobService created");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(LOG_TAG, "MyJobService destroyed");
}
@Override
public boolean onStartJob(JobParameters params) {
// This is where you would implement all of the logic for your job. Note that this runs
// on the main thread, so you will want to use a separate thread for asynchronous work
// (as we demonstrate below to establish a network connection).
// If you use a separate thread, return true to indicate that you need a "reschedule" to
// return to the job at some point in the future to finish processing the work. Otherwise,
// return false when finished.
Log.i(LOG_TAG, "Totally and completely working on job " + params.getJobId());
// First, check the network, and then attempt to connect.
if (isNetworkConnected()) {
new SimpleDownloadTask() .execute(params);
return true;
} else {
Log.i(LOG_TAG, "No connection on job " + params.getJobId() + "; sad face");
}
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
// Called if the job must be stopped before jobFinished() has been called. This may
// happen if the requirements are no longer being met, such as the user no longer
// connecting to WiFi, or the device no longer being idle. Use this callback to resolve
// anything that may cause your application to misbehave from the job being halted.
// Return true if the job should be rescheduled based on the retry criteria specified
// when the job was created or return false to drop the job. Regardless of the value
// returned, your job must stop executing.
Log.i(LOG_TAG, "Whelp, something changed, so I'm calling it on job " + params.getJobId());
return false;
}
/**
* Determines if the device is currently online.
*/
private boolean isNetworkConnected() {
ConnectivityManager connectivityManager =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
return (networkInfo != null && networkInfo.isConnected());
}
/**
* Uses AsyncTask to create a task away from the main UI thread. This task creates a
* HTTPUrlConnection, and then downloads the contents of the webpage as an InputStream.
* The InputStream is then converted to a String, which is logged by the
* onPostExecute() method.
*/
private class SimpleDownloadTask extends AsyncTask<JobParameters, Void, String> {
protected JobParameters mJobParam;
@Override
protected String doInBackground(JobParameters... params) {
// cache system provided job requirements
mJobParam = params[0];
try {
InputStream is = null;
// Only display the first 50 characters of the retrieved web page content.
int len = 50;
URL url = new URL("https://www.google.com");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000); //10sec
conn.setConnectTimeout(15000); //15sec
conn.setRequestMethod("GET");
//Starts the query
conn.connect();
int response = conn.getResponseCode();
Log.d(LOG_TAG, "The response is: " + response);
is = conn.getInputStream();
// Convert the input stream to a string
Reader reader = null;
reader = new InputStreamReader(is, "UTF-8");
char[] buffer = new char[len];
reader.read(buffer);
return new String(buffer);
} catch (IOException e) {
return "Unable to retrieve web page.";
}
}
@Override
protected void onPostExecute(String result) {
jobFinished(mJobParam, false);
Log.i(LOG_TAG, result);
}
}
}