1瘸彤、進(jìn)程相關(guān)的知識(shí)
1.1婚肆、什么叫進(jìn)程
說到進(jìn)程,很多人包括我會(huì)聯(lián)想到線程,以為這兩個(gè)東西之間有什么關(guān)系邻寿,但是這一周的學(xué)習(xí)渤弛,至少讓我知道己肮,多進(jìn)程和單線程主穗,多進(jìn)程和多線程,單進(jìn)程和多線程這樣的組合都可以堤如。借用老師的比喻蒲列,線程比作流水線,進(jìn)程就是一個(gè)車間搀罢,而我們的系統(tǒng)就是工廠蝗岖,然而它們的數(shù)量之間不一定是現(xiàn)實(shí)中的。進(jìn)程是系統(tǒng)進(jìn)行資源分配以及調(diào)度的基本單位榔至,這個(gè)意思就是會(huì)所同一個(gè)進(jìn)程里內(nèi)存資源是共享的抵赢,然而不同進(jìn)程里的資源是不共享的,這就涉及到進(jìn)程間通信的問題唧取,后面會(huì)講到铅鲤。
1.2、創(chuàng)建進(jìn)程
創(chuàng)建一個(gè)進(jìn)程很簡單枫弟,只需要在Androidmenifest中添加process標(biāo)簽邢享,就可以創(chuàng)建一個(gè)進(jìn)程,命名格式一般為包名淡诗,如:
android:process="com.example.w"
如果只是想創(chuàng)建一個(gè)遠(yuǎn)程服務(wù)骇塘,可以用":"創(chuàng)建一個(gè)子進(jìn)程程如:
android:process=":remote"
1.3、進(jìn)程間的等級(jí)
進(jìn)程可有如下等級(jí):
- 前臺(tái)進(jìn)程韩容;用戶正在使用的進(jìn)程款违,簡單理解就是用戶正在交互的對象。一直存在的前臺(tái)進(jìn)程是極少的群凶,而且前臺(tái)進(jìn)程一般是最后內(nèi)存小的連前臺(tái)進(jìn)程都沒有地方運(yùn)行的時(shí)候插爹,才會(huì)殺掉進(jìn)程。
- 可見進(jìn)程:如彈出一個(gè)對話框的Acitivity座掘,后面的Activity是可見的递惋。后面可見的Activity可以被認(rèn)為是一個(gè)可見進(jìn)程柔滔。
- 服務(wù)進(jìn)程:一個(gè)通過startService()啟動(dòng)的service溢陪,用戶雖然看不到然而進(jìn)行著和用戶關(guān)注的任務(wù)萍虽,如下載,播放音樂
- 后臺(tái)進(jìn)程:按了home鍵之后不可見的activity形真,一般是執(zhí)行了onStop()方法之后杉编,在內(nèi)存不足的情況下可能會(huì)被回收
- 空進(jìn)程:里面沒有任何可以運(yùn)行的程序組件,這些進(jìn)程存在就是作為一個(gè)緩存咆霜,為下一次啟動(dòng)程序縮短時(shí)間邓馒。
2、多進(jìn)程之間的通信
2.1蛾坯、多進(jìn)程的優(yōu)點(diǎn)
- 穩(wěn)定安全:一個(gè)子進(jìn)程如果因?yàn)槟承┊惓=Y(jié)束之后光酣,不會(huì)直接導(dǎo)致主程序的崩潰。
- 擴(kuò)大內(nèi)存空間:android系統(tǒng)對于一個(gè)應(yīng)用程序的內(nèi)存占用是有限制的脉课,當(dāng)使用多進(jìn)程的時(shí)候救军,可以減少主進(jìn)程占用的內(nèi)存,這樣降低被系統(tǒng)kill的概率倘零。
2.2唱遭、創(chuàng)建多進(jìn)程時(shí)候注意的問題
- 每個(gè)進(jìn)程初始化的時(shí)候都會(huì)執(zhí)行一遍Application的onCreate()方法,在這個(gè)方法中一般是進(jìn)行一些全局初始化的操作呈驶,所以進(jìn)程初始化的過程多次執(zhí)行onCreate()是浪費(fèi)資源拷泽,解決方法,獲取當(dāng)前的進(jìn)程名袖瞻,只初始化當(dāng)前的進(jìn)程司致,如下代碼:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//獲取當(dāng)前pid
int pid = android.os.Process.myPid();
Log.i("haha", "application pid is :" + pid);
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
//進(jìn)程列表
List<ActivityManager.RunningAppProcessInfo> processInfos = am.getRunningAppProcesses();
if (processInfos != null && !processInfos.isEmpty()){
for (ActivityManager.RunningAppProcessInfo appProcessInfo : processInfos){
//匹配當(dāng)前進(jìn)程的pid
if (appProcessInfo.pid == pid){
//匹配進(jìn)程名
if (TextUtils.equals(appProcessInfo.processName,getPackageName())){
Log.i("haha", "主進(jìn)程被初始化了");
}else if (TextUtils.equals(appProcessInfo.processName, "com.text.messenger.service")){
Log.i("haha", "其他進(jìn)程被初始化了");
}
}
}
}
}
}
每個(gè)進(jìn)程都有自己的初始化代碼,這樣就不會(huì)被初始化多次了聋迎。
- 進(jìn)程不是越多越好脂矫,當(dāng)進(jìn)程很多的時(shí)候就要考慮耗電等其他問題
以上參考:進(jìn)程初始化中Application多次初始化的問題
2.3、進(jìn)程間的通信ipc
inter process communication
正因?yàn)檫M(jìn)程間內(nèi)存不共享砌庄,所以就需要方法來進(jìn)行進(jìn)程間數(shù)據(jù)的傳遞羹唠。
(單進(jìn)程,多線程)->handler
(多進(jìn)程娄昆,單線程)->Messenger
(多線程佩微,多進(jìn)程)->AIDL(android interface definition language)
這里推薦一個(gè)最近發(fā)現(xiàn)的可在國內(nèi)用的開發(fā)者網(wǎng)站:android developers , 感謝群里胖大哥。
方法1:Messenger
官方文檔上有介紹萌焰,對于大多數(shù)應(yīng)用來說哺眯,多進(jìn)程單線程的操作已經(jīng)能夠滿足很多需求,而且使用Messenger比使用AIDL簡單扒俯。
文檔里的代碼:
public class MessengerService extends Service {
static final int MSG_SAY_HELLO = 1;
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
//接收到客戶端的消息
Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
final Messenger mMessenger = new Messenger(new IncomingHandler());
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}
首先創(chuàng)建一個(gè)Handler奶卓,用于處理客戶端發(fā)過來的消息一疯,然后創(chuàng)建Messenger對象,這個(gè)對象用于在客戶端如Activity中給Service這個(gè)進(jìn)程發(fā)送消息夺姑,通過onBind()方法返回給客戶端Binder對象墩邀,在客戶端中使用IBinder對象將Messenger實(shí)例化,然后使用這個(gè)Messenger給服務(wù)端發(fā)送消息盏浙。那么客戶端(這里是Activity)代碼如下:(來自文檔)
public class ActivityMessenger extends Activity {
Messenger mService = null;
//是否連接成功
boolean mBound;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
//實(shí)例化Messenger對像
mService = new Messenger(service);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
mBound = false;
}
};
public void sayHello(View v) {
if (!mBound) return;
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
//使用該Messenger發(fā)送消息
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onStart() {
super.onStart();
// 綁定服務(wù)
bindService(new Intent(this, MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// 解綁服務(wù)
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
}
上面的代碼和前面說的流程差不多,得到Messenger對象就可以發(fā)送消息了眉睹。還有還有別忘記在AndroidMenifest中注冊這個(gè)Service,并聲明android:process這個(gè)屬性废膘。
這個(gè)例子只是簡單的客戶端給服務(wù)端發(fā)送消息竹海,顯然功能不能滿足,有來有往才符合交互丐黄。接下來就是服務(wù)端給客戶端發(fā)送消息斋配,文檔里有這樣一段話:
If you want the service to respond, then you need to also create a Messenger in the client. Then when the client receives the onServiceConnected() callback, it sends a Message to the service that includes the client's Messenger in the replyTo parameter of the send() method.
我英語不好開始看的時(shí)候用某度翻譯了一下,也看不懂灌闺,然后它貼心的告訴我有個(gè)例子艰争,我立馬就去看了。最后一句話的意思才懂了一些菩鲜。
首先园细,上面英語告訴我們,得先在客戶端新建一個(gè)Messenger接校,那么問題來了猛频,這個(gè)Messenger是要傳給服務(wù)端來給客戶端發(fā)送消息的,如果有很多的客戶端怎么辦蛛勉?我們可以考慮在服務(wù)端用一個(gè)Map或者List來保存這些不同客戶端的Messenger鹿寻,如:
ArrayList<Messenger> mClients = new ArrayList<>();
服務(wù)端的代碼:
/**
*
* 多進(jìn)程,單線程
* Created by W on 2016/9/4.
*/
public class MessengerService extends Service {
public static final int MSG_REGISTER_CLIENT = 1;
public static final int MSG_UNREGISTER_CLIENT = 2;
public static final int MSG_SET_VALUE = 3;
int mValue = 122;
ArrayList<Messenger> mClients = new ArrayList<>();
//接收傳過來的消息
class InComingHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MSG_REGISTER_CLIENT:
Log.i("haha", "添加了");
mClients.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
Log.i("haha", "移除");
break;
case MSG_SET_VALUE:
try {
Log.i("haha", "發(fā)送給Activity");
mClients.get(0).send(Message.obtain(null, MSG_SET_VALUE, mValue,0));
} catch (RemoteException e) {
e.printStackTrace();
mClients.remove(0);
}
break;
default:
super.handleMessage(msg);
}
}
}
Messenger mMessenger = new Messenger(new InComingHandler());
@Override
public void onCreate() {
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
然后客戶端的代碼:
/**
*
* Created by W on 2016/9/4.
*/
public class MessengerActivity extends AppCompatActivity{
boolean mBound = false;
private Messenger mMessenger;
//該Activity的Messenger
Messenger messenger = new Messenger(new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MessengerService.MSG_SET_VALUE:
Log.i("haha", "從服務(wù)傳過來的:" + msg.arg1 + "");
break;
default:
super.handleMessage(msg);
}
}
});
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMessenger = new Messenger(service);
mBound = true;
Log.i("haha", "服務(wù)連接成功");
//以下的代碼诽凌,就是一旦連接成功毡熏,以下的代碼,就是一旦連接成功侣诵,就把該activity的Messenger傳給服務(wù)端
Message message = Message.obtain(null, MessengerService.MSG_REGISTER_CLIENT);
message.replyTo = messenger;
try {
mMessenger.send(message);
Log.i("haha", "給服務(wù)發(fā)消息成功");
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mMessenger = null;
mBound = true;
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.messenger);
Log.i("haha", "onCreate()");
Button button = (Button) findViewById(R.id.send_button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Message message = Message.obtain(null, MessengerService.MSG_SET_VALUE);
message.replyTo = messenger;
try {
mMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
@Override
protected void onStart() {
super.onStart();
Log.i("haha", "開始綁定服務(wù)");
bindService(new Intent(this, MessengerService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if (mBound){
if (mMessenger != null){
Message message = Message.obtain(null, MessengerService.MSG_UNREGISTER_CLIENT);
message.replyTo = messenger;
Log.i("haha", "給服務(wù)發(fā)送消息解除監(jiān)聽");
try {
mMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mServiceConnection);
mBound = false;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i("haha", "onDestroy");
}
}
Activity創(chuàng)建痢法,走onCreate(),onStart()杜顺,在start中綁定服務(wù)财搁,然后回調(diào)的接口返回了Service的Messenger對象,Activity也把自己的Messenger對象通過msg.replyTo傳給了服務(wù)端躬络,同時(shí)mClients添加這個(gè)Messenger尖奔。這樣之后它們就可以互相發(fā)消息了。可能我講的不是很好提茁,如果在真機(jī)上調(diào)試看Log淹禾,就基本上能理清這個(gè)流程。
哎呀不用不知道茴扁,Messenger有一個(gè)特別坑的地方铃岔,發(fā)現(xiàn)不能傳一個(gè)序列啊,這周作業(yè)本來是打算把一個(gè)對象傳過去丹弱,然而不行啊德撬。還有一個(gè)在進(jìn)程間全局變量沒什么作用啊铲咨。果然只有實(shí)踐才知道好多東西
方法2.AIDL
我感覺沒用這個(gè)aidl都已經(jīng)忘的差不多了躲胳。
首先在android studio中main目錄下新建一個(gè)AIDL文件夾,如圖:
基本上是一路下一步纤勒。我們看到生成的文件里面有一個(gè).aidl的文件:
// IMyAidlInterface.aidl
package com.example.myactionbardemo;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
String getName(String aName);
}
這里生成了一個(gè)接口坯苹,里面的抽象方法是需要實(shí)現(xiàn)的。
然后點(diǎn)任務(wù)欄上的tools->make project摇天。就會(huì)在如下菜單下生成一個(gè)接口:
重點(diǎn)關(guān)注有個(gè)Stub類實(shí)現(xiàn)了IMyAidlInterface接口粹湃,然后有個(gè)asInterface()方法可以生成接口對象。這就是我們做的前期工作泉坐,還有如果重新修改了.aidl文件一定要重新make project为鳄。
然后就是開始使用了,定義一個(gè)服務(wù)端:
public class AIDLService extends Service{
IMyAidlInterface.Stub mStub = new IMyAidlInterface.Stub() {
//需要實(shí)現(xiàn)的兩個(gè)方法腕让,或者說會(huì)回調(diào)的方法孤钦。
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public String getName(String aName) throws RemoteException {
return aName + "style";
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
//返回的Stub對象
return mStub;
}
}
這個(gè)就會(huì)傳到客戶端,這里仍然用Activity代替客戶端:
IMyAidlInterface mIMyAidlInterface;
ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
以上的代碼應(yīng)該是比較熟悉了纯丸,得到了接口對象偏形,就能回調(diào)她的方法,相當(dāng)于把客戶端的數(shù)據(jù)傳給服務(wù)端進(jìn)行操作了觉鼻,最重要的事它可以傳對象啊俊扭,這點(diǎn)就足以了。
另坠陈,進(jìn)程間傳遞的對象需要序列化萨惑,在此android studio有個(gè)parcelable插件可以自動(dòng)給一個(gè)對象序列化。
再另仇矾,messenger底層也是用aidl實(shí)現(xiàn)的庸蔼。
寫的有點(diǎn)多,下面的英語看看就好若未。
If you only need to interact with the service while your activity is visible, you should bind during onStart() and unbind during onStop().
If you want your activity to receive responses even while it is stopped in the background, then you can bind during onCreate() and unbind during onDestroy(). Beware that this implies that your activity needs to use the service the entire time it's running (even in the background), so if the service is in another process, then you increase the weight of the process and it becomes more likely that the system will kill it.