快速入門android AIDL(開啟多程序並進行通訊)
為何要開啟多程序?主要有兩種情況:
一、一個應用由於自身需要採用多程序模式來實現。比如播放器之類,如果僅僅在service中執行會影響主執行緒的響應速度,很可能會造成ANR,一般情況下不會這麼寫;如果僅僅在子執行緒中執行,一旦開啟該執行緒的Activity被殺死後,執行緒也被殺死,無法實現後臺執行效果,更加不合理。而如果在另外一個程序中使用service後臺執行,就顯得十分恰當了。
二、由於android對單個應用所使用的最大記憶體做了限制,為了加大一個應用可使用的記憶體,所以通過多程序來獲取多份記憶體空間。
本篇文章demo重點:(demo原始碼在文章結尾)
1、開啟多程序
2、兩個程序之間使用AIDL進行通訊
開啟多程序:
在Android中常用的使用多程序只有一種辦法,那就是在AndroidManifest中為四大元件(Activity、Service、Broadcast Receiver、ContentProvier)指定android:process屬性。筆者demo中的遠端service如下圖:
最終繫結該service後在DDMS中程序的顯示情況如下圖:
可以看到最後的兩個程序都是同一個包名,只是第二個是“:remote”。這樣就非常簡單的開啟了多程序。
講到此處,很多好奇的讀者定然有疑問了,“android:process”中的引數到底代表了什麼?簡單來講就是代表了新開的這個程序的id。如果兩個應用要共享同一個程序就需要用到這個了。
那麼筆者此處寫的“:remote”又是什麼意思呢?“remote”不是關鍵,這個完全可以自己隨意取名字,“:”冒號才是關鍵。
程序名以“:”開頭的程序屬於當前應用的私有程序,其他應用的元件不可以和它跑在同一個程序中。而程序名不以“:”開頭的程序屬於全域性程序,其他應用可以通過某些方式和它跑在同一個程序中。
兩個程序之間使用AIDL進行通訊:
筆者此篇文章實現的主要效果:
能夠在當前程序中MainActivity,執行另一個程序中開啟的Service中實現的方法testMethod(),方法與最終效果如下:
介面上主要有兩個按鈕,第一個是開啟遠端程序中的Service,另一個為執行該方法。
主要實現步驟:(主要有三條,分別為AIDL、Service、和呼叫處(demo中為MainActivity))
1、建立一個AIDL介面,並寫入自己要在程序間通訊用的抽象方法。
myAIDL.aidl:
package com.example.double2.myaidltest;
interface myAIDL {
void testMethod();
}
建立AIDL檔案與建立java檔案等類似,直接右擊建立即可。android studio中就十分方便,會自動在main檔案下建立一個aidl資料夾,並在該資料夾創建於你專案名相同的包名。
可能遇到的小問題:
筆者第一次建立AIDL,在Service中發現找不到該AIDL的包。遇到相同問題的讀者可以在建立AIDL並寫完抽象方法之後使用build->make project重新構建一下專案。
2、建立一個遠端Service,在Service中建立一個類繼承AIDL介面中的Stub類並實現Stub中的抽象方法,最後不要忘記在onBind中返回這個類的物件。
public class AIDLRemoteService extends Service {
private static final String TAG = "AIDLRemoteService";
private final myAIDL.Stub mBinder=new myAIDL.Stub(){
@Override
public void testMethod() throws RemoteException {
Log.d(TAG, "testMethod: "+"this is myAIDLTest");
}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
3、在要呼叫的地方(筆者demo中就為MainActivity中)繫結該Service,將Service返回的Binder物件轉換成AIDL介面所屬的型別,接著直接呼叫AIDL的方法。
在成功連線之後,將Service返回的Binder物件轉換成AIDL介面所屬的型別:
private myAIDL mMyAIDL;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG, "onServiceConnected");
mMyAIDL = myAIDL.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "onServiceDisconnected");
mMyAIDL = null;
}
};
在呼叫處直接使用:
btnStartMethod.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
mMyAIDL.testMethod();
} catch (RemoteException e) {
Toast.makeText(MainActivity.this, "服務被異常殺死,請重新開啟。", Toast.LENGTH_SHORT).show();
}
}
});
demo專案結構:
myAIDL.aidl:
package com.example.double2.myaidltest;
interface myAIDL {
void testMethod();
}
AIDLRemoteService:package com.example.double2.myaidltest;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
/**
* 專案名稱:MyAIDLTest
* 建立人:Double2號
* 建立時間:2016/6/10 8:13
* 修改備註:
*/
public class AIDLRemoteService extends Service {
private static final String TAG = "AIDLRemoteService";
private final myAIDL.Stub mBinder=new myAIDL.Stub(){
@Override
public void testMethod() throws RemoteException {
Log.d(TAG, "testMethod: "+"this is myAIDLTest");
}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
MainActivity:package com.example.double2.myaidltest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Button btnBindService;
private Button btnStartMethod;
private myAIDL mMyAIDL;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG, "onServiceConnected");
mMyAIDL = myAIDL.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "onServiceDisconnected");
mMyAIDL = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
btnBindService = (Button) findViewById(R.id.btn_bind_service);
btnStartMethod = (Button) findViewById(R.id.btn_start_method);
btnBindService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, AIDLRemoteService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
});
btnStartMethod.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
mMyAIDL.testMethod();
} catch (RemoteException e) {
Toast.makeText(MainActivity.this, "服務被異常殺死,請重新開啟。", Toast.LENGTH_SHORT).show();
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mServiceConnection);
}
}
activity_main:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<Button
android:id="@+id/btn_bind_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="bindService"/>
<Button
android:id="@+id/btn_start_method"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="startMethod"/>
</LinearLayout>
AndroidManifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.double2.myaidltest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<service android:name=".AIDLRemoteService"
android:process=":remote"/>
</application>
</manifest>