AlarmManager
可重复使用的位图
inBitmap变量是在Android 3.0+版本加入到系统源码当中,也就意味着inBitmap参数只有在Android 3.0+版本及以上能够正常使用,当你的app版本低于3.0的时候,只能使用bitmap.recycle()进行Bitmap的回收操作;在Android 3.0+以上根据系统版本的不同,使用inBitmap的规则也不相同,具体区分如下:
- 4.4之前的版本inBitmap只能够重用相同大小的Bitmap内存区域。简单而言,被重用的Bitmap需要与新的Bitmap规格完全一致,否则不能重用。
- 4.4及之后的版本系统不再限制旧Bitmap与新Bitmap的大小,只要保证旧Bitmap的大小是大于等于新Bitmap大小即可。
- 除上述规则之外,旧Bitmap必须是mutable的,这点也很好理解,如果一个Bitmap不支持修改,那么其内存自然也重用不了。
//MainActivity
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "zwm, onCreate");
BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true; //设置inMutable
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher, options);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Log.d(TAG, "zwm, bitmap: " + bitmap + ", size: " + bitmap.getAllocationByteCount());
}
BitmapFactory.Options newOptions = new BitmapFactory.Options();
newOptions.inBitmap = bitmap; //设置inBitmap被复用的Bitmap
Bitmap newBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher, newOptions);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Log.d(TAG, "zwm, newBitmap: " + newBitmap + ", size: " + newBitmap.getAllocationByteCount());
}
}
}
//输出log
2019-11-18 15:10:12.677 25709-25709/com.tomorrow.second D/MainActivity: zwm, onCreate
2019-11-18 15:10:12.682 25709-25709/com.tomorrow.second D/MainActivity: zwm, bitmap: android.graphics.Bitmap@f9f6b44, size: 186624
2019-11-18 15:10:12.684 25709-25709/com.tomorrow.second D/MainActivity: zwm, newBitmap: android.graphics.Bitmap@f9f6b44, size: 186624
存储访问框架
//MainActivity
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private static final int WRITE_REQUEST_CODE = 10;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
Log.d(TAG, "zwm, onCreate");
createFile();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (data == null || resultCode != Activity.RESULT_OK) {
return;
}
if(requestCode == WRITE_REQUEST_CODE) {
Log.d( TAG, "zwm, WRITE_REQUEST_CODE uri : " + data.getData() );
}
}
private void createFile() {
Log.d(TAG, "zwm, createFile");
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_TITLE, "test_create.png");
startActivityForResult(intent, WRITE_REQUEST_CODE);
}
}
//输出log
2019-11-18 15:38:57.299 4880-4880/com.tomorrow.second D/MainActivity: zwm, onCreate
2019-11-18 15:38:57.299 4880-4880/com.tomorrow.second D/MainActivity: zwm, createFile
2019-11-18 15:39:53.299 4880-4880/com.tomorrow.second D/MainActivity: zwm, WRITE_REQUEST_CODE uri : content://com.android.providers.downloads.documents/document/39
沉浸式全屏模式
透明系统状态栏
绑定到服务
//AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tomorrow.second">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
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=".TestService">
<intent-filter>
<action android:name="com.tomorrow.intent.service" />
</intent-filter>
</service>
</application>
</manifest>
//MainActivity
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
Log.d(TAG, "zwm, onCreate");
setContentView(R.layout.activity_main);
Intent intent = new Intent("com.tomorrow.intent.service");
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "zwm, onServiceConnected");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "zwm, onServiceDisconnected");
}
}, Context.BIND_AUTO_CREATE);
}
}
//TestService
public class TestService extends Service {
private static final String TAG = "TestService";
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "zwm, onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
}
//输出log
2019-11-18 16:35:12.627 19199-19199/com.tomorrow.second E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.tomorrow.second, PID: 19199
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.tomorrow.second/com.tomorrow.second.MainActivity}: java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.tomorrow.intent.service }
Material Design
最近使用的应用屏幕中的并发文档和 Activity
//AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tomorrow.second">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
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>
<activity android:name=".SecondActivity"
android:documentLaunchMode="intoExisting"/>
<service android:name=".TestService">
<intent-filter>
<action android:name="com.tomorrow.intent.service" />
</intent-filter>
</service>
</application>
</manifest>
//MainActivity
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
Log.d(TAG, "zwm, onCreate");
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
}
}
//SecondActivity
public class SecondActivity extends Activity {
private static final String TAG = "SecondActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "zwm, onCreate");
setContentView(R.layout.activity_second);
}
}
//输出log
2019-11-18 16:53:41.197 22703-22703/com.tomorrow.second D/MainActivity: zwm, onCreate
2019-11-18 16:53:41.542 22703-22703/com.tomorrow.second D/SecondActivity: zwm, onCreate
媒体回放控制 媒体浏览
Android 媒体播放框架MediaSession分析与实践
- MediaBrowser:媒体浏览器
- MediaBrowserService:浏览器服务
- MediaSession:媒体会话
- MediaController:媒体控制器
//build.gradle(project)
buildscript {
repositories {
maven {
name 'AliYun Google Repository Proxy'
url 'https://maven.aliyun.com/repository/google'
}
maven {
name 'AliYun Jcenter Repository Proxy'
url 'https://maven.aliyun.com/repository/jcenter'
}
// google()
// jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
maven {
name 'AliYun Google Repository Proxy'
url 'https://maven.aliyun.com/repository/google'
}
maven {
name 'AliYun Jcenter Repository Proxy'
url 'https://maven.aliyun.com/repository/jcenter'
}
// google()
// jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
//build.gradle(module)
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.tomorrow.architetest"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
//AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tomorrow.architetest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
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=".MusicService">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
</application>
</manifest>
//MainActivity
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Button btnPlay;
private TextView textTitle;
private RecyclerView recyclerView;
private List<MediaBrowserCompat.MediaItem> list;
private DemoAdapter demoAdapter;
private LinearLayoutManager layoutManager;
private MediaBrowserCompat mBrowser;
private MediaControllerCompat mController;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "zwm, onCreate");
setContentView(R.layout.activity_demo);
mBrowser = new MediaBrowserCompat(
this,
new ComponentName(this, MusicService.class),//绑定浏览器服务
BrowserConnectionCallback,//设置连接回调
null
);
Log.d(TAG, "zwm, mBrowser: " + mBrowser);
btnPlay = (Button) findViewById(R.id.btn_play);
textTitle = (TextView) findViewById(R.id.text_title);
list = new ArrayList<>();
layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
demoAdapter = new DemoAdapter(this,list);
demoAdapter.setOnItemClickListener(new DemoAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Log.d(TAG, "zwm, onItemClick position: " + position);
Bundle bundle = new Bundle();
bundle.putString("title", list.get(position).getDescription().getTitle().toString());
mController.getTransportControls().playFromUri(
rawToUri(Integer.valueOf(list.get(position).getMediaId())),
bundle);
}
@Override
public void onItemLongClick(View view, int position) {
}
});
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(demoAdapter);
Log.d(TAG, "zwm, Browser发送连接请求");
mBrowser.connect();
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "zwm, onStart");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "zwm, onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "zwm, onDestroy");
Log.d(TAG, "zwm, Browser中断连接请求");
mBrowser.disconnect();
}
public void clickEvent(View view) {
Log.d(TAG, "zwm, clickEvent");
switch (view.getId()) {
case R.id.btn_play:
if(mController!=null){
handlerPlayEvent();
}
break;
}
}
/**
* 处理播放按钮事件
*/
private void handlerPlayEvent(){
Log.d(TAG, "zwm, handlerPlayEvent, state: " + mController.getPlaybackState().getState());
switch (mController.getPlaybackState().getState()){
case PlaybackStateCompat.STATE_PLAYING:
mController.getTransportControls().pause();
break;
case PlaybackStateCompat.STATE_PAUSED:
mController.getTransportControls().play();
break;
default:
mController.getTransportControls().playFromSearch("", null);
break;
}
}
/**
* 连接状态的回调接口,连接成功时会调用onConnected()方法
*/
private MediaBrowserCompat.ConnectionCallback BrowserConnectionCallback =
new MediaBrowserCompat.ConnectionCallback(){
@Override
public void onConnected() {
Log.d(TAG,"zwm, BrowserConnectionCallback onConnected");
if (mBrowser.isConnected()) {
//mediaId即为MediaBrowserService.onGetRoot的返回值
//若Service允许客户端连接,则返回结果不为null,其值为数据内容层次结构的根ID
//若拒绝连接,则返回null
String mediaId = mBrowser.getRoot();
Log.d(TAG,"zwm, BrowserConnectionCallback mediaId: " + mediaId);
//Browser通过订阅的方式向Service请求数据,发起订阅请求需要两个参数,其一为mediaId
//而如果该mediaId已经被其他Browser实例订阅,则需要在订阅之前取消mediaId的订阅者
//虽然订阅一个 已被订阅的mediaId 时会取代原Browser的订阅回调,但却无法触发onChildrenLoaded回调
//ps:虽然基本的概念是这样的,但是Google在官方demo中有这么一段注释...
// This is temporary: A bug is being fixed that will make subscribe
// consistently call onChildrenLoaded initially, no matter if it is replacing an existing
// subscriber or not. Currently this only happens if the mediaID has no previous
// subscriber or if the media content changes on the service side, so we need to
// unsubscribe first.
//大概的意思就是现在这里还有BUG,即只要发送订阅请求就会触发onChildrenLoaded回调
//所以无论怎样我们发起订阅请求之前都需要先取消订阅
mBrowser.unsubscribe(mediaId);
//之前说到订阅的方法还需要一个参数,即设置订阅回调SubscriptionCallback
//当Service获取数据后会将数据发送回来,此时会触发SubscriptionCallback.onChildrenLoaded回调
mBrowser.subscribe(mediaId, BrowserSubscriptionCallback);
try{
mController = new MediaControllerCompat(MainActivity.this,mBrowser.getSessionToken());
mController.registerCallback(ControllerCallback);
Log.d(TAG, "zwm, BrowserConnectionCallback mController: " + mController);
}catch (RemoteException e){
e.printStackTrace();
}
}
}
@Override
public void onConnectionFailed() {
Log.d(TAG,"zwm, BrowserConnectionCallback onConnectionFailed");
}
};
/**
* 向媒体浏览器服务(MediaBrowserService)发起数据订阅请求的回调接口
*/
private final MediaBrowserCompat.SubscriptionCallback BrowserSubscriptionCallback =
new MediaBrowserCompat.SubscriptionCallback(){
@Override
public void onChildrenLoaded(@NonNull String parentId,
@NonNull List<MediaBrowserCompat.MediaItem> children) {
Log.d(TAG,"zwm, BrowserSubscriptionCallback onChildrenLoaded, parentId: " + parentId);
//children 即为Service发送回来的媒体数据集合
for (MediaBrowserCompat.MediaItem item:children){
Log.d(TAG,"zwm, BrowserSubscriptionCallback title: " + item.getDescription().getTitle().toString());
list.add(item);
}
demoAdapter.notifyDataSetChanged();
}
};
/**
* 媒体控制器控制播放过程中的回调接口,可以用来根据播放状态更新UI
*/
private final MediaControllerCompat.Callback ControllerCallback =
new MediaControllerCompat.Callback() {
/***
* 音乐播放状态改变的回调
* @param state
*/
@Override
public void onPlaybackStateChanged(PlaybackStateCompat state) {
Log.d(TAG,"zwm, ControllerCallback onPlaybackStateChanged, state: " + state.getState());
switch (state.getState()){
case PlaybackStateCompat.STATE_NONE://无任何状态
textTitle.setText("");
btnPlay.setText("开始");
break;
case PlaybackStateCompat.STATE_PAUSED:
btnPlay.setText("开始");
break;
case PlaybackStateCompat.STATE_PLAYING:
btnPlay.setText("暂停");
break;
}
}
/**
* 播放音乐改变的回调
* @param metadata
*/
@Override
public void onMetadataChanged(MediaMetadataCompat metadata) {
CharSequence title = metadata.getDescription().getTitle();
textTitle.setText(title);
Log.d(TAG, "zwm, ControllerCallback onMetadataChanged title: " + title);
}
};
private Uri rawToUri(int id){
Log.d(TAG, "zwm, rawToUri id: " + id);
String uriStr = "android.resource://" + getPackageName() + "/" + id;
Log.d(TAG, "zwm, uriStr: " + uriStr);
return Uri.parse(uriStr);
}
}
//DemoAdapter
public class DemoAdapter extends RecyclerView.Adapter<DemoAdapter.ViewHolder> {
private Context context;
private List<MediaBrowserCompat.MediaItem> list;
private OnItemClickListener mOnItemClickListener;
class ViewHolder extends RecyclerView.ViewHolder {
// TODO: 声明组件
TextView textTitle;
public ViewHolder(View view) {
super(view);
// TODO: 注册组件,view.findViewById(R.id.xxx)
textTitle = view.findViewById(R.id.text_title);
}
}
public DemoAdapter(Context context, List<MediaBrowserCompat.MediaItem> list) {
this.context = context;
this.list = list;
}
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
public void setOnItemClickListener(OnItemClickListener mOnItemClickListener) {
this.mOnItemClickListener = mOnItemClickListener;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// TODO: 为对应itemViewId赋值,例:R.layout.xxx
int itemViewId = R.layout.item_music;
ViewHolder holder = new ViewHolder(LayoutInflater.from(context).inflate(itemViewId, parent, false));
return holder;
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
// TODO: 绑定组件的事件
holder.textTitle.setText(list.get(position).getDescription().getTitle());
// 如果设置了回调,则设置点击事件
if (mOnItemClickListener != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos = holder.getLayoutPosition();
mOnItemClickListener.onItemClick(holder.itemView, pos);
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
int pos = holder.getLayoutPosition();
mOnItemClickListener.onItemLongClick(holder.itemView, pos);
return true;
}
});
}
}
@Override
public int getItemCount() {
return list.size();
}
}
//MusicService
public class MusicService extends MediaBrowserServiceCompat {
private MediaSessionCompat mSession;
private MediaPlayer mMediaPlayer;
private PlaybackStateCompat mPlaybackState;
private static final String TAG = "MusicService";
public static final String MEDIA_ID_ROOT = "";
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG,"zwm, onCreate");
mPlaybackState = new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_NONE,0,1.0f)
.build();
Log.d(TAG,"zwm, mPlaybackState state: " + mPlaybackState.getState());
mSession = new MediaSessionCompat(this,"MusicService");
mSession.setCallback(SessionCallback);
mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mSession.setPlaybackState(mPlaybackState);
Log.d(TAG,"zwm, mSession: " + mSession);
//设置token后会触发MediaBrowserCompat.ConnectionCallback的回调方法
//表示MediaBrowser与MediaBrowserService连接成功
setSessionToken(mSession.getSessionToken());
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnPreparedListener(PreparedListener);
mMediaPlayer.setOnCompletionListener(CompletionListener);
Log.d(TAG,"zwm, mMediaPlayer: " + mMediaPlayer);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG,"zwm, onDestroy");
if (mMediaPlayer != null) {
mMediaPlayer.release();
mMediaPlayer = null;
}
if (mSession != null) {
mSession.release();
mSession = null;
}
}
@Nullable
@Override
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) {
Log.d(TAG,"zwm, onGetRoot, clientPackageName: " + clientPackageName + ", clientUid: " + clientUid);
return new BrowserRoot(MEDIA_ID_ROOT, null);
}
@Override
public void onLoadChildren(@NonNull String parentId, @NonNull final Result<List<MediaBrowserCompat.MediaItem>> result) {
Log.d(TAG,"zwm, onLoadChildren, parentId: " + parentId);
//将信息从当前线程中移除,允许后续调用sendResult方法
result.detach();
//我们模拟获取数据的过程,真实情况应该是异步从网络或本地读取数据
MediaMetadataCompat metadata = new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, ""+R.raw.testmusic)
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, "Over The Horizontal")
.build();
ArrayList<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();
mediaItems.add(createMediaItem(metadata));
//向Browser发送数据
result.sendResult(mediaItems);
Log.d(TAG,"zwm, 向Browser发送数据");
}
private MediaBrowserCompat.MediaItem createMediaItem(MediaMetadataCompat metadata){
Log.d(TAG,"zwm, createMediaItem");
return new MediaBrowserCompat.MediaItem(
metadata.getDescription(),
MediaBrowserCompat.MediaItem.FLAG_PLAYABLE
);
}
/**
* 响应控制器指令的回调
*/
private android.support.v4.media.session.MediaSessionCompat.Callback SessionCallback = new MediaSessionCompat.Callback(){
/**
* 响应MediaController.getTransportControls().play
*/
@Override
public void onPlay() {
Log.d(TAG,"zwm, SessionCallback onPlay");
if(mPlaybackState.getState() == PlaybackStateCompat.STATE_PAUSED){
mMediaPlayer.start();
mPlaybackState = new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_PLAYING,0,1.0f)
.build();
mSession.setPlaybackState(mPlaybackState);
}
}
/**
* 响应MediaController.getTransportControls().onPause
*/
@Override
public void onPause() {
Log.d(TAG,"zwm, SessionCallback onPause");
if(mPlaybackState.getState() == PlaybackStateCompat.STATE_PLAYING){
mMediaPlayer.pause();
mPlaybackState = new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_PAUSED,0,1.0f)
.build();
mSession.setPlaybackState(mPlaybackState);
}
}
/**
* 响应MediaController.getTransportControls().playFromUri
* @param uri
* @param extras
*/
@Override
public void onPlayFromUri(Uri uri, Bundle extras) {
Log.d(TAG,"zwm, SessionCallback onPlayFromUri, uri: " + uri);
try {
switch (mPlaybackState.getState()){
case PlaybackStateCompat.STATE_PLAYING:
case PlaybackStateCompat.STATE_PAUSED:
case PlaybackStateCompat.STATE_NONE:
mMediaPlayer.reset();
mMediaPlayer.setDataSource(MusicService.this,uri);
Log.d(TAG,"zwm, prepare");
mMediaPlayer.prepare();//准备同步
mPlaybackState = new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_CONNECTING,0,1.0f)
.build();
Log.d(TAG,"zwm, mSession setPlaybackState STATE_CONNECTING");
mSession.setPlaybackState(mPlaybackState);
//我们可以保存当前播放音乐的信息,以便客户端刷新UI
Log.d(TAG,"zwm, mSession setMetadata");
mSession.setMetadata(new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_TITLE,extras.getString("title"))
.build()
);
break;
}
}catch (IOException e){
e.printStackTrace();
}
}
@Override
public void onPlayFromSearch(String query, Bundle extras) {
Log.d(TAG,"zwm, SessionCallback onPlayFromSearch");
}
};
/**
* 监听MediaPlayer.prepare()
*/
private MediaPlayer.OnPreparedListener PreparedListener = new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
Log.d(TAG,"zwm, onPrepared, start play");
mMediaPlayer.start();
mPlaybackState = new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_PLAYING,0,1.0f)
.build();
Log.d(TAG,"zwm, mSession setPlaybackState STATE_PLAYING");
mSession.setPlaybackState(mPlaybackState);
}
} ;
/**
* 监听播放结束的事件
*/
private MediaPlayer.OnCompletionListener CompletionListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
Log.d(TAG,"zwm, onCompletion");
mPlaybackState = new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_NONE,0,1.0f)
.build();
mSession.setPlaybackState(mPlaybackState);
mMediaPlayer.reset();
}
};
}
//activity_demo.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f2f2f2">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<LinearLayout
android:id="@+id/view_controller"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_alignParentBottom="true"
android:orientation="vertical"
android:background="#fff">
<TextView
android:id="@+id/text_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:textSize="16sp"
android:text=""/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp">
<Button
android:id="@+id/btn_play"
android:text="播放"
android:onClick="clickEvent"
style="@style/demo_controller_btn" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
//item_music.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#fff"
android:layout_marginBottom="5dp"
android:elevation="3dp">
<TextView
android:id="@+id/text_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="15dp"/>
</RelativeLayout>
</RelativeLayout>
//styles.xml
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="demo_controller_btn">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_weight">1</item>
</style>
</resources>
//colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>
//strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>
//音频文件
app/src/main/res/raw/testmusic.mp3
//输出log
2019-11-19 20:57:16.501 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, onCreate
2019-11-19 20:57:16.637 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, mBrowser: android.support.v4.media.MediaBrowserCompat@6bf292f
2019-11-19 20:57:16.646 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, Browser发送连接请求
2019-11-19 20:57:16.652 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, onStart
2019-11-19 20:57:16.816 9073-9073/com.tomorrow.architetest D/MusicService: zwm, onCreate
2019-11-19 20:57:16.817 9073-9073/com.tomorrow.architetest D/MusicService: zwm, mPlaybackState state: 0
2019-11-19 20:57:16.828 9073-9073/com.tomorrow.architetest D/MusicService: zwm, mSession: android.support.v4.media.session.MediaSessionCompat@f651a79
2019-11-19 20:57:16.835 9073-9073/com.tomorrow.architetest D/MusicService: zwm, mMediaPlayer: android.media.MediaPlayer@eb353be
2019-11-19 20:57:16.953 9073-9073/com.tomorrow.architetest D/MusicService: zwm, onGetRoot, clientPackageName: com.tomorrow.architetest, clientUid: 10405
2019-11-19 20:57:16.955 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, BrowserConnectionCallback onConnected
2019-11-19 20:57:16.956 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, BrowserConnectionCallback mediaId:
2019-11-19 20:57:16.958 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, BrowserConnectionCallback mController: android.support.v4.media.session.MediaControllerCompat@caa9fb1
2019-11-19 20:57:16.959 9073-9073/com.tomorrow.architetest D/MusicService: zwm, onLoadChildren, parentId:
2019-11-19 20:57:16.961 9073-9073/com.tomorrow.architetest D/MusicService: zwm, createMediaItem
2019-11-19 20:57:16.962 9073-9073/com.tomorrow.architetest D/MusicService: zwm, 向Browser发送数据
2019-11-19 20:57:16.963 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, BrowserSubscriptionCallback onChildrenLoaded, parentId:
2019-11-19 20:57:16.963 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, BrowserSubscriptionCallback title: Over The Horizontal
2019-11-19 20:57:21.896 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, onItemClick position: 0
2019-11-19 20:57:21.897 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, rawToUri id: 2131558400
2019-11-19 20:57:21.897 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, uriStr: android.resource://com.tomorrow.architetest/2131558400
2019-11-19 20:57:21.898 9073-9073/com.tomorrow.architetest D/MusicService: zwm, SessionCallback onPlayFromUri, uri: android.resource://com.tomorrow.architetest/2131558400
2019-11-19 20:57:21.911 9073-9073/com.tomorrow.architetest D/MusicService: zwm, prepare
2019-11-19 20:57:21.968 9073-9073/com.tomorrow.architetest D/MusicService: zwm, mSession setPlaybackState STATE_CONNECTING
2019-11-19 20:57:21.970 9073-9073/com.tomorrow.architetest D/MusicService: zwm, mSession setMetadata
2019-11-19 20:57:21.974 9073-9073/com.tomorrow.architetest D/MusicService: zwm, onPrepared, start play
2019-11-19 20:57:21.977 9073-9073/com.tomorrow.architetest D/MusicService: zwm, mSession setPlaybackState STATE_PLAYING
2019-11-19 20:57:21.978 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, ControllerCallback onPlaybackStateChanged, state: 8
2019-11-19 20:57:21.981 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, ControllerCallback onMetadataChanged title: Over The Horizontal
2019-11-19 20:57:21.982 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, ControllerCallback onPlaybackStateChanged, state: 3
2019-11-19 20:57:33.238 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, clickEvent
2019-11-19 20:57:33.239 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, handlerPlayEvent, state: 3
2019-11-19 20:57:33.245 9073-9073/com.tomorrow.architetest D/MusicService: zwm, SessionCallback onPause
2019-11-19 20:57:33.251 9073-9073/com.tomorrow.architetest D/MainActivity: zwm, ControllerCallback onPlaybackStateChanged, state: 2
网友评论