電量?jī)?yōu)化wakelock

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í)候出吹,必須保證acquirerelease是成對(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é):
  1. 關(guān)鍵邏輯的執(zhí)行過(guò)程旁舰,就需要Wake Lock來(lái)保護(hù)锋华。如斷線重連重新登陸
  2. 休眠的情況下如何喚醒來(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);
        }
    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末前方,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子娘赴,更是在濱河造成了極大的恐慌憾儒,老刑警劉巖询兴,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異起趾,居然都是意外死亡诗舰,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)训裆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)眶根,“玉大人,你說(shuō)我怎么就攤上這事边琉∈舭伲” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵变姨,是天一觀的道長(zhǎng)族扰。 經(jīng)常有香客問(wèn)我,道長(zhǎng)定欧,這世上最難降的妖魔是什么渔呵? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮砍鸠,結(jié)果婚禮上扩氢,老公的妹妹穿的比我還像新娘。我一直安慰自己爷辱,他們只是感情好录豺,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著饭弓,像睡著了一般巩检。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上示启,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天兢哭,我揣著相機(jī)與錄音,去河邊找鬼夫嗓。 笑死迟螺,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的舍咖。 我是一名探鬼主播矩父,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼排霉!你這毒婦竟也來(lái)了窍株?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎球订,沒(méi)想到半個(gè)月后后裸,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡冒滩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年微驶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片开睡。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡因苹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出篇恒,到底是詐尸還是另有隱情扶檐,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布胁艰,位于F島的核電站蘸秘,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蝗茁。R本人自食惡果不足惜醋虏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望哮翘。 院中可真熱鬧颈嚼,春花似錦、人聲如沸饭寺。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)艰匙。三九已至限煞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間员凝,已是汗流浹背署驻。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留健霹,地道東北人旺上。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像糖埋,于是被迫代替她去往敵國(guó)和親宣吱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容