终于又有活儿干了。
监控网速。可以提供给其他app。涉及到IPC,bound service不再适用。这里就用上了AIDL。
AIDL简介详见官方文档:http://developer.android.com/intl/zh-cn/guide/components/aidl.html
这里主要简单介绍一下基本用法(复杂的用法留到下篇再讲):
服务端创建AIDL:
package com.meizu.flyme.tv.bandwithmonitor;
// Declare any non-default types here with import statements
// 有参数的情况会略有不同,这个下篇再讲
interface IMonitorService {
long getBandwidth(); // 返回带宽。单位KB/s。下同
long getRealTimeSpeed(); // 返回实时网速
}
可以看到,AS为我们自动生成了一些类:
生成的类中,最重要的是IMonitorService.Stub。在服务端,我们要重载这个类,需要重载的方法和对应aidl文件中声明的方法一样,如下:
private IMonitorService.Stub mStub = new IMonitorService.Stub() {
@Override
public long getBandwidth() throws RemoteException {
return 0;
}
@Override
public long getRealTimeSpeed() throws RemoteException {
return 0;
}
};
都对应Stub中重载的方法完全和aidl中声明的一样。这样,我们便能控制返回给客户端的结果。
下面看看客户端需要做些什么:
1.将服务端中的aidl文件拷贝到客户端的工程中。对于这个例子,则是把PROJECT_PATH/app/src/main/aidl/com/robert/bandwidthmonitor中的com/robert/bandwidthmonitor/IMonitorService.aidl拷贝了到CLIENT_ROOT_PATH/app/src/main/aidl下。
2.和通常的绑定服务一样,不过在SerivceConnection.onServiceConnected中调用IMonitorService.Stub.asInterface(Binder)得到Service。如下:
private IMonitorService mService;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
KLog.e();
mService = IMonitorService.Stub.asInterface(iBinder);
setupTimer();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
下面是具体代码:
下面看一下服务端(接受请求的一端)是怎么实现逻辑的(忽略监听网络的逻辑):
package com.robert.bandwidthmonitor.service;
public class MonitorService extends Service {
private static final int SAMPLE_PERIOD_TIME = 1000;
private static final int DOWNLOAD_TEST_TIME = 15000; // 15s
private long mBandwidth = 0;
private long mCurrentSpeed = 0;
private Timer mTimer;
private long mLastRecv = 0;
// 最重要的是这个Stub
private IMonitorService.Stub mStub = new IMonitorService.Stub() {
@Override
public long getBandwidth() throws RemoteException {
return mBandwidth;
}
@Override
public long getRealTimeSpeed() throws RemoteException {
return mCurrentSpeed;
}
};
public static Intent newIntent(Context context) {
return new Intent(context, MonitorService.class);
}
@Override
public IBinder onBind(Intent intent) {
KLog.e();
return mStub;
}
@Override
public void onCreate() {
super.onCreate();
KLog.e();
mTimer = new Timer("MonitorTimer");
mTimer.schedule(new TimerTask() {
@Override
public void run() {
calculateRealTimeSpeed();
}
}, 1000, SAMPLE_PERIOD_TIME); // sample frequency: 1s
initBandwidth();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
@Override
public void onDestroy() {
KLog.d();
if (mTimer != null) {
mTimer.cancel();
}
}
private void initBandwidth() {
startTestTask();
}
private void startTestTask() {
KLog.e();
new StatCollectTask().execute("https://qd.myapp.com/myapp/qqteam/AndroidQQ/mobileqq_android.apk"); // 下载qq apk
}
private void calculateRealTimeSpeed() {
long cur = TrafficStats.getTotalRxBytes();
mCurrentSpeed = (cur - mLastRecv) / SAMPLE_PERIOD_TIME;
mLastRecv = cur;
}
private class StatCollectTask extends AsyncTask<String, Void, Long> {
private String urlString;
@Override
protected Long doInBackground(String... strings) {
urlString = strings[0];
Timer timer = new Timer("FinishTaskTimer");
timer.schedule(new TimerTask() {
@Override
public void run() {
StatCollectTask.this.cancel(true);
}
}, DOWNLOAD_TEST_TIME);
try {
URL url = new URL(urlString);
long start = System.currentTimeMillis();
int tot = 0;
while (isCancelled() == false) {
tot += readBytes(url);
}
long end = System.currentTimeMillis();
KLog.d("read bytes: " + tot / 1024);
return tot / ((end - start) / 1000);
} catch (MalformedURLException e) {
KLog.e();
} catch (IOException e) {
e.printStackTrace();
} finally {
timer.cancel();
}
return null;
}
@Override
protected void onCancelled(Long aLong) {
processResult(aLong);
}
@Override
protected void onPostExecute(Long aLong) {
processResult(aLong);
}
// on ui thread
private void processResult(Long aLong) {
if (aLong != null) {
mBandwidth = aLong / 1024;
KLog.e("finish download test. result: " + mBandwidth + " KB/s");
}
}
private int readBytes(URL url) throws IOException {
KLog.e("execute downloading: " + url);
URLConnection conn = url.openConnection();
conn.setUseCaches(false);
InputStream in = conn.getInputStream();
int cnt = 0;
try {
int bytesRead = 0;
byte[] buffer = new byte[1024 * 1024]; // 1MB
while ((bytesRead = in.read(buffer)) != -1) {
cnt += bytesRead;
if (isCancelled()) {
break;
}
}
} finally {
in.close();
}
return cnt;
}
}
}
AndroidManifest.xml
<manifest package="com.robert.bandwidthmonitor"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<service
android:name=".service.MonitorService"
android:enabled="true"
android:exported="true"
android:process=":remote">
<intent-filter>
<action android:name="com.robert.bandwidthmonitor.service.IMonitorService"/>
intent-filter>
service>
application>
manifest>
注意service的android:process属性。(:remote为BandwidthMonitor创建了一个私有进程,如果没有冒号,则代表创建一个全局的进程,名字就是remote。名字应该避免冲突。)
客户端代码:
public class AIDLActivity extends AppCompatActivity {
private TextView mBandwidth;
private TextView mSpeed;
private Timer mTimer;
private IMonitorService mService;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
KLog.e();
mService = IMonitorService.Stub.asInterface(iBinder);
setupTimer();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
private void setupTimer() {
mTimer = new Timer("speed");
mTimer.schedule(new TimerTask() {
@Override
public void run() {
try {
final long speed = mService.getRealTimeSpeed();
final long max = mService.getBandwidth();
runOnUiThread(new Runnable() {
@Override
public void run() {
mSpeed.setText("speed: " + speed + "KB/s");
mBandwidth.setText("bandwidth: " + max + "KB/s");
}
});
} catch (DeadObjectException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}, 1000, 1000);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
KLog.e();
mBandwidth = (TextView) findViewById(R.id.bandwidth);
mSpeed = (TextView) findViewById(R.id.speed);
// Intent intent = new Intent(this, MonitorService.class);
Intent intent = new Intent("com.meizu.flyme.tv.bandwidth.service.IMonitorService");
intent.setPackage("com.robert.bandwithmonitor"); // 必须设置
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
KLog.e();
if (mTimer != null) {
mTimer.cancel();
}
unbindService(mConnection);
}
}
intent必须设置package,intent必须为显示的(explicit),否则会报错。(在官方看到过,不过找不到了。)