Android 保持Service不被Kill掉的方法--雙Service守護 Android實現雙進程守護

本文分為兩個部分,第一部分為雙Service守護,第二部分為雙進程守護

第一部分:

一、Service簡介:
Java.lang.Object

???Android.content.Context

???????android.content.ContextWrapper

???????????android.app.Service

Service是應用程序Application的一個組件(component)。
它的作用有兩點:1.用來提供一個長期在后臺運行并且不與用戶交互的操作,2.也可以為其他應用程序提供服務。
Service必須和其他四大組件一樣,使用<service>標簽在AndroidManifest.xml中進行聲明。
啟動service有兩種方式Context.startService() 和 Context.bindService()。

注意,除了特別指定外,service并不是單獨的進程,一般service在其宿主進程的主線程(UI Thread)中運行【當然也可以在新的線程中startService,這樣Service就不是在MainThread了】。這意味著,如果您的服務要做任何 耗時(如 MP3 播放) 或阻塞 (比如網絡) 操作,它應該產生它自己的線程,用來做那項工作。(service不是單獨的進程也不是單獨的線程)

Service提供了兩大功能:
Context.startService()用來在后臺啟動一個服務;
Context.bindService()用來綁定其他服務,以此來獲取其他service提供的服務;

?

本地服務 Local Service?用于應用程序內部

它可以啟動并運行,直至有人停止了它或它自己停止。在這種方式下,它以調用Context.startService()啟動,而以調用Context.stopService()結束。它可以調用Service.stopSelf() 或 Service.stopSelfResult()來自己停止。不論調用了多少次startService()方法,你只需要調用一次stopService()來停止服務。

【用于實現應用程序自己的一些耗時任務,比如查詢升級信息,并不占用應用程序比如Activity所屬線程,而是單開線程后臺執行,這樣用戶體驗比較好】

?


遠程服務 Remote Service?用于android系統內部的應用程序之間

它可以通過自己定義并暴露出來的接口進行程序操作。客戶端建立一個到服務對象的連接,并通過那個連接來調用服務。連接以調用Context.bindService()方法建立,以調用 Context.unbindService()關閉。多個客戶端可以綁定至同一個服務。如果服務此時還沒有加載,bindService()會先加載它。

【可被其他應用程序復用,比如天氣預報服務,其他應用程序不需要再寫這樣的服務,調用已有的即可】

?

?

二、Service運行方式和生命周期圖:

以startService()啟動服務,系統將通過傳入的Intent在底層搜索相關符合Intent里面信息的service。如果服務沒有啟動則先運行onCreate,然后運行onStartCommand (可在里面處理啟動時傳過來的Intent和其他參數),直到明顯調用stopService或者stopSelf才將停止Service。無論運行startService多少次,只要調用一次stopService或者stopSelf,Service都會停止。使用stopSelf(int)方法可以保證在處理好intent后再停止。onStartCommand ,在2.0后被引入用于service的啟動函數,2.0之前為public void onStart(Intent intent, int startId) 。


以bindService()方法啟用服務,調用者與服務綁定在了一起,調用者一旦退出,服務也就終止。onBind()只有采用Context.bindService()方法啟動服務時才會回調該方法。該方法在調用者與服務綁定時被調用,當調用者與服務已經綁定,多次調用Context.bindService()方法并不會導致該方法被多次調用。采用Context.bindService()方法啟動服務時只能調用onUnbind()方法解除調用者與服務解除,服務結束時會調用onDestroy()方法。

?

(注意這個新老API的改變)


void onStart(Intent intent, int startId)
This method was deprecated????? in API level 5.??? Implement onStartCommand(Intent, int, int) instead.

?

int onStartCommand(Intent intent, int flags, int startId)
Called by the system every time a client explicitly starts the service by calling? startService(Intent), providing the arguments it supplied and a? unique integer token representing the start request.

?

三、Service的優先級

官方文檔告訴我們,Android系統會盡量保持擁有service的進程運行,只要在該service已經被啟動(start)或者客戶端連接(bindService)到它。當內存不足時,需要保持,擁有service的進程具有較高的優先級。

1. 如果service正在調用onCreate,onStartCommand或者onDestory方法,那么用于當前service的進程則變為前臺進程以避免被killed。
2. 如果當前service已經被啟動(start),擁有它的進程則比那些用戶可見的進程優先級低一些,但是比那些不可見的進程更重要,這就意味著service一般不會被killed.
3. 如果客戶端已經連接到service (bindService),那么擁有Service的進程則擁有最高的優先級,可以認為service是可見的。
4. 如果service可以使用startForeground(int, Notification)方法來將service設置為前臺狀態,那么系統就認為是對用戶可見的,并不會在內存不足時killed。
5. 如果有其他的應用組件作為Service,Activity等運行在相同的進程中,那么將會增加該進程的重要性。

?

四、保持service不被kill掉

方法一:

START_STICKY is used for services that are explicitly started and stopped as needed, while START_NOT_STICKY or START_REDELIVER_INTENT are used for services that should only remain running while processing any commands sent to them

onStartCommand方法幾個返回值簡介:
?
1、START_STICKY
?
在運行onStartCommand后service進程被kill后,那將保留在開始狀態,但是不保留那些傳入的intent。不久后service就會再次嘗試重新創建,因為保留在開始狀態,在創建???? service后將保證調用onstartCommand。如果沒有傳遞任何開始命令給service,那將獲取到null的intent。
?
2、START_NOT_STICKY
?
在運行onStartCommand后service進程被kill后,并且沒有新的intent傳遞給它。Service將移出開始狀態,并且直到新的明顯的方法(startService)調用才重新創建。因為如果沒有傳遞任何未決定的intent那么service是不會啟動,也就是期間onstartCommand不會接收到任何null的intent。
?
3、START_REDELIVER_INTENT
?
在運行onStartCommand后service進程被kill后,系統將會再次啟動service,并傳入最后一個intent給onstartCommand。直到調用stopSelf(int)才停止傳遞intent。如果在被kill后還有未處理好的intent,那被kill后服務還是會自動啟動。因此onstartCommand不會接收到任何null的intent。

    @Overridepublic int onStartCommand(Intent intent, int flags, int startId) {flags = START_STICKY;return super.onStartCommand(intent, flags, startId);}

【結論】 手動返回START_STICKY,親測當service因內存不足被kill,當內存又有的時候,service又被重新創建,比較不錯,但是不能保證任何情況下都被重建,比如進程被干掉了....

方法二:

提升service優先級

?在AndroidManifest.xml文件中對于intent-filter可以通過android:priority = "1000"這個屬性設置最高優先級,1000是最高值,如果數字越小則優先級越低,同時適用于廣播。

        <serviceandroid:name="com.dbjtech.acbxt.waiqin.UploadService"android:enabled="true" ><intent-filter android:priority="1000" ><action android:name="com.dbjtech.myservice" /></intent-filter></service>

【結論】目前看來,priority這個屬性貌似只適用于broadcast,對于Service來說可能無效

方法三:

提升service進程優先級

Android中的進程是托管的,當系統進程空間緊張的時候,會依照優先級自動進行進程的回收。Android將進程分為6個等級,它們按優先級順序由高到低依次是:

?? 1.前臺進程( FOREGROUND_APP)
?? 2.可視進程(VISIBLE_APP )
?? 3. 次要服務進程(SECONDARY_SERVER )
?? 4.后臺進程 (HIDDEN_APP)
?? 5.內容供應節點(CONTENT_PROVIDER)
?? 6.空進程(EMPTY_APP)

當service運行在低內存的環境時,將會kill掉一些存在的進程。因此進程的優先級將會很重要,可以使用startForeground將service放到前臺狀態。這樣在低內存時被kill的幾率會低一些。

在onStartCommand方法內添加如下代碼:

         Notification notification = new Notification(R.drawable.ic_launcher,getString(R.string.app_name), System.currentTimeMillis());PendingIntent pendingintent = PendingIntent.getActivity(this, 0,new Intent(this, AppMain.class), 0);notification.setLatestEventInfo(this, "uploadservice", "請保持程序在后臺運行", pendingintent);startForeground(0x111, notification);

注意在onDestroy里還需要stopForeground(true),運行時在下拉列表會看到自己的APP在:

【結論】如果在極度極度低內存的壓力下,該service還是會被kill掉,并且不一定會restart?

保持Service不被Kill掉的方法--雙Service守護,代碼如下:

AndroidManifest.xml:

    <activityandroid:name=".MainActivity"android:label="@string/app_name" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><serviceandroid:name="ServiceOne"android:process=":remote" ><intent-filter><action android:name="com.example.servicedemo.ServiceOne" /></intent-filter></service><serviceandroid:name="ServiceTwo"android:process=":remote" ><intent-filter><action android:name="com.example.servicedemo.ServiceTwo" /></intent-filter></service>

MainActivity.java:

package com.example.servicedemo;import java.util.ArrayList;import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Intent serviceOne = new Intent();serviceOne.setClass(MainActivity.this, ServiceOne.class);startService(serviceOne);Intent serviceTwo = new Intent();serviceTwo.setClass(MainActivity.this, ServiceTwo.class);startService(serviceTwo);}public static boolean isServiceWorked(Context context, String serviceName) {ActivityManager myManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);ArrayList<RunningServiceInfo> runningService = (ArrayList<RunningServiceInfo>) myManager.getRunningServices(Integer.MAX_VALUE);for (int i = 0; i < runningService.size(); i++) {if (runningService.get(i).service.getClassName().toString().equals(serviceName)) {return true;}}return false;}
}

ServiceOne.java:

package com.example.servicedemo;import java.util.Timer;
import java.util.TimerTask;import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;public class ServiceOne extends Service {public final static String TAG = "com.example.servicedemo.ServiceOne";@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.e(TAG, "onStartCommand");thread.start();return START_STICKY;}Thread thread = new Thread(new Runnable() {@Overridepublic void run() {Timer timer = new Timer();TimerTask task = new TimerTask() {@Overridepublic void run() {Log.e(TAG, "ServiceOne Run: "+System.currentTimeMillis());boolean b = MainActivity.isServiceWorked(ServiceOne.this, "com.example.servicedemo.ServiceTwo");if(!b) {Intent service = new Intent(ServiceOne.this, ServiceTwo.class);startService(service);Log.e(TAG, "Start ServiceTwo");}}};timer.schedule(task, 0, 1000);}});@Overridepublic IBinder onBind(Intent arg0) {return null;}}

ServiceTwo.java:

package com.example.servicedemo;import java.util.Timer;
import java.util.TimerTask;import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;public class ServiceTwo extends Service {public final static String TAG = "com.example.servicedemo.ServiceTwo";@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.e(TAG, "onStartCommand");thread.start();return START_REDELIVER_INTENT;}Thread thread = new Thread(new Runnable() {@Overridepublic void run() {Timer timer = new Timer();TimerTask task = new TimerTask() {@Overridepublic void run() {Log.e(TAG, "ServiceTwo Run: " + System.currentTimeMillis());boolean b = MainActivity.isServiceWorked(ServiceTwo.this, "com.example.servicedemo.ServiceOne");if(!b) {Intent service = new Intent(ServiceTwo.this, ServiceOne.class);startService(service);}}};timer.schedule(task, 0, 1000);}});@Overridepublic IBinder onBind(Intent arg0) {return null;}}

第二部分:

做過android開發的人應該都知道應用會在系統資源匱乏的情況下被系統殺死!當后臺的應用被系統回收之后,如何重新恢復它呢?網上對此問題有很多的討論。這里先總結一下網上流傳的各種解決方案,看看這些辦法是不是真的可行。
1.提高優先級
這個辦法對普通應用而言,應該只是降低了應用被殺死的概率,但是如果真的被系統回收了,還是無法讓應用自動重新啟動!
2.讓service.onStartCommand返回START_STICKY
通過實驗發現,如果在adb shell當中kill掉進程模擬應用被意外殺死的情況(或者用360手機衛士進行清理操作),如果服務的onStartCommand返回START_STICKY,在eclipse的進程管理器中會發現過一小會后被殺死的進程的確又會出現在任務管理器中,貌似這是一個可行的辦法。但是如果在系統設置的App管理中選擇強行關閉應用,這時候會發現即使onStartCommand返回了START_STICKY,應用還是沒能重新啟動起來!

3.android:persistent="true"
網上還提出了設置這個屬性的辦法,通過實驗發現即使設置了這個屬性,應用程序被kill之后還是不能重新啟動起來的!

4.讓應用成為系統應用
實驗發現即使成為系統應用,被殺死之后也不能自動重新啟動。但是如果對一個系統應用設置了persistent="true",情況就不一樣了。實驗表明對一個設置了persistent屬性的系統應用,即使kill掉會立刻重啟。一個設置了persistent="true"的系統應用,在android中具有core service優先級,這種優先級的應用對系統的low memory killer是免疫的!

OK,說了半天,只有core service優先級的應用才能保證在被意外殺死之后做到立刻滿血復活。而普通應用要想成為系統應用就必須要用目標機器的簽名文件進行簽名,但這樣又造成了應用無法保證兼容所有不同廠商的產品。那么該怎么辦呢?這里就來說一說雙進程守護。網上也有人提到過雙進程守護的辦法,但是很少能搜索到類似的源碼!如果從進程管理器重觀察會發現新浪微博或者360衛視都有兩個相關的進程,其中一個就是守護進程,由此可以猜到這些商業級的軟件也采用了雙進程守護的辦法。

什么是雙進程守護呢?顧名思義就是兩個進程互相監視對方,發現對方掛掉就立刻重啟!不知道應該把這樣的一對進程是叫做相依為命呢還是難兄難弟好呢,但總之,雙進程守護的確是一個解決問題的辦法!相信說到這里,很多人已經迫切的想知道如何實現雙進程守護了。這篇文章就介紹一個用NDK來實現雙進程保護的辦法,不過首先說明一點,下面要介紹的方法中,會損失不少的效率,反應到現實中就是會使手機的耗電量變大!但是這篇文章僅僅是拋磚引玉,相信看完之后會有更多高人指點出更妙的實現辦法。

需要了解些什么?
這篇文章中實現雙進程保護的方法基本上是純的NDK開發,或者說全部是用C++來實現的,需要雙進程保護的程序,只需要在程序的任何地方調用一下JAVA接口即可。下面幾個知識點是需要了解的:
1.Linux中多進程;
2.unix domain套接字實現跨進程通信;
3.linux的信號處理;
4.exec函數族的用法;

其實這些東西本身并不是多復雜的技術,只是我們把他們組合起來實現了一個雙進程守護而已,沒有想象中那么神秘!在正式貼出代碼之前,先來說說幾個實現雙進程守護時的關鍵點:
1.父進程如何監視到子進程(監視進程)的死亡?
很簡單,在linux中,子進程被終止時,會向父進程發送SIG_CHLD信號,于是我們可以安裝信號處理函數,并在此信號處理函數中重新啟動創建監視進程;
2.子進程(監視進程)如何監視到父進程死亡?
當父進程死亡以后,子進程就成為了孤兒進程由Init進程領養,于是我們可以在一個循環中讀取子進程的父進程PID,當變為1就說明其父進程已經死亡,于是可以重啟父進程。這里因為采用了循環,所以就引出了之前提到的耗電量的問題。
3.父子進程間的通信
有一種辦法是父子進程間建立通信通道,然后通過監視此通道來感知對方的存在,這樣不會存在之前提到的耗電量的問題,在本文的實現中,為了簡單,還是采用了輪詢父進程PID的辦法,但是還是留出了父子進程的通信通道,雖然暫時沒有用到,但可備不時之需!

騰訊的面試官問我:應用程序死了如何恢復?確實,雙進程守護只能做到進程被殺死后重新啟動,但是重啟后如何恢復到之前的狀態這是一個問題。因為進程被意外殺死的情況,onSaveInstance是來不及執行的,所以程序的狀態沒法保存!對于雙進程守護來說,不知道是不是可以再父進程進入后臺以后(onStop),把數據收集起來保存到子進程中,然后父進程重啟以后從子進程中取出這些信息呢?這是一個辦法,但是上面說明的雙進程守護程序的實現中還做不到,因為父進程重啟以后,子進程也掛掉重新建立了,要想實現優雅的恢復,還得在做出點改進才是!只能實時保存數據到數據庫等。

參考:

Android實現雙進程守護 - 天山折梅 - 博客頻道 - CSDN.NET
http://blog.csdn.net/ztemt_sw2/article/details/27101681

轉載于:https://www.cnblogs.com/zhujiabin/p/6073529.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/255960.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/255960.shtml
英文地址,請注明出處:http://en.pswp.cn/news/255960.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【mmdetection2.0錯誤】——ModuleNotFoundError: No module named ‘mmdet‘

一開始以為是安裝包導入的相對路徑的問題&#xff0c;結果鼓搗了一上午都沒有用&#xff0c;最后才發現再進行mmdet2.0環境配置的時候忘記編譯了 也就是如下語句&#xff1a; python setup.py develop

聊聊分布式事務

事務就是一個會話過程中&#xff0c;對上下文的影響是一致的&#xff0c;要么所有的更改都做了&#xff0c;要么所有的更變都撤銷掉。就要么生&#xff0c;要么死。沒有半死不死的中間不可預期狀態。參考下薛定諤的貓。 事務是為了保障業務數據的完整性和準確性的。分布式事務&…

PLSQL DBMS_DDL.ANALYZE_OBJECT

http://space.itpub.net/11893231/viewspace-683241 本文轉自健哥的數據花園博客園博客&#xff0c;原文鏈接&#xff1a;http://www.cnblogs.com/gaojian/archive/2012/11/30/2795775.html&#xff0c;如需轉載請自行聯系原作者

【深度學習mmdetection錯誤】——mmdetection 運行報錯KeyError:‘ConvWS is already registered in conv layer‘

于是修改以下mmdetection的安裝文件&#xff1a; site-packages/mmdet-2.1.0unknown-py3.7-linux-x86_64.egg/mmdet/ops/conv_ws.py" 把 CONV_LAYERS.register_module(ConvWS) 修改為&#xff1a; CONV_LAYERS.register_module(nameConvWS, forceTrue)

ABB RAPID 在 Notepad++ 中語法高亮的實現

ABB RAPID 在 Notepad 中語法高亮的實現 分類&#xff1a; Misc2014-04-08 15:43 145人閱讀 評論(0) 收藏 舉報notepadNotepad 內置了一個稱為 UDL2.0 (User Defined Language) 的引擎&#xff0c;來實現用戶自定義語法高亮&#xff0c;使用它&#xff0c;可以定制自己的代碼語…

Redis服務器的啟動過程分析

轉載于&#xff1a;http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/127.html?1455808771 本文將通過分析代碼來介紹Redis的啟動過程&#xff0c;通過查看Redis 的啟動腳本&#xff0c;得知Redis的啟動時從Redis.c的main方法開始的。Redis啟動可以分為以下幾個步驟…

MyEclipse運行時自動保存

今天第一次用MyEclipse&#xff0c;我發現我的代碼明明修改了&#xff0c;但運行結果發現總是修改前的代碼結果。后來發現&#xff0c;是代碼修改后必須保存&#xff0c;再點運行。這個功能明顯不合適&#xff0c;所以需要更改MyEclipse的配置。紅框是修改后的結果。 轉載于:ht…

PLSQL中INDEX BY TABLE 的 prior 和 next 操作學習

開始 --INDEX BY Table SET SERVEROUTPUT ON;DECLARETYPE enm_tab_type IS TABLE OFemp.ename%TYPEINDEX BY BINARY_INTEGER;enm_table enm_tab_type; BEGINenm_table(1):1001;enm_table(2):1002;enm_table(3):1003;enm_table(4):1004;enm_table(6):1006;dbms_output.put_line(…

【深度學習torch——error】——“xxx.pt is a zip archive(did you mean to use torch.jit.load()?)

這個問題是在進行權重文件加載進行預測的時候發生的&#xff0c;原因其實就是torch版本不對 我是用的工作站訓練使用的是torch1.7.0&#xff0c;然后用自己的電腦進行預測&#xff0c;就報錯了&#xff0c;原因就是自己的電腦是torch1.2.0版本的 因為在1.6版本以上的模型改變…

ABB 機器人 IRBP系列轉臺的一段代碼注釋

PROC IndexToStn1() //檢測變位機狀態 并設置要運行到的角度位置 并對不同的GetNextPartAdv返回值情況 進行處理 VAR bool bActive;VAR jointtarget jtCurrent; //聲明一個位置變量IF (NOT bInterchCalib1) CalibIntch1; ! reset inpo…

如何寫一個bootloader

聲明&#xff1a;本文為學習Codeproject文章的個人總結性文章&#xff0c; 原文&#xff1a;http://www.codeproject.com/Articles/664165/Writing-a-boot-loader-in-Assembly-and-C-Part 本人開發環境&#xff1a; 操作系統&#xff1a;Ubuntu 32位&#xff08;64位的會有push…

定時執行某段程序

有時候我們需要每天 定時的 自動 去執行某段程序&#xff0c;那么這個功能如何實現呢&#xff1f; 經過百度&#xff0c;定時器就可以實現&#xff0c;總結如下&#xff1a; 我用控制臺寫了一個程序&#xff0c;用來在指定時間內 打印 “我執行了” 上面就是程序的運行結構&…

【error】深度優先搜索TypeError: unhashable type: ‘list‘

查網上的原因是&#xff1a; python字典的key不支持list類型和dict類型&#xff0c;需要轉換 但是我沒有使用到key&#xff0c;后來仔細查看發現是增加了一個裝飾器導致的&#xff0c;functions.lru.cache 把裝飾器注釋掉即可 # 利用深度搜索進行查找 lru_cache(None) def …

Okhttp 插入緩存攔截器 解析

我們在做網絡請求的時候&#xff0c;如果網絡請求過于頻繁而且請求的數據變動不大&#xff0c;或者基本沒有變動&#xff0c;這個時候如果沒有緩存功能&#xff0c;我們想一下 會浪費掉多少資源&#xff0c;一次請求刷新一次&#xff0c;去請求一次&#xff0c;不但會消耗用戶的…

淺談PROFINET IO通信的實時性

PROFINET由PROFIBUS國際組織&#xff08;PROFIBUS International&#xff0c;PI&#xff09;推出&#xff0c;是新一代基于工業以太網技術的自動化總線標準。作為一項戰略性的技術創新&#xff0c;PROFINET為自動化通信領域提 供了一個完整的網絡解決方案&#xff0c;囊括了諸如…

目標

學習計劃以及目標---------------------------------------------------------------------------------------------------------------------------------------------------------------- 正文 在上大學之前&#xff0c;可以說我完全是一個…

今日頭條核心技術“個性推薦算法”揭秘

今日頭條核心技術“個性推薦算法”揭秘 最近面試華興資本&#xff0c; 他們比較關注今日頭條算法的實現&#xff0c; 今天特轉載網上 今日頭條算法解密【IT168 評論】互聯網給用戶帶來了大量的信息&#xff0c;滿足了用戶在信息時代對信息的需求&#xff0c;也使得用戶在面對大…

PROFINET及其同步實時通訊分析

1 概述 PROFINET實時以太網是由Profibus International&#xff08;PI&#xff09;組織提出的基于以太網的自動化標準。從2004年4月開 始&#xff0c;PI與Interbus Club總線俱樂部聯手&#xff0c;負責合作開發與制定標準。PROFINET構成從I/O級直至協調治理級的基于組件的分…

【劍指offer15.二進制中1的個數】——位操作(左移右移等)

目錄 二進制的表示 二進制的位操作 應用&#xff1a; 劍指offer15.統計二進制中1的個數&#xff08;多種方法&#xff0c;位右移操作、與操作等&#xff09; 轉自&#xff1a;https://www.jianshu.com/p/3a31065a8e58 紅色為自己添加 我們都知道在計算機中所有的信息最終都…

java 異常處理機制(java 編程思想)

一、概念  “異常”這個詞有“我對此感到意外”的意思。問題出現了&#xff0c;你也許并不清楚該如何處理&#xff0c;但你的確知道不應該置之不理&#xff1b;你要停下來&#xff0c;看看是不是有別人或在別的地方&#xff0c;能夠處理這個問題。只是在當前的環境中還沒有足夠…