拓展:
??Android之Service與IntentService的比較
???? ??http://blog.csdn.net/smile3670/article/details/7702521
保證服務不被殺死:
http://blog.csdn.net/mad1989/article/details/22492519? ? (提高優先級,服務死掉的時候發送廣播,重啟服務)
? ??
1.什么是服務
? 就把服務理解為在后臺長期運行但是沒有界面的Activity,因為Service與Activity有很多相似的地方。
? ?1)啟動一個activity或service都要通過Intent
? ?2) 如果想打開一個activity/service,按是否返回數據,需要采用不同的方法。
??■服務的作用
????讓某些邏輯在后臺(長期)執行。
????服務可以結合廣播接收者碰撞出各種效果(看你的想像力了?。?br />
2.進程
Foreground process 前臺進程
?相當于Activity執行了onResume方法 用戶正在操作頁面 前臺進程的優先級最高
Visible process 可視進程
相當于Activity執行了onPasue方法 ?用戶依然能看的見屏幕
Service process 服務進程
相當于通過startservice方式開啟了一個服務 在當前進程里面運行
Background process 后臺進程 ?相當于Activity執行了onStop方法 ?用戶看不見頁面 但是注意Activity沒有執行ondestroy方法?
Empty process 空進程 后臺沒有任何組件運行 這個時候屬于空進程?
3.服務的創建和開啟
? ■服務的創建:
????定義一個類繼承Service,在清單文件里注冊這個類,<service>標簽。
? ? ?
? ■服務的開啟:
? ? ?服務的開啟屬于Context里的方法,所以繼承了Context的類Activity、Service或者是擁有Context對象的類
? ?都可以開啟一個服務(如廣播接收者的onReceive方法里有Context對象,所以廣播接收者也可以開啟一個服務)
? ?開啟服務的方式有2種:startService和bindService。
? ? ?不同的方式開啟的服務,作用不同,服務的生命周期不同,服務需要復寫的方法也不同,掌握
? ?這兩種開啟服務的方式區別十分地重要。(********重點*********)
????如果想要服務長期運行,就用startService方法;如果想調用服務里的方法,就用bindService
? ?方法來開啟服務。
????不同方式開啟服務的生命周期圖:
? ? ??
? ? ?1)startService方式開啟服務
????????★當用戶第一次調用start-service方法 服務會執行onCreate、onStartCommand、onStart方法?
????????★當用戶第二次調用start-service方法 服務只會走onStartCommand、onStart方法?
????????★服務一旦通過start-service方法開啟后 服務就會在后臺長期運行 直到用戶手工停止或調用
? ? ?????? stopService方法停止,或者服務調用自身的stopSelf()方法。如下圖,手動關閉service:
????????
???????★start開啟服務代碼
????????????//啟動服務 ????????????????Intent?intent?=?new?Intent(this,CallService.class); ????????????????startService(intent);
? ??
? ? ?
? ? ?2)bindService方式開啟服務? ?
????★第一次點擊按鈕?通過bindservice開啟服務?服務只會走?onCreate?和?onbind方法
????★第二次點擊按鈕?通過bindservice開啟服務??服務沒有反應?
????★不求同時生?但求同時死??只的是Activity和服務之間,Activity一掛掉,bind方式開啟的服務也會
???? ?隨之掛掉
????★服務只能解綁一次?多次解綁會報異常?
????★通過bindservice方式開啟服務?在設置頁面找不到?他可以理解成是一個隱形的服務??
????★當服務的onbind方法返回null的時候onServiceConnected方法不執行?
??????
????▇bindservice方式調用服務方法里面的流程(**********重點**********)
????????(1)定義一個服務 在清單文件里面配置 ?在服務里面定義一個方法 ?
????????(2)Activity想調用服務里面的方法?
????????(3)在服務的內部定義一個中間人對象(IBinder) 在這個實現類里面可以間接的調用到服務里面的
????????? ?方法
????????(4)在onbind方法中把我們自己定義的這個中間人對象返回?
????????(5)當Activity想調用服務里面方法的時候 ?先通過bindservice方法獲取中間人對象?
????????(6)通過我們獲取的中間人對象就可以間接調用到服務里面的方法了?
????????
? ? ? ? ?一般寫在"中間人"對象(IBinder)里的方法,都是實現接口里的方法,再在方法里調用服
? ? ? ?務里定義的方法。
?????■綁定服務抽取接口
????????接口可以隱藏代碼內部的細節 讓程序員暴露只想暴露的方法
????????實現步驟
????????(1)定義一個接口 把服務里面想暴露方法都定義在接口里 ?
????????(2)我們定義的這個中間人對象實現我們定義的這個接口?
????????(3)還是通過bindservice方式獲取我們中間人的對象
????????(4)還是通過中間人對象間接調用服務里面的方法
?
4.應用1_電話竊聽器案例(startService開啟服務方式)
? ? 需求:手機一接聽電話就把通話進行錄音,保存起來。
? ? 實現思路:電話竊聽,肯定不希望用戶看到,所以不需要界面,那么竊聽錄音的邏輯應寫在服務里。服務有了,
? ? ? ? ? 需要被開啟,為了顯得應用更智能一些,就定義一個廣播接收者來接收開機廣播來開啟服務了。
? ? 具體實現步驟:
? ? 1)定義電話竊聽錄音邏輯的服務類
???? 創建服務類之后,按照好的編程習慣,立馬在清單里配置service標簽。?
public?class?CallService?extends?Service?{ private?MediaRecorder?recorder?=?null; @Override public?IBinder?onBind(Intent?arg0)?{ return?null; } @Override public?void?onCreate()?{ super.onCreate(); //創建一個TelephonyManager對象 //注意要強轉 TelephonyManager?manager?=?(TelephonyManager)?this.getSystemService(this.TELEPHONY_SERVICE); //通過TelephonyManager來獲取通話的狀態?????????????????????/ manager.listen(new?MyPoneListener(),?PhoneStateListener.LISTEN_CALL_STATE); //注意常量是PhoneStateListener的常量。 } //定義一個類繼承PhoneListener private?class?MyPoneListener?extends?PhoneStateListener{ //復寫它的一個監聽通話狀態的方法 @Override public?void?onCallStateChanged(int?state,?String?incomingNumber)?{ //判斷狀態 /*int? CALL_STATE_IDLE? Device?call?state:?No?activity. int? CALL_STATE_OFFHOOK? Device?call?state:?Off-hook. int? CALL_STATE_RINGING? Device?call?state:?Ringing.*/ //代表空閑狀態 if(state?==?TelephonyManager.CALL_STATE_IDLE) { System.out.println("結束錄音"); if(recorder?!=?null) { ?recorder.stop(); ?recorder.reset();???//?You?can?reuse?the?object?by?going?back?to?setAudioSource()?step ?recorder.release();?//?Now?the?object?cannot?be?reused } } //接聽狀態 else?if(state?==?TelephonyManager.CALL_STATE_OFFHOOK) { System.out.println("開始錄音"); if(recorder?!=?null) { recorder.start();???//?Recording?is?now?started } } //響鈴狀態 if(state?==?TelephonyManager.CALL_STATE_RINGING) { System.out.println("準備錄音,創建錄音機。"); recorder?=?new?MediaRecorder(); //設置錄音錄制的是雙方的還是單方的。 recorder.setAudioSource(MediaRecorder.AudioSource.MIC); //設置錄音的保存格式3GP recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); //設置錄音的編碼方式 ?recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); //設置錄音的保存位置? ?recorder.setOutputFile("/mnt/sdcard/record.mp3"); ?try?{ ????//準備錄音? recorder.prepare(); }?catch?(Exception?e)?{ e.printStackTrace(); } } } } }
? ??2)定義廣播接收者來啟動服務
????創建廣播接收者之后,按照好的編程習慣,立馬在清單里配置receiver標簽,并配置好過濾器過濾開機廣播。
public?class?BootReceiver?extends?BroadcastReceiver?{ @Override public?void?onReceive(Context?context,?Intent?intent)?{ //當手機重啟后?開啟服務? Intent?intent1?=?new?Intent(context,CallService.class); context.startService(intent1); } }
? ??
? ??3)添加權限?
????像什么配置組件,添加權限能提前完成的東西就提前完成。
?????<!--?讀取電話狀態權限??--> ????????<uses-permission?android:name="android.permission.READ_PHONE_STATE"/> ???? ?????<!--?錄音權限?--> ????????<uses-permission?android:name="android.permission.RECORD_AUDIO"/> ???? ?????<!--?SD卡讀寫權限?--> ????????<uses-permission?android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> ? ?????<!--?開機啟動監聽?--> ????????<uses-permission?android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
?????
5.應用2_百度音樂盒案例(start/bind混合開啟服務方式)????
? ? 需求:在activity里定義播放、暫停、繼續3個功能按鈕,效果如下圖所示:
???? ?
? ? 實現思路:一般音樂播放軟件,在界面銷毀之后,音樂還能繼續在長期運行播放,所以音樂播放的邏輯應該寫在服
????? ? 務里,用startService方式開啟服務;點擊按鈕還要調用服務里的方法,那么又要用bindService方
????? ? 式開啟服務。所以要以混合模式開啟服務。
? ? 具體實現步驟:
? ? 1)服務相應接口定義
?????//定義一個接口來暴露想暴露的方法???????? ????????public?interface?Iservice?{ ???????? public?abstract?void?callPlay(); ???????? public?abstract?void?callPause(); ???????? public?abstract?void?callRePlay(); ????????}
? ?2)服務定義??
??????package?com.itheima.baidumusic; ????????import?java.io.IOException; ???????? ????????import?android.app.Service; ????????import?android.content.Intent; ????????import?android.media.MediaPlayer; ????????import?android.net.Uri; ????????import?android.os.Binder; ????????import?android.os.IBinder; ???????? ????????/** ?????????*?播放音樂的?Service?邏輯寫在Service里,通過中間人對象返回。 ?????????*? ?????????*?模板步驟:?1.寫一個類繼承Binder,也就是IBinder(接口)的子類?,并實現接口,暴露想暴露的方法。?2.返回這個類的對象?。 ?????????*? ?????????*?@author?LENOVO ?????????*? ?????????*/ ????????public?class?PlayService?extends?Service?{ ???????? private?MediaPlayer?musicPlayer?=?null; ???????? ???????? @Override ???????? public?IBinder?onBind(Intent?intent)?{ ???????? System.out.println("onBind方法執行了。。。。。。"); ???????? musicPlayer?=?new?MediaPlayer(); ???????? try?{ ???????? musicPlayer.setDataSource("/mnt/sdcard/luanhong.mp3"); ???????? }?catch?(Exception?e)?{ ???????? e.printStackTrace(); ???????? } ???????? return?new?MyBinder(); ???????? } ???????? ???????? //?定義播放音樂的方法 ???????? public?void?play()?{ ???????? System.out.println("播放音樂"); ???????? try?{ ???????? musicPlayer.prepare(); ???????? }?catch?(Exception?e)?{ ???????? e.printStackTrace(); ???????? } ???????? musicPlayer.start(); ???????? } ???????? ???????? //?定義暫停音樂的方法 ???????? public?void?pause()?{ ???????? System.out.println("暫停播放"); ???????? musicPlayer.pause(); ???????? } ???????? ???????? //?定義繼續音樂的方法 ???????? public?void?rePlay()?{ ???????? System.out.println("繼續播放"); ???????? musicPlayer.start(); ???????? } ???????? ???????? //?定義一個Binder的子類對象<中間人對象> ???????? public?class?MyBinder?extends?Binder?implements?Iservice?{ ???????? @Override ???????? public?void?callPlay()?{ ???????? play(); ???????? } ???????? ???????? @Override ???????? public?void?callPause()?{ ???????? pause(); ???????? } ???????? ???????? @Override ???????? public?void?callRePlay()?{ ???????? rePlay(); ???????? } ???????? ???????? } ????????}
? ?3)Activity里啟動服務
??????public?class?MainActivity?extends?Activity?{ //?定義與服務的連接 private?MyConn?conn?=?null; //?自定義那個類才會具有獨有的播放音樂的功能,直接聲明那個類實現的接口,屬于多態。 private?Iservice?serviceBinder?=?null; @Override protected?void?onCreate(Bundle?savedInstanceState)?{ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //?1.通過startService方法開啟服務?為了讓音樂盒可以長期的運行,即按下回退鍵服務不會隨之一起銷毀。 Intent?intent?=?new?Intent(this,?PlayService.class); startService(intent); conn?=?new?MyConn(); //?2.通過bindService方法開啟服務?為了調用服務里的方法 bindService(intent,?conn,?BIND_AUTO_CREATE); //?3.在activity里的onDestroy方法里注銷綁定服務。 } //?定義一個類實現ServiceConnection接口 private?class?MyConn?implements?ServiceConnection?{ @Override public?void?onServiceConnected(ComponentName?name,?IBinder?service)?{ //?注意要最終是要用Iservice的特有方法,所以要強轉成Iservice. System.out.println("綁定服務成功"); serviceBinder?=?(Iservice)?service; } @Override public?void?onServiceDisconnected(ComponentName?name)?{ System.out.println("綁定服務失敗"); } } //?實現點擊按鈕的方法 public?void?click(View?v)?{ switch?(v.getId())?{ case?R.id.bt_play: serviceBinder.callPlay(); break; case?R.id.bt_pause: serviceBinder.callPause(); break; case?R.id.bt_rePlay: serviceBinder.callRePlay(); break; default: break; } } //?在activity里的onDestroy方法里注銷綁定服務 @Override protected?void?onDestroy()?{ //?解除綁定服務 unbindService(conn); super.onDestroy(); } }
??????
6.AIDL
Android?Interface?Definition?Language??Android接口定義語言
本地服務:??運行在自己應用(Android)里面的服務
遠程服務?:?運行在其他應用(Android)里面的服務
作用: ?想解決的問題就是進程間通信,也就是調用其它進程里的服務里的方法。
? AIDL 實現的步驟?
????(1) 在一個應用里面定義一個服務 服務里面有一個方法 這個方法稱為遠程服務里面的方法
????(2)在這個服務里面定義中間人對象 ?定義接口iservice.java 把想暴露的方法定義在接口里
????(3)把iservice.java文件改成 aidl文件 ?注意aidl不支持public、abstract等修飾符
????(4)系統會自動給我們生產一個iservice.java文件 stub extends IBinder imp iservie接口?
????(5)把我們定義的中間人對象直接繼承Stub
????(6)我想在另外一個應用里面去調用這個服務里面的方法 要保證2個應用使用的是同一個aidl文件
????(7)如何保證2 個應用使用的是同一個aidl文件谷歌要求 ? 包名相同?
????(8)還是通過bindservice 方式去獲取到中間人對象?
????(9)注意獲取我們定義的中間人對象的方式不一樣了,在服務連接對象ServiceConnection里的onServiceConnected方法里通過
????????????????????stub 的一個靜態方法去獲取我們定義的中間人對象?Stub.asinterface(Ibinder obj);
????
AIDL的應用場景:支付寶
▼用AIDL模擬調用支付寶服務里的服務方法: ??
? ?第1步: ?建立兩個應用
??????
??第2步: ?模擬支付寶服務(實際上支付寶是很復雜的) ?
????★先定義AIDL(相當于接口)?
?????定義AIDL之后,程序會自動在gen目錄下生成相同包名相同文件名的java文件,可以看到Java
????文件中有一抽象類Stub,既繼承了Binder類又實現了IService接口。這就是為什么下面服
????務“中間人”是直接繼承Stub的原因。
???
????★定義支付寶服務
???? ?注意要為服務配一個過濾器,指定一個action,因為支付寶服務要被另外一個應用所調用,
????要用到隱式意圖,那么就必須配置一個過濾器。
?????????????<service?android:name="com.itheima.service.PayService"?> ????????????????<intent-filter> ????????????????????<action?android:name="com.itheima.MY_ALI_PAY"?/> ????????????????</intent-filter> ?????????????</service>
????? 定義支付寶服務
????/** ?????*?支付寶服務 ?????*/ ????public?class?PayService?extends?Service?{ ???? ???? @Override ???? public?IBinder?onBind(Intent?intent)?{ ???? //?TODO?Auto-generated?method?stub ???? return?new?MyBinder(); ???? } ???? ???? //定義一個支付的方法 ???? public?boolean?Pay(String?username,String?password,double?money) ???? { ???? //邏輯 ???? System.out.println("密碼加密。。。。。。。。。"); ???? System.out.println("檢查手機有沒有病毒。。。。。。。。。"); ???? System.out.println("判斷用戶名和密碼。。。。。。。。"); ???? System.out.println("......"); ???? ???? if(!(username.equals("root")?&&?password.equals("1234"))) ???? { ????// Toast.makeText(getApplicationContext(),"密碼錯誤.....",?0).show(); ???? System.out.println("sdggsgggs"); ???? return?false; ???? } ???? if(money?<?4000) ???? { ????// Toast.makeText(getApplicationContext(),"豆子數不足4000.....",?0).show(); ???? System.out.println("QQQQQQQQQQQ"); ???? return?false; ???? } ???? ???? return?true; ???? } ???? ???? ???? //定義一個中間人對象與調用本地服務不一樣,直接繼承Stub類就可以了。 ???? private?class?MyBinder?extends?Stub ???? { ???? ???? @Override ???? public?boolean?callPay(String?username,?String?password,?double?money) ???? throws?RemoteException?{ ???? return?Pay(username,?password,?money); ???? } ???? } ????}
????第3步: ?在另外一個應用里調用支付寶服務
??????★?首先,將支付寶服務應用的aidl拷貝過來,并且包名要保持一致。
??????★?Activity調用支付寶服務的代碼:
public?class?MainActivity?extends?Activity?{? ????????//連接遠程服務的連接對象??? ???? private?MyConn?conn?=?null; ???? ???? //怎么才能將其它應用的IService得到呢,使用AIDL技術。 ???? private?IService?serviceBinder?=?null; ???? ????????@Override ????????protected?void?onCreate(Bundle?savedInstanceState)?{ ????????????super.onCreate(savedInstanceState); ????????????setContentView(R.layout.activity_main); ????????????//綁定服務-----------------注意這個邏輯要寫在onCreate方法里,因為它需要一定的時間。 ???????? //由于是跨應用訪問,要使用隱式意圖。 ???????? Intent?intent?=?new?Intent(); ???????? intent.setAction("com.itheima.MY_ALI_PAY"); ???????? ???????? conn?=?new?MyConn(); ???????? bindService(intent,?conn,?BIND_AUTO_CREATE); ????????} ???????? ????????public?void?click(View?v) ????????{ ???????? try?{ ???? boolean?flag?=?serviceBinder.callPay("root",?"1234",?5000); //空指針,沒有連接上。。。 ???? if(flag) ???? { ???? System.out.println("支付成功"); ???? } ???? }?catch?(RemoteException?e)?{ ???? e.printStackTrace(); ???? } ???????? ????????} ???????? ????????//定義一個類實現ServiceConnection接口 ????????public?class?MyConn?implements?ServiceConnection ????????{ ???? ???? @Override ???? public?void?onServiceConnected(ComponentName?name,?IBinder?service)?{ ???? System.out.println("服務連接上了。。。。"); ???? serviceBinder?=?Stub.asInterface(service); ???? } ???? ???? @Override ???? public?void?onServiceDisconnected(ComponentName?name)?{ ???? System.out.println("服務連接失敗。。。。"); ???? } ???????? ????????} ???????? ????????//解除綁定Service ????????@Override ????????protected?void?onDestroy()?{ ???????? unbindService(conn); ???????? super.onDestroy(); ????????} ????}
?
? ? ? 實際開發中遇到的問題:
? ? ?1.https://blog.csdn.net/sparklebirdie/article/details/50481343? ? ?(startCommand中的intent為null)
?????
通知欄的使用
1)自定義:https://blog.csdn.net/baidujiangwei18/article/details/51347601
2)RemoteView異常:https://blog.csdn.net/qq_38320839/article/details/80402865
?????????其實RemoteView源碼注釋寫的很清楚,限定了它的布局的View的種類。
????? 3)9.0通知適配
?????????一般的創建通知欄的方法,發現在android9.0手機上不顯示。
? ? ? ? 》》創建Notification要增加渠道:
????????????? ?https://blog.csdn.net/zj2576688626/article/details/95340701
????????????? ?https://blog.csdn.net/qq_31392539/article/details/93799917
????????????????https://blog.csdn.net/big_sea_m/article/details/83824323
????????》》開啟通知權限:(引導用戶到設置里開啟)
????????????? ?http://www.manongjc.com/article/34177.html?(推薦)
????? ?????????https://www.jianshu.com/p/ddd2c0edbba9
? ?? ??
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。