Android 视频播放器 应用层设计 考虑事项(一)

一、屏保:

视频播放状态:保持屏幕点亮;

非播放状态: 按照机器【显示屏保】设置;

关键函数:

 private void updateSurfaceScreenOn() {
        if (mSurfaceHolder != null) {
        	 Log.d(TAG, "SCREEN_ON updateSurfaceScreenOn boolean ="+(mScreenOnWhilePlaying && mStayAwake));
            mSurfaceHolder.setKeepScreenOn(mScreenOnWhilePlaying && mStayAwake);
        }
    }
    
    public void setScreenOnWhilePlaying(boolean screenOn) {
        if (mScreenOnWhilePlaying != screenOn) {
            mScreenOnWhilePlaying = screenOn;
            updateSurfaceScreenOn();
        }
    }
    
    public void setWakeMode(Context context, int mode) {
        boolean washeld = false;
        if (mWakeLock != null) {
            if (mWakeLock.isHeld()) {
                washeld = true;
                mWakeLock.release();
            }
            mWakeLock = null;
        }

        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(mode|PowerManager.ON_AFTER_RELEASE, MediaPlayer.class.getName());
        mWakeLock.setReferenceCounted(false);
        if (washeld) {
            mWakeLock.acquire();
        }
    }
    
    private void stayAwake(boolean awake) {
        if (mWakeLock != null) {
            if (awake && !mWakeLock.isHeld()) {
            	  Log.d(TAG, "SCREEN_ON stayAwake mWakeLock.acquire");
                mWakeLock.acquire();
            } else if (!awake && mWakeLock.isHeld()) {
            	  Log.d(TAG, "SCREEN_ON stayAwake mWakeLock.release");
                mWakeLock.release();
            }
        }
        mStayAwake = awake;
        updateSurfaceScreenOn();
    }

权限<uses-permission android:name="android.permission.WAKE_LOCK"/>   

setScreenOnWhilePlaying

Controls whether the view's window should keep the screen on while visible.


电池管理

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
 PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
 wl.acquire();
   ..screen will stay on during this section..
 wl.release();

二、生命周期、异常状态(切换后台、接听电话、闹铃响)

关键字 :SurfaceView与AudioTrack的管理


三、异常处理

http://www.blogjava.net/freeman1984/archive/2007/09/27/148850.html

勿以恶小而为之,勿以善小而不为。

以下【陋习】需注意:

1.  捕获了异常却不对异常进行处理,printStackTrace只是在调试阶段有帮助。

2. 不指定具体的异常,通通用Exception。不利于对不同的异常作特定处理。

以下程序有几个典型错误?

1 OutputStreamWriter out = ...
2 java.sql.Connection conn = ...
3 try { // ⑸
4  Statement stat = conn.createStatement();
5  ResultSet rs = stat.executeQuery(
6   "select uid, name from user");
7  while (rs.next())
8  {
9   out.println("ID:" + rs.getString("uid") // ⑹
10    ",姓名:" + rs.getString("name"));
11  }
12  conn.close(); // ⑶
13  out.close();
14 }
15 catch(Exception ex) // ⑵
16 {
17  ex.printStackTrace(); //⑴,⑷
18 }




四、接入点切换和管理

开源项目APNDroid


五、自定义View


六、数据持久化


七、异步准备工作

关键字:异步缓冲、 Asynchronous Preparation、

To avoid hanging your UI thread, spawn another thread toprepare theMediaPlayer and notify the main thread when done. However, whileyou could write the threading logicyourself, this pattern is so common when usingMediaPlayer that the frameworksupplies a convenient way to accomplish this task by using theprepareAsync() method. This methodstarts preparing the media in the background and returns immediately. When the mediais done preparing, theonPrepared()method of theMediaPlayer.OnPreparedListener, configured throughsetOnPreparedListener() is called.


八、面向接口

关键字:面向接口编程;用接口还是用抽象类;

开发系统时,主体构架使用接口,接口构成系统的骨架。这样就可以通过更换实现接口的类来更换系统的实现。

播放器提供这些接口,供客户开发者通过implements实现这些接口完成指定的操作。

我们就要使用面向接口编程的原则,让接口构成系统的骨架,以便达到更换实现接口的类就可以更换系统的实现的目的。

 /**
     * Interface definition for a callback to be invoked when playback of
     * a media source has completed.
     */
    public interface OnCompletionListener{
        void onCompletion(PlayerEngineImpl player);
    }
    
    /**
     * Interface definition of a callback to be invoked indicating buffering
     * status of a media resource being streamed over the network.
     */
    public interface OnBufferingUpdateListener {
        /**
         * Called to update status in buffering a media stream.
         * 
         * @param player the PlayerEngineImpl the update pertains to
         * @param percent the percentage (0-100) of the buffer
         *                that has been filled thus far
         */
        void onBufferingUpdate(int percent);
        
        /**
         * get media information complete, begin to buffering a media stream.
         * 
         * @param player the PlayerEngineImpl the update pertains to
         */
        void onBufferingBegin(PlayerEngineImpl player);
        
        /**
         * buffering complete, the player change to STARTED state. after this method called, isPlaying() will return true
         * 
         * @param player the PlayerEngineImpl the update pertains to
         */
        void onBufferingComplete();
    }
    
    /**
     * Interface definition for a callback to be invoked when play start.
     */
    public interface OnStartPlayListener
    {
        /**
         * Called when the end of a media play start.
         * 
         * @param player the PlayerEngineImpl that reached the end of the file
         */
        void OnStartPlay(PlayerEngineImpl player);
    }
    
    /**
     * Interface definition of a callback to be invoked when the
     * video size is first known or updated
     */
    public interface OnVideoSizeChangedListener
    {
        /**
         * Called to indicate the video size
         * 
         * @param player        the TMPCPlayer associated with this callback
         * @param width     the width of the video
         * @param height    the height of the video
         */
        public void onGetVideoSize(PlayerEngineImpl player, int width, int height);
    }
    
    /**
     * Interface definition of a callback to be invoked when there
     * has been an error during an asynchronous operation (other errors
     * will throw exceptions at method call time).
     */
    public interface OnErrorListener
    {
        boolean onError(PlayerEngineImpl player, int what, int extra);
    }
    
    public interface OnMediaInfoListener{
    	public void onGetMediaInfo(int fileLength, int isLive);
    }
    
    
    public interface OnPlayProgressListener{
    	public void onPlayProgress(int pos);
    }
    
    /**
     * Register a callback to be invoked when the end of a media source
     * has been reached during playback.
     *
     * @param listener the callback that will be run
     */
	public void setOnCompletionListener(PlayerEngineImpl.OnCompletionListener listener) {
		mOnCompletionListener = listener;
	}
	
	/**
     * Register a callback to be invoked when the status of a network
     * stream's buffer has changed.
     *
     * @param listener the callback that will be run.
     */
	public void setOnBufferingUpdateListener(PlayerEngineImpl.OnBufferingUpdateListener listener) {
		mOnBufferingUpdateListener = listener;
	}
	
	/**
     * Register a callback to be invoked when play start.
     *
     * @param listener the callback that will be run.
     */
	public void setOnStartPlayListener(PlayerEngineImpl.OnStartPlayListener listener) {
		mOnStartPlayListener = listener;
	}
	
    /**
     * Register a callback to be invoked when the video size is
     * known or updated.
     * 
     * @param listener the callback that will be run
     */
	public void setOnVideoSizeChangedListener(PlayerEngineImpl.OnVideoSizeChangedListener listener) {
		mOnVideoSizeChangedListener = listener;
	}
	
    /**
     * Register a callback to be invoked when an error has happened
     * during an asynchronous operation.
     * 
     * @param listener the callback that will be run
     */
	public void setOnErrorListener(PlayerEngineImpl.OnErrorListener listener) {
		mOnErrorListener = listener;
	}
	
	public void setOnMediaInfoListener(PlayerEngineImpl.OnMediaInfoListener listener) {
		mOnMediaInfoListener = listener;
	}
	
	public void setOnPlayProgressListener(PlayerEngineImpl.OnPlayProgressListener listener) {
		mOnPlayProgressListener = listener;
	}



九、包装Native方法

关键字:弱引用WeakReference传递类对象

等待播放回放前采用synchronously或asynchronously方式


十、URL正确性检测

关键字:正则表达式


十一、JVM性能

http://developer.android.com/guide/practices/design/jni.html

http://java.sun.com/docs/books/jni/html/jniTOC.html

1. 垃圾回收机制,弱引用

2.基本概念:两个重要的数据结构:JavaVM 和 JNIEnv;JNIEnv是用作本地线程,不能在线程间共享。当在某处代码中无法获得JNIEnv时,可以通过共享JavaVM,使用GetEnv
来获得JNIEnv。

例如,以下函数运行在native层的消息回调里,并非在JNI的本地线程中。所以用到了GetEnv 和 AttachCurrentThread;

GetEnv returns NULL if the current thread is not attached to the VM. (Remember, JNIEnv is thread-specific.) If you created the thread yourself, you will need to use the JNI AttachCurrentThread function to attach it.

Both of these require a JavaVM pointer. There's only one of these per process, so you can get it during JNI_OnLoad or a setup call from your program (GetJavaVM function) when you have a JNIEnv passed in.

void Notify_Create_AudioTrack(int freq, int format, int channels)
{
	bool  isAttacked = false;
	JNIEnv *env = AndroidRuntime::getJNIEnv();
	if(NULL == g_jni_tmpc.JVM)
	{
		LOGE("JVM == NULL");
		return ;
	}
	int status = (g_jni_tmpc.JVM)->GetEnv((void **) &env, g_jni_tmpc.JNI_VERSION);
	if(status < 0) {
		status = g_jni_tmpc.JVM->AttachCurrentThread(&env, NULL);
		if(status < 0) {
		   LOGE("Call_Back_Invoke: failed to attach current thread");
		    return;
		}
		isAttacked = true;
	}
	
	env->CallVoidMethod(g_jni_tmpc.obj, g_fields.jf_CreatAudioTrack, freq, format, channels);

	g_jni_tmpc.audio_param.nBufSize = env->GetIntField(g_jni_tmpc.obj, g_fields.jf_audio_bufsize);
	g_jni_tmpc.audio_param.nHardwareBufTime = g_jni_tmpc.audio_param.nBufSize*1000/(freq*channels*format/8);
	LOGI("Notify_Create_AudioTrack nBufSize=%d, nHardwareBufTime=%d", g_jni_tmpc.audio_param.nBufSize, g_jni_tmpc.audio_param.nHardwareBufTime);

	if(isAttacked) 
	{
		(g_jni_tmpc.JVM)->DetachCurrentThread();
	}
}

3.在线程中如何处理JavaVM:Until a thread isattached, it has no JNIEnv, and cannot make JNI calls.

4. 函数表、寻找IDs、在native层执行函数方法

5.Global References

JNI keeping a global reference to an object, accessing it with other JNI methods.

Every argument passed to a native method, and almost every object returnedby a JNI function is a "local reference". This means that it's valid for theduration of the current native method in the current thread.Even if the object itself continues to live on after the native methodreturns, the reference is not valid.

JNIEXPORT void JNICALL Java_com_******_nativeSetup
(JNIEnv *env, jobject thiz, jobject weak_this)
{
	// Hold onto the MediaPlayer class for use in calling the static method
	// that posts events to the application thread.
	jclass clazz = env->GetObjectClass(thiz);
	if (clazz == NULL) {
	    LOGE("Can't find android/media/MediaPlayer");
	    return;
	}
	g_jni_tmpc.cls  = (jclass)env->NewGlobalRef(clazz);

	g_jni_tmpc.obj  = env->NewGlobalRef(thiz);
	// We use a weak reference so the MediaPlayer object can be garbage collected.
	// The reference is only used as a proxy for callbacks.
	g_jni_tmpc.weak_obj  = env->NewGlobalRef(weak_this);

	Get_JNI_FieldID(env, g_jni_tmpc.obj);

}


5.如何加载共享库:System.loadLibrary ;共享库中的函数在JNI_OnLoad中注册;



十二、播放器状态变化



十三、后台播放

关键字:Streaming video playback,Service,background、communicate between the activity and the service

http://marakana.com/forums/android/examples/60.html

1.把对Player实例的控制放在Service中:onBind  onCreate  onStart (暂停,播放,上一首,下一首)... onDestroy ;

2. 在Activity中启动Service: startService(intent);



十四、播放模式


十五、消息交互(Native/JNI/JAVA)

关键在于,如何把Nactive层的消息,传递到应用程序主线程去处理,从而修改界面的显示状态。

uses the EventHandler system to post the event back to the main app thread.

多线程交互Handler机制:在Android平台中,新启动的线程是无法访问Activity里的Widget的,当然也不能将运行状态外送出来,这就需要有Handler机制进行消息的传递了。handler通俗一点讲就是用来在各个进程之间发送数据的处理对象。

在任何进程中,只要获得了另一个进程的handler则可以通过handler.sendMessage(message)方法向那个进程发送数据。


difference between getMainLooper() and Looper.myLooper()?

Looper机制:用于在线程中运行一个消息循环,线程默认是无Loop。通过prepare()和loop()来启动消息循环。典型用法:

 class LooperThread extends Thread {
      public Handler mHandler;

      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }

判断是否在UI线程:

 if (Looper.getMainLooper().equals(Looper.myLooper())) {
     // UI thread
 } else {
     // Non UI thread
 }

有X线程和Main线程,在X线程向Main线程转发消息.有几种实现方法?

方法一、在X线程拥有Main线程的handler

方法二、在X线程获得Main线程的Looper

======================小华丽丽的分割线====================

1.JNI接口模块(com....cpp)

2.Java层播放器实现模块(player)

3.java层界面模块(UI)

以上三者的交互方式.


十六、垃圾回收

关键字:garbage collection、System.gc()、Memory Management、Out-Of-Memory errors/exceptions

1.  System.gc().invoke it manually?

2. 避免Out-Of-Memory errors/exceptions应该注意的问题

(1)合适的图片尺寸

never load a full-sized image unless it's
absolutely necessary. Load an image with the size that fits your UI (e..g
don't load a 2000x3000 pixel image if you want to show it in a view that is
never larger than 400x600 pixels...)


(2)
list重用

Do you properly re-use the list-*item* views in your list-view?

E.g. in your list-adapter 'getView()' method:
public void View getView(int position, View convertView, ViewGroup parent) {
  if (convertView == null) {
    convertView = inflater.inflate(R.layout.list_item_layout, null);
    ...

  }



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值