feat: benchmark MediaStore API of Android
This commit is contained in:
@@ -6,6 +6,9 @@
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
@@ -23,7 +26,14 @@
|
||||
android:theme="@style/Theme.Lockstep">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="at.lockstep.app.MediaStoreBenchmarkActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package at.lockstep.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Button;
|
||||
|
||||
@@ -12,6 +13,7 @@ import at.lockstep.pb.PlaybackEngine;
|
||||
public class MainActivity extends Activity {
|
||||
private Button btnStart;
|
||||
private Button btnStop;
|
||||
private Button btnMediaStoreBenchmark;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -20,6 +22,7 @@ public class MainActivity extends Activity {
|
||||
|
||||
btnStart = findViewById(R.id.btnStart);
|
||||
btnStop = findViewById(R.id.btnStop);
|
||||
btnMediaStoreBenchmark = findViewById(R.id.btnMediaStoreBenchmark);
|
||||
|
||||
// TODO: handle clicking START button twice
|
||||
btnStart.setOnClickListener(v ->
|
||||
@@ -39,5 +42,10 @@ public class MainActivity extends Activity {
|
||||
btnStop.setOnClickListener(v ->
|
||||
startService(LstForegroundService.stopIntent(MainActivity.this))
|
||||
);
|
||||
|
||||
btnMediaStoreBenchmark.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(MainActivity.this, MediaStoreBenchmarkActivity.class);
|
||||
startActivity(intent);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
package at.lockstep.app;
|
||||
|
||||
import static androidx.activity.result.ActivityResultCallerKt.registerForActivityResult;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
import android.provider.MediaStore;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import at.lockstep.R;
|
||||
|
||||
public class MediaStoreBenchmarkActivity extends Activity {
|
||||
|
||||
private TextView resultTextView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_media_store_benchmark);
|
||||
|
||||
resultTextView = findViewById(R.id.resultTextView);
|
||||
|
||||
if (hasReadPermission()) {
|
||||
loadMusic();
|
||||
} else {
|
||||
requestReadPermission();
|
||||
}
|
||||
}
|
||||
|
||||
private static final int REQUEST_READ_PERMISSION = 1001;
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(
|
||||
int requestCode,
|
||||
String[] permissions,
|
||||
int[] grantResults
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
if (requestCode == REQUEST_READ_PERMISSION) {
|
||||
boolean granted = grantResults.length > 0
|
||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
if (granted) {
|
||||
loadMusic();
|
||||
} else {
|
||||
resultTextView.setText("Permission denied.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasReadPermission() {
|
||||
String permission;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
permission = Manifest.permission.READ_MEDIA_AUDIO;
|
||||
} else {
|
||||
permission = Manifest.permission.READ_EXTERNAL_STORAGE;
|
||||
}
|
||||
|
||||
return ContextCompat.checkSelfPermission(this, permission)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
private void requestReadPermission() {
|
||||
String permission;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
permission = Manifest.permission.READ_MEDIA_AUDIO;
|
||||
} else {
|
||||
permission = Manifest.permission.READ_EXTERNAL_STORAGE;
|
||||
}
|
||||
|
||||
//permissionLauncher.launch(permission);
|
||||
requestPermissions(
|
||||
new String[]{ permission },
|
||||
REQUEST_READ_PERMISSION
|
||||
);
|
||||
}
|
||||
|
||||
private void loadMusic() {
|
||||
List<String> musicList = new ArrayList<>();
|
||||
|
||||
android.net.Uri collection = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
|
||||
String[] projection = new String[] {
|
||||
MediaStore.Audio.Media._ID,
|
||||
MediaStore.Audio.Media.TITLE,
|
||||
MediaStore.Audio.Media.DATA
|
||||
};
|
||||
|
||||
String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0";
|
||||
|
||||
long start = SystemClock.elapsedRealtime();
|
||||
|
||||
try (android.database.Cursor cursor = getContentResolver().query(
|
||||
collection,
|
||||
projection,
|
||||
selection,
|
||||
null,
|
||||
MediaStore.Audio.Media.TITLE + " ASC"
|
||||
)) {
|
||||
if (cursor != null) {
|
||||
int titleColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE);
|
||||
int dataColumn = cursor.getColumnIndex(MediaStore.Audio.Media.DATA);
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
String title = cursor.getString(titleColumn);
|
||||
String path = dataColumn != -1 ? cursor.getString(dataColumn) : null;
|
||||
|
||||
if (path != null) {
|
||||
musicList.add(title + "\n" + path);
|
||||
} else {
|
||||
musicList.add(title + "\n[path unavailable]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long elapsedMs = SystemClock.elapsedRealtime() - start;
|
||||
|
||||
StringBuilder header = new StringBuilder();
|
||||
header.append("Found ").append(musicList.size()).append(" music files\n");
|
||||
header.append("Query time: ").append(elapsedMs).append(" ms\n\n");
|
||||
|
||||
resultTextView.setText(header.toString() + String.join("\n\n", musicList));
|
||||
|
||||
// 200 music files in 32 ms
|
||||
// paths like "/storage/emulated/0/Download/...mp3"
|
||||
}
|
||||
}
|
||||
@@ -18,4 +18,10 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Stop collection" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnMediaStoreBenchmark"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Benchmark media store" />
|
||||
</LinearLayout>
|
||||
13
app/src/main/res/layout/activity_media_store_benchmark.xml
Normal file
13
app/src/main/res/layout/activity_media_store_benchmark.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/resultTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:text="Loading..."
|
||||
android:textSize="14sp" />
|
||||
</ScrollView>
|
||||
Reference in New Issue
Block a user