需求:
客戶端通過AIDL傳一段文件流給服務(wù)端缚够,服務(wù)端根據(jù)流還原生成文件并打開
前置條件:
不贅述AIDL的使用方法
示例代碼不做詳細的容錯處理
想法:
如果要通過AIDL把一個文件流傳輸?shù)搅硪欢酥景颍R時可以想得到的一個方法是:
client端的線程通過FileInputStream每次read一個緩沖锹锰,通過AIDL相應(yīng)的去調(diào)用server端的方法,把這個緩沖區(qū)的byte數(shù)組傳遞到server端癣漆,然后server端通過FileOutputStream把這些byte數(shù)組write到新生成的臨時文件即可长赞。
但這種方法的弊端就是在文件傳輸期間會阻塞server端。眾所周知态贤,service是運行在應(yīng)用的主線程中的,如果service中出現(xiàn)了很多不在其子線程中運行的阻塞或耗時操作醋火,肯定會影響整個應(yīng)用的流暢性的悠汽,所以這種方法不可行。
解決方法:
問題的關(guān)鍵是如何把接收的過程放到server端的線程中芥驳?解決方法是使用管道柿冲!但是Android進程間通信用Binder來傳遞數(shù)據(jù),有一個最基本的要求就是要實現(xiàn)Parcelable接口晚树。所以這里就需要用到:
ParcelFileDescriptor[] pfd = ParcelFileDescriptor.createPipe();
- pfd[0]是管道的read端
- pfd[1]是管道的write端
簡單流程說明:
客戶端:
先把pfd[0]通過AIDL調(diào)用傳輸給服務(wù)端姻采,然后
ParcelFileDescriptor.AutoCloseOutputStream aos = new ParcelFileDescriptor.AutoCloseOutputStream(pfd[1]);
發(fā)送線程把本地文件FileInputStream讀取的數(shù)據(jù)寫到aos
服務(wù)端:
ParcelFileDescriptor.AutoCloseInputStream ais = new ParcelFileDescriptor.AutoCloseInputStream(input); //input是傳過來的pfd[0]
接收線程把ais中讀取的數(shù)據(jù)寫到臨時文件的FileOutputStream中
簡單代碼片段
AIDL文件:
package com.test.aidlservice;
interface ITestInterface
{
void openFile(String fileName, in ParcelFileDescriptor input);
}
服務(wù)端:
package com.test.aidlservice;
public class TestService extends Service
{
@Nullable
@Override
public IBinder onBind(Intent intent)
{
return new TestBinder();
}
class TestBinder extends ITestInterface.Stub
{
@Override
public void openFile(String fileName, ParcelFileDescriptor input) throws
RemoteException
{
// 通過管道的read端包裝輸入流
ParcelFileDescriptor.AutoCloseInputStream autoCloseInputStream =
new ParcelFileDescriptor.AutoCloseInputStream(input);
// 生成臨時文件路徑
String path = Environment.getExternalStorageDirectory().toString();
path = path + "/temp_" + fileName;
// 把流存到臨時文件中
try
{
new OpenFileTask(path, autoCloseInputStream).execute();
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
}
}
}
OpenFileTask的任務(wù)就是通過傳入的臨時文件path生成對應(yīng)的FileOutputStream,然后把autoCloseInputStream讀出的數(shù)據(jù)寫入到FileOutputStream中爵憎。整個文件寫完后慨亲,調(diào)用startActivity打開對應(yīng)格式的文件,在此不再贅述宝鼓。
客戶端:
private ITestInterface mTestService = null;
private ServiceConnection mConnection = new ServiceConnection()
{
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
mTestService = ITestInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name)
{
mTestService = null;
}
};
bind部分:
Intent intent = new Intent();
intent.setAction("com.test.aidl.ITestInterface");
intent.setPackage("com.test.aidlservice");
bindService(intent, mConnection, BIND_AUTO_CREATE);
傳輸部分:
//獲取一個需要被服務(wù)端打開的測試文件
String filePath = Environment.getExternalStorageDirectory().toString() + "/test.docx";
try
{
ParcelFileDescriptor[] pfd = ParcelFileDescriptor.createPipe();
ParcelFileDescriptor.AutoCloseOutputStream autoCloseOutputStream = new ParcelFileDescriptor.AutoCloseOutputStream(pfd[1]);
new WriteFileTask(filePath, autoCloseOutputStream).execute();
mTestService.openFile("test.docx", pfd[0]);
}
catch (IOException e)
{
e.printStackTrace();
}
catch (RemoteException e)
{
e.printStackTrace();
}
WriteFileTask的任務(wù)就是通過傳入的文件path生成對應(yīng)的FileInputStream刑棵,然后讀取FileInputStream中的數(shù)據(jù)寫入到autoCloseOutputStream中即可。