Android 殺不死的程序
阿新 • • 發佈:2019-02-01
最近和同事討論微信殺死程序結束後,還能收到好友發來的微信這功能。以前一直用著,沒有去研究這技術。最近專案提前完成,有了點空閒時間,就研究這技術。然後寫了個demo,試著執行,發現OK了。在這給朋友分享下,可能有不夠好的地方,歡迎大神們指出。
首先,正常下,當我們退出程式或手動殺死後,我們的程式就停止運行了。或者不退出在後臺執行時,使用360加速球清理時,也會殺死我們的程序。個人理解360加速時,把不被新增信任的程序用一個for迴圈,在迴圈裡一個個的kill。於是有了一種思路,在我們應用中開啟兩個程序,當一個程序被殺死時,另外一個程序啟動被殺死的程序,相互守護。沿著這思路,開始擼程式碼。
1.建立兩個Service:LocationService和RemoteServices
LocationService代表主程序,RemoteServices代表守護程序。這裡要實現RemoteServices是另外的程序需要在Androidmanifest中配置:
<service android:name=".RemoteServices" android:enabled="true" android:exported="true" android:process=".RemoteServices" />
接下來是要對這兩個service相互繫結,相互監聽對方的狀態。兩個程序中的通訊我們可以使用aidl,這裡不對aidl細講,不瞭解的朋友可以去查下。
建立一個ProcessIdle.aidl檔案
// ProcessIdle.aidl
package inner;
interface ProcessIdle {
String getProcessName();
}
這裡只定義一個方法getProcessName(),用於獲取程序名。接下來編寫LocationService和RemoteServices兩個類。
RemoteServices
public class RemoteServices extends Service { MyBinder myBinder; MyConn myConn; @Nullable @Overridepublic IBinder onBind(Intent intent) { return myBinder; } @Override public int onStartCommand(Intent intent, int flags, int startId) { showWindow("RemoteServices======"); Toast.makeText(this, "RemoteServices=======onStartCommand", Toast.LENGTH_SHORT).show(); boolean isLocationServiceRunning = Util.isServiceRunning(this, "com.example.zonglijia.killapp.LocationService"); if (!isLocationServiceRunning) { RemoteServices.this.startService(new Intent(RemoteServices.this, LocationService.class)); } RemoteServices.this.bindService(new Intent(RemoteServices.this, LocationService.class), myConn, Context.BIND_IMPORTANT); return START_STICKY; } @Override public void onCreate() { Toast.makeText(this, "RemoteServices=======onCreate", Toast.LENGTH_SHORT).show(); Log.i("RemoteServices", "onCreate"); super.onCreate(); if (myBinder == null) myBinder = new MyBinder(); if (myConn == null) { myConn = new MyConn(); } // showWindow("RemoteServices======"); } class MyBinder extends ProcessIdle.Stub { @Override public String getProcessName() throws RemoteException { return "RemoteServices"; } } class MyConn implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { Toast.makeText(RemoteServices.this, "已連線本地服務", Toast.LENGTH_LONG).show(); } @Override public void onServiceDisconnected(ComponentName name) { Toast.makeText(RemoteServices.this, "斷開連線本地服務", Toast.LENGTH_LONG).show(); RemoteServices.this.startService(new Intent(RemoteServices.this, LocationService.class)); RemoteServices.this.bindService(new Intent(RemoteServices.this, LocationService.class), myConn, Context.BIND_IMPORTANT); } } WindowManager mWM; TextView view; private void showWindow(String text) { mWM = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE); WindowManager.LayoutParams params = new WindowManager.LayoutParams(); params.height = WindowManager.LayoutParams.WRAP_CONTENT; params.width = WindowManager.LayoutParams.WRAP_CONTENT; params.format = PixelFormat.TRANSLUCENT; params.type = WindowManager.LayoutParams.TYPE_TOAST; params.setTitle("Toast"); params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; // view = LayoutInflater.from(this).inflate(R.layout.window_show, null); // TextView textView = (TextView) view.findViewById(R.id.text); view = new TextView(this); view.setText(text); view.setTextColor(Color.RED); mWM.addView(view, params); } @Override public void onDestroy() { Toast.makeText(this, "RemoteServices=======onDestroy", Toast.LENGTH_SHORT).show(); Log.i("RemoteServices", "onDestroy"); RemoteServices.this.startService(new Intent(RemoteServices.this, LocationService.class)); RemoteServices.this.bindService(new Intent(RemoteServices.this, LocationService.class), myConn, Context.BIND_IMPORTANT); super.onDestroy(); } }
LocationService
public class LocationService extends Service { MyBinder myBinder; MyConn2 myConn2; @Nullable @Override public IBinder onBind(Intent intent) { return myBinder; } @Override public int onStartCommand(Intent intent, int flags, int startId) { showWindow("LocationServices======"); Toast.makeText(this, "LocationService=======onStartCommand", Toast.LENGTH_SHORT).show(); boolean isRemoteServiceRunning = Util.isServiceRunning(this, "com.example.zonglijia.killapp.RemoteServices"); if (!isRemoteServiceRunning) { LocationService.this.startService(new Intent(LocationService.this, RemoteServices.class)); } LocationService.this.bindService(new Intent(LocationService.this, RemoteServices.class), myConn2, Context.BIND_IMPORTANT); return START_STICKY; } @Override public void onCreate() { Toast.makeText(this, "LocationService=======onCreate", Toast.LENGTH_SHORT).show(); Log.i("LocationService", "onCreate"); super.onCreate(); if (myBinder == null) myBinder = new MyBinder(); if (myConn2 == null) { myConn2 = new MyConn2(); } // showWindow("LocationServices======"); } class MyBinder extends ProcessIdle.Stub { @Override public String getProcessName() throws RemoteException { return "LocationProcess"; } } class MyConn2 implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { Toast.makeText(LocationService.this, "已連線遠端服務", Toast.LENGTH_LONG).show(); } @Override public void onServiceDisconnected(ComponentName name) { Toast.makeText(LocationService.this, "斷開連線遠端服務", Toast.LENGTH_LONG).show(); LocationService.this.startService(new Intent(LocationService.this, RemoteServices.class)); LocationService.this.bindService(new Intent(LocationService.this, RemoteServices.class), myConn2, Context.BIND_IMPORTANT); } } WindowManager mWM; TextView view; private void showWindow(String text) { mWM = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE); WindowManager.LayoutParams params = new WindowManager.LayoutParams(); params.height = WindowManager.LayoutParams.WRAP_CONTENT; params.width = WindowManager.LayoutParams.WRAP_CONTENT; params.format = PixelFormat.TRANSLUCENT; params.type = WindowManager.LayoutParams.TYPE_TOAST; params.setTitle("Toast"); params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; // view = LayoutInflater.from(this).inflate(R.layout.window_show, null); // TextView textView = (TextView) view.findViewById(R.id.text); view = new TextView(this); view.setText(text); view.setTextColor(Color.RED); mWM.addView(view, params); } @Override public void onDestroy() { Log.i("LocationService", "onDestroy"); Toast.makeText(this, "LocationService=======onDestroy", Toast.LENGTH_SHORT).show(); LocationService.this.startService(new Intent(LocationService.this, RemoteServices.class)); LocationService.this.bindService(new Intent(LocationService.this, RemoteServices.class), myConn2, Context.BIND_IMPORTANT); super.onDestroy(); } }
這兩個類的程式碼內容幾乎相同,其中的邏輯是先判斷另一程序是否在執行,如果否就先啟動,然後再進行繫結。當監聽到另一程序斷開時,就再次啟動並繫結。就是下面的程式碼塊
class MyConn2 implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { Toast.makeText(LocationService.this, "已連線遠端服務", Toast.LENGTH_LONG).show(); } @Override public void onServiceDisconnected(ComponentName name) { Toast.makeText(LocationService.this, "斷開連線遠端服務", Toast.LENGTH_LONG).show(); LocationService.this.startService(new Intent(LocationService.this, RemoteServices.class)); LocationService.this.bindService(new Intent(LocationService.this, RemoteServices.class), myConn2, Context.BIND_IMPORTANT); } }
好了,大致的思路就是這樣,細節的處理程式碼就沒再貼,需要的朋友可以在下面留言,可以發郵箱給你們。
執行測試後,在5.0以下的機型都測試成功,5.0以上機型除了在手動殺死程序時,測試失敗,其它方式殺死都測試成功。對於在5.0以上的機型相容,會再下一篇分享