美文网首页
新特性与行为变更 -- 代码1

新特性与行为变更 -- 代码1

作者: TomyZhang | 来源:发表于2019-11-23 16:46 被阅读0次

AlarmManager

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

Support Library -- 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

相关文章

网友评论

      本文标题:新特性与行为变更 -- 代码1

      本文链接:https://www.haomeiwen.com/subject/rmvhwctx.html