您好,登錄后才能下訂單哦!
拓展:
??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,按是否返回數據,需要采用不同的方法。
??■服務的作用
????讓某些邏輯在后臺(長期)執行。
????服務可以結合廣播接收者碰撞出各種效果(看你的想像力了!)
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進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。