zoukankan      html  css  js  c++  java
  • AppWidget深入 AndroidMusic中的MediaAppWidgetProvider注释

    怀着沉痛的心情发表这篇博客,不知道是不是我在CSDN上发表的最后一篇。这两天打算研究framework层和Linux的一些东西,顺便总结11月份和12月份的一些知识!!

     // 本来以为AppWidget是通过AIDL和MediaPlaybackService通信了,后来看了看不是
     // 如果一些基础的看不懂的话可以看看我上一篇
    public class MediaAppWidgetProvider extends AppWidgetProvider {
        static final String TAG = "MusicAppWidgetProvider";
        
        public static final String CMDAPPWIDGETUPDATE = "appwidgetupdate";
    
        private static MediaAppWidgetProvider sInstance;
        
    	// 单例模式,当你点击一个播放时所有的AppWidget都播放
        static synchronized MediaAppWidgetProvider getInstance() {
            if (sInstance == null) {
                sInstance = new MediaAppWidgetProvider();
            }
            return sInstance;
        }
    
    	// 每次在创建AppWiget的时候或者到了更新频率的时候执行此函数
        @Override
        public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    	    // 设置AppWidget的一些默认设置,并且传递一些相关参数
            defaultAppWidget(context, appWidgetIds);
            
            // Send broadcast intent to any running MediaPlaybackService so it can
            // wrap around with an immediate update.
    		// 发送广播消息通知正在运行的MediaPlaybackService以响应AppWidget
            Intent updateIntent = new Intent(MediaPlaybackService.SERVICECMD);
            updateIntent.putExtra(MediaPlaybackService.CMDNAME,
                    MediaAppWidgetProvider.CMDAPPWIDGETUPDATE);
            updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
            updateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
    		// 发送广播
            context.sendBroadcast(updateIntent);
        }
        
        /**
         * Initialize given widgets to default state, where we launch Music on default click
         * and hide actions if service not running.
         */
    	 // 初始化widget的默认状态,在onUpdate()里就开始执行
        private void defaultAppWidget(Context context, int[] appWidgetIds) {
            final Resources res = context.getResources();
    		// 获得AppWidget上的布局视图,然后通过views获得控件id
            final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.album_appwidget);
            
            views.setViewVisibility(R.id.title, View.GONE);
            views.setTextViewText(R.id.artist, res.getText(R.string.widget_initial_text));
    
    		// 开始创建Widget的时候默认是不播放音乐的
            linkButtons(context, views, false /* not playing */);
            pushUpdate(context, appWidgetIds, views);
        }
        
    	// 刷新Widget的状态
        private void pushUpdate(Context context, int[] appWidgetIds, RemoteViews views) {
            // Update specific list of appWidgetIds if given, otherwise default to all
            final AppWidgetManager gm = AppWidgetManager.getInstance(context);
            if (appWidgetIds != null) {
                gm.updateAppWidget(appWidgetIds, views);
            } else {
                gm.updateAppWidget(new ComponentName(context, this.getClass()), views);
            }
        }
        
        /**
         * Check against {@link AppWidgetManager} if there are any instances of this widget.
         */
    	// 通过AppWidgetManager检查是否有存在AppWidget
        private boolean hasInstances(Context context) {	    
            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
            int[] appWidgetIds = appWidgetManager.getAppWidgetIds(
                    new ComponentName(context, this.getClass()));
            return (appWidgetIds.length > 0);
        }
    
        /**
         * Handle a change notification coming over from {@link MediaPlaybackService}
         */
    	 // 这个函数是在MediaPlaybackService中调用,作用是检查播放状态是否发生变化
        void notifyChange(MediaPlaybackService service, String what) {
            if (hasInstances(service)) {
                if (MediaPlaybackService.META_CHANGED.equals(what) ||
                        MediaPlaybackService.PLAYSTATE_CHANGED.equals(what)) {
    				// 如果状态有改变,那么更新AppWidget的状态
                    performUpdate(service, null);
                }
            }
        }
        
        /**
         * Update all active widget instances by pushing changes 
         */
    	// 更新所有AppWidget的状态
        void performUpdate(MediaPlaybackService service, int[] appWidgetIds) {
    		// 获取所有的相关资源
            final Resources res = service.getResources();
    		// RemoteViews用于获取AppWidget的相关控件Id,及更新相关控件
            final RemoteViews views = new RemoteViews(service.getPackageName(), R.layout.album_appwidget);
            
    		// 通过MediaPlaybackService获取歌曲相关信息,歌曲名,歌手名,SD卡的状态
            CharSequence titleName = service.getTrackName();
            CharSequence artistName = service.getArtistName();
            CharSequence errorState = null;
            
            // Format title string with track number, or show SD card message
    		// 获取扩展介质(一般指SD卡)状态以更新变量 errorState
    		// 各种状态可以查看string中值自己确定
            String status = Environment.getExternalStorageState();
            if (status.equals(Environment.MEDIA_SHARED) ||
                    status.equals(Environment.MEDIA_UNMOUNTED)) {
                if (android.os.Environment.isExternalStorageRemovable()) {
                    errorState = res.getText(R.string.sdcard_busy_title);
                } else {
                    errorState = res.getText(R.string.sdcard_busy_title_nosdcard);
                }
            } else if (status.equals(Environment.MEDIA_REMOVED)) {
                if (android.os.Environment.isExternalStorageRemovable()) {
                    errorState = res.getText(R.string.sdcard_missing_title);
                } else {
                    errorState = res.getText(R.string.sdcard_missing_title_nosdcard);
                }
            } else if (titleName == null) {
                errorState = res.getText(R.string.emptyplaylist);
            }
            
            if (errorState != null) {
                // Show error state to user
    			// 如果SD卡状态不符,歌曲名的TextView隐藏,在艺术家TextView中显示errorState
                views.setViewVisibility(R.id.title, View.GONE);
                views.setTextViewText(R.id.artist, errorState);
                
            } else {
                // No error, so show normal titles
    			// 正常显示
                views.setViewVisibility(R.id.title, View.VISIBLE);
                views.setTextViewText(R.id.title, titleName);
                views.setTextViewText(R.id.artist, artistName);
            }
            
            // Set correct drawable for pause state
    		// 判断歌曲是否在播放,如果在播放是播放按钮显示为暂停,反之
            final boolean playing = service.isPlaying();
            if (playing) {
                views.setImageViewResource(R.id.control_play, R.drawable.ic_appwidget_music_pause);
            } else {
                views.setImageViewResource(R.id.control_play, R.drawable.ic_appwidget_music_play);
            }
    
            // Link actions buttons to intents
    		// 根据Widget的布局事件,发送相应的Intent
            linkButtons(service, views, playing);
            
    		// 更新所有AppWidget的状态
            pushUpdate(service, appWidgetIds, views);
        }
    
        /**
         * Link up various button actions using {@link PendingIntents}.
         * 
         * @param playerActive True if player is active in background, which means
         *            widget click will launch {@link MediaPlaybackActivity},
         *            otherwise we launch {@link MusicBrowserActivity}.
         */
        private void linkButtons(Context context, RemoteViews views, boolean playerActive) {
            // Connect up various buttons and touch events
            Intent intent;
    		// 关于PendingIntent的相关说明请看我上一篇说明
            PendingIntent pendingIntent;
            
    		// 获取MediaPlaybackService的组件名称
            final ComponentName serviceName = new ComponentName(context, MediaPlaybackService.class);
            
            if (playerActive) {
    		    // 如果正在播放,则启动播放器Activity 即MediaPlaybackActivity
                intent = new Intent(context, MediaPlaybackActivity.class);
                pendingIntent = PendingIntent.getActivity(context,
                        0 /* no requestCode */, intent, 0 /* no flags */);
                views.setOnClickPendingIntent(R.id.album_appwidget, pendingIntent);
            } else {
    			// 如果没有在播放则启动音乐浏览Activity 即MusicBrowserActivity
                intent = new Intent(context, MusicBrowserActivity.class);
                pendingIntent = PendingIntent.getActivity(context,
                        0 /* no requestCode */, intent, 0 /* no flags */);
                views.setOnClickPendingIntent(R.id.album_appwidget, pendingIntent);
            }
            
            intent = new Intent(MediaPlaybackService.TOGGLEPAUSE_ACTION);
            intent.setComponent(serviceName);
            pendingIntent = PendingIntent.getService(context,
                    0 /* no requestCode */, intent, 0 /* no flags */);
            views.setOnClickPendingIntent(R.id.control_play, pendingIntent);
            
    		// 获取动作,播放下一首歌曲
            intent = new Intent(MediaPlaybackService.NEXT_ACTION);
            intent.setComponent(serviceName);
            pendingIntent = PendingIntent.getService(context,
                    0 /* no requestCode */, intent, 0 /* no flags */);
            views.setOnClickPendingIntent(R.id.control_next, pendingIntent);
        }
    } 

    GoogleDriver

  • 相关阅读:
    远程线程注入与CreateRemoteThread
    游戏修改器编写原理
    软件保护技术常见保护技巧
    反跟踪技术
    C++用static声明的函数和变量小结
    PE文件格式分析及修改
    MMX指令集在C++中的使用
    HOOKAPI之修改IAT法则
    如何获取 程序加载后的内存起始地址
    Java线程中断的本质和编程原则
  • 原文地址:https://www.cnblogs.com/wuyida/p/6300649.html
Copyright © 2011-2022 走看看