1.1 surface的使用
使用surface时,包含了三个类的使用,分别是surface,surfaceView,SurfaceHolder.
- Sueface类:每一个surface在系统内部都对应了一段单独的视频缓冲区,surface就是这段缓冲区的一个类描述,可以通过surface提供的方法直接给这段缓冲区上绘图。
- SurfaceHolder类:该类是一个接口,主要是抽象出用于控制其对应的surface的标准方法。
- surfaceView类:surface本身并不是和GUI协同工作的,GUI需要为应用程序单独分配一段显示区,而这段显示区在一定程度上还要受到GUI的管理,比如,创建,销毁显示区;同时该显示区在一定程度上还需要和其他视图之间具有某种联系,以备GUI重新绘制界面时所需,而surface本身是一段显示缓冲区,内部并不具备这些操作的属性,surfaceView就是为满足这些操作而设计的。
3者的关系可如下所示:
GUI系统——>surfaceView——>surface
应用程序——>surfaceHolder——>surface
GUI系统通过surfaceView来操作surface,而应用程序则是通过surfaceHolder来操作surface,一般情况下,无论是应用程序还是系统,都不直接调用surface这个接口。
从单纯绘制界面的角度来看surface,在标准的GUI视图中,要绘制对应的界面只能在onDraw()方法中,因为只有在onDraw()方法中才能得到画布canvas对象。对于surface来讲,处于应用程序的考虑,子程序可以获得一个独立的surface对象,可以随时任意的绘制界面。因此surface有一个获取画布的接口,lockCanvas(),该方法返回此surface对应的画布,子程序是可以自由的享受这个surface,满足应用的界面需求。
使用surface的大多数场合是游戏开发,因为在游戏开发中,往往需要一些特别地界面元素,包括图形,变换和动画等内容。如何在surface上绘图:::
声明surface,可以在layout.xml文件中使用<surfaceView>关键字定义一个Surface对象,就像是用Button定义一个按钮一样,在xml中声明一个surface以后,调用SetContentView()方法创建该对象时,系统不会立即创建,即surface的创建时异步的,只有当surface创建好之后,用户才能使用该surface,子程序绘制surface时,一般通过surfaceHolder对象,每个surface对应一个Holder,当surface创建好后,会回调对应的Holder对象定义的回调函数,这就是SurfaceHolder.Callback接口。
该接口包含了三个方法,分别是:
- SurfaceCreated(),当surface创建好后,会调用该函数,一般可以在干函数内部设置一个系统变量,用于标识surface是否创建好。
Public void surfaceCreated(SurfaceHolder holder){
msurfaceExsit=true;
}
- surfaceChanged(),当surface的界面大小改变时,调用该方法。
- surfaceDestroy();当surface对象被销毁时时,调用该方法。
在实际的游戏当中,往往会启动另一个线程用以控制surface内部的显示和用户交互,而在创建该线程时,只需要传递surface对应的surfaceHolder即可,同时,可以为子程序创建一个Handler对象,主线程可以通过该Handler对象像子程序传递用户的按键消息。
总结:
- Surface是android的一个重要元素,用于android画面的图形绘制。
- SurfaceView是视图(View)的一个继承类,每一个SurfaceView都内嵌封装一个Surface。通过调用SurfaceHolder可以调用SurfaceView,控制图形的尺寸和大小。
- SurfaceHolder 是通过getholder()来取得。
- 创立SurfaceHolder 对象后,用SurfaceHolder.Callback()来回调SurfaceHolder,对SurfaceView进行控制。
- sv=(SurfaceView)this.findViewById(R.id.surfaceview);
- sh=sv.getHolder();
- sh.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
- sf=sh.getSurface();
- sh.setKeepScreenOn(true);
- sh.addCallback(new SurfaceListener());
- 多线程
Android 的多线程是基于Linux本身的多线程机制,而多线程机制之间的同步又是通过java本身的线程同步,线程是运行在同一地址空间的不同调度单元,有自己的堆栈空间,而进程是运行在独立的,彼此之间互不察觉的不同调度单元。
一个应用程序可能包含多个线程,每个线程都有一个run()方法,run()执行完毕后,所有的线程就自动结束。
每个线程都有一个消息队列,用于不同线程之间传递消息。在run()方法内部,如果不主动读取消息队列中的消息,这些消息就是一些不用的消息,因为没被处理。
在android系统中,读取消息和处理消息是两个步骤,并有不同的部分完成,先读取消息,然后才处理消息,但是,无论是本线程还是其他线程,都不能直接处理消息队列中的消息,需要通过线程内部定义一个Handler类对象来处理消息队列,一个Thread只能有一个Handler对象,在实际应用中,读取消息队列一般需要循环执行,即不断的从消息队列中获取消息并进行相应的处理,这就需要一个Looper对象。
Looper对象用于循环读取消息队列的值,并回调Handler对象中定义的消息处理函数,同时,Looper对象还可以将读出的消息从队列中移除,执行完一次消息处理后,在循环从消息队列中读取下一个消息,知道Looper对象调用stop方法退出循环,若没有消息,Looper对象则会等待,线程不会退出。
为了方便使用Looper功能,android又定义了一个HandlerThread类,该类基于Thread,并且内部已经添加了Looper功能,只需复写其onLooperPrepared()方法,android中,一个Activity就是一个线程,多个Activity之间的切换是在同一个线程中。
-
-
- Thread
-
Thread 是一个类,和java中的基本相同,但是抛弃了一些java线程不安全的做法,比如,终止一个thread,在java中,可以使用thread。Stop等,但在android中,这些方法没有实现,不能直接使用。
新建一个thread需要两个函数:
- 构造函数
在android中,新建的线程多为activity,service等程序片段服务,而在线程的内部执行过程中,很多时候都需要使用应用程序内部的context对象,因此,构造函数中往往会传递应用程序的context对象,从而在线程的内部可以调用context相关的系统服务。
- Run()函数
该函数是thread中必须使用的对象,用于完成具体的任务。启动线程时,不能直接调用thread.run()方法,而是调用thread.start()方法启动,start()方法是thread内部使用的,该方法包括初始化线程的工作,然后回调run()方法,这些对应用程序是不可见的。
Run()方法执行完后就自动停止线程,因此,如果需要线程循环执行run方法,可以在run内部定义一个状态变量,通过检查该变量,决定是否执行,同时可在线程外设置该状态变量的值,从而终止该线程。
package com.java.lyn;
import java.io.IOException;
import android.graphics.Canvas;
import android.view.SurfaceHolder;
import static com.java.lyn.Constant.*;
public class MenuThread extends Thread{
MenuView mv;
SurfaceHolder holder;//显示一个surface的抽象接口,可以控制surface的大小和格式,
//以及在surface上编辑像素,和监视surace的改变。这个接口通//常通过SurfaceView类实现。
public MenuThread(MenuView mv)//构造器
{
this.mv = mv;
this.holder =mv.getHolder();
}
public void run()//run()方法
{
Canvas canvas;
while(MENU_FLAG)
{
canvas = null;
if(true)
{
try
{
canvas=this.holder.lockCanvas();//得到一个canvas实例
synchronized(this.holder)
{
mv.onDraw(canvas);
}
}catch(Exception e)
{
e.printStackTrace();
}
finally
{
if(canvas!=null)
{
this.holder.unlockCanvasAndPost(canvas); // 将画好的画布提交
}
}
}
try
{
sleep(2000);
}catch(Exception e)
{
e.printStackTrace();
}
}
}
}
-
-
- Runnable
-
作用和thread相同,只是这是一个接口,待看。。。。。。。。
-
- 消息发送handler
Handler用于处理线程中的消息队列,当looper从消息队列中获取消息后,会把消息派发给handler对象。一个线程一个handler对象,所以,只要拥有其他线程的handler对象的引用,就可以像其发送消息,还可以给本线程发送消息。
一般有两种用途:
- 实现一个定时任务。
- 在线程间传递数据。
对于1.需要首先了解下message的结构,他是一个数据结构类,包含很多成员变量和方法,对于简单的消息处理,一般需要了解3项,分别是:
In what:用于区分消息类型
Int arg1:额外的消息参数
Int arg2:额外的消息参数;
- handler发送消息方式
两种:一类是postXXX()方法,
一类是sendXXX方法,该方法发送一个message类型的消息到消息队列,当消息被处理时,系统会调用handler对象定义的handlermessage方法处理该消息,实现定时任务时主要用到sendXXX()方法:
1、sendEmptyMessage(int)空消息是指仅仅包含what值。
case MotionEvent.ACTION_UP:
if(x>=0&&x<=105&&y>=300&&y<=335)
{
activity.hd.sendEmptyMessage(GAME_LOAD);
}
else if(x>=0&&x<=105&&y>=340&&y<=375)
{
activity.hd.sendEmptyMessage(GAME_HELP);
}
else if(x>=0&&x<=105&&y>=380&&y<=415)
{
activity.hd.sendEmptyMessage(GAME_ABOUT);
}
else if(x>=0&&x<=105&&y>=420&&y<=455)
{
System.exit(0);
}
Exp2:
hd=new Handler()
{
@Override
public void handleMessage(Message msg)
{
super.handleMessage(msg);//
switch(msg.what)
{
case GAME_SOUND:
sv=new SoundView(PuzzlesgameActivity.this);
setContentView(sv);
break;
case GAME_MENU:
MENU_FLAG=true;//设置MenuThread标志位为true
mv=new MenuView(PuzzlesgameActivity.this);
setContentView(mv);
break;
case GAME_LOAD:
MENU_FLAG=false;//设置MenuThread标志位为false
setContentView(R.layout.icon);
new Thread()
{
public void run()
{
try
{
sleep(2000);
}catch(Exception e)
{
e.printStackTrace();
}
hd.sendEmptyMessage(GAME_PLAY);
}
}.start();
break;
};//分号
(2)同步实现进程:
Synchornized(){
If()
Tyr{
………………..
}catch()
{
}finially{}
例如:
public void run()
{
Canvas canvas;
while(MENU_FLAG)
{
canvas = null;
if(true)
{
try
{
canvas=this.holder.lockCanvas();//得到一个canvas实例
synchronized(this.holder)
{
mv.onDraw(canvas);
}
}catch(Exception e)
{
e.printStackTrace();
}
finally
{
if(canvas!=null)
{
this.holder.unlockCanvasAndPost(canvas); // 将画好的画布提交
}
}
}
try
{
sleep(2000);
}catch(Exception e)
{
e.printStackTrace();
}
}
}
2 surface绘制
绘制流程
1 APP端建立一个surface空的容器,每个窗口独占一个surface实例
2 APP通过binder将surface给到WMS,请求填充
3 wms为了填充surface 向SF申请真正的图层,relayout操作后将为窗口创建一个surface 并保存。
4 sf收到请求后向wms请求分配真正的图层。
5将图层信息handler传递给WMS,回传给app层。
6 APP控制surface利用surfacecontrol与sf通讯。
ColorFade level(就是在手机屏幕的一层surface,当level为0是为一层黑帧,level为1.0时为透明)的值为0,表示屏幕还没有绘制好,所以此时需要block screen直到window界面绘制完成
当AMS认为一个activity需要显示or隐藏,即到前台或者退出前台时,
@Override
public void setAppVisibility(IBinder token, boolean visible) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setAppVisibility()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
AppWindowToken wtoken; Z排序时 wms中同一windowtoken的窗口被安排在一起。应用组件可以是activity 输入法 壁纸等
synchronized(mWindowMap) {
wtoken = findAppWindowToken(token);//通过ActivityRecord:Token找到AppWindowToken,即找到这个token对应的Activity窗口
if (wtoken == null) {
Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token);
return;
}
if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG_WM, "setAppVisibility(" +
token + ", visible=" + visible + "): " + mAppTransition +
" hidden=" + wtoken.hidden + " hiddenRequested=" +
wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6));
//mOpeningApps是WMS的成员,里面存放所有打开的窗口的AppWindowToken,首先移除,后面根据visible添加
//mClosingApps是WMS的成员,里面存放所有关闭的窗口的AppWindowToken,首先移除,后面根据visible添加
mOpeningApps.remove(wtoken);
mClosingApps.remove(wtoken);
wtoken.waitingToShow = false;
wtoken.hiddenRequested = !visible;
if (!visible) {
// If the app is dead while it was visible, we kept its dead window on screen.
// Now that the app is going invisible, we can remove it. It will be restarted
// if made visible again.died的window的相关数据也要清除
wtoken.removeAllDeadWindows();
wtoken.setVisibleBeforeClientHidden();
} else if (visible) {
if (!mAppTransition.isTransitionSet() && mAppTransition.isReady()) {
// Add the app mOpeningApps if transition is unset but ready. This means
// we're doing a screen freeze, and the unfreeze will wait for all opening
// apps to be ready.
mOpeningApps.add(wtoken);
}
wtoken.startingMoved = false;
// If the token is currently hidden (should be the common case), or has been
// stopped, then we need to set up to wait for its windows to be ready.
if (wtoken.hidden || wtoken.mAppStopped) {
wtoken.clearAllDrawn();
//如果hidden的值等于false,说明Activity组件当前是不可见的。又由于上面visible为true,表示Activity将要被设置成可见的,
//因此,这时候就需要将AppWindowToken对象wtoken的成员变量waitingToShow的值设置为true。
// If the app was already visible, don't reset the waitingToShow state.
if (wtoken.hidden) {
wtoken.waitingToShow = true;
}
if (wtoken.clientHidden) {
// In the case where we are making an app visible
// but holding off for a transition, we still need
// to tell the client to make its windows visible so
// they get drawn. Otherwise, we will wait on
// performing the transition until all windows have
// been drawn, they never will be, and we are sad.
wtoken.clientHidden = false;
//通知应用程序进程将参数token所描述的Activity组件设置为true
//sendAppVisibilityToClient/dispatchAppVibility 这两个函数就是通知上层应用窗口可见性发生变化
wtoken.sendAppVisibilityToClients();
}
}
wtoken.requestUpdateWallpaperIfNeeded();
if (DEBUG_ADD_REMOVE) Slog.v(
TAG_WM, "No longer Stopped: " + wtoken);
wtoken.mAppStopped = false;
}
// If we are preparing an app transition, then delay changing
// the visibility of this token until we execute that transition.
//这个if分之在动画设置完成并且屏幕不冻屏,亮屏、Display OK的情况下才会走
if (okToDisplay() && mAppTransition.isTransitionSet()) {
// A dummy animation is a placeholder animation which informs others that an
// animation is going on (in this case an application transition). If the animation
// was transferred from another application/animator, no dummy animator should be
// created since an animation is already in progress.
if (wtoken.mAppAnimator.usingTransferredAnimation
&& wtoken.mAppAnimator.animation == null) {
Slog.wtf(TAG_WM, "Will NOT set dummy animation on: " + wtoken
+ ", using null transfered animation!");
}
if (!wtoken.mAppAnimator.usingTransferredAnimation &&
(!wtoken.startingDisplayed || mSkipAppTransitionAnimation)) {
if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG_WM, "Setting dummy animation on: " + wtoken);
设置哑动画,可以理解是一个站位的作用,后面会对它设置真正的动画
wtoken.mAppAnimator.setDummyAnimation();
}
wtoken.inPendingTransaction = true;
if (visible) {
mOpeningApps.add(wtoken);
wtoken.mEnteringAnimation = true;
} else {
mClosingApps.add(wtoken);
wtoken.mEnteringAnimation = false;
}
if (mAppTransition.getAppTransition() == AppTransition.TRANSIT_TASK_OPEN_BEHIND) {
// We're launchingBehind, add the launching activity to mOpeningApps.
final WindowState win =
findFocusedWindowLocked(getDefaultDisplayContentLocked());
if (win != null) {
final AppWindowToken focusedToken = win.mAppToken;
if (focusedToken != null) {
if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, " +
" adding " + focusedToken + " to mOpeningApps");
// Force animation to be loaded.
focusedToken.hidden = true;
mOpeningApps.add(focusedToken);
}
}
}
return;
}
final long origId = Binder.clearCallingIdentity();
wtoken.inPendingTransaction = false;
//将参数token所描述的Activity组件的可见性设置为参数visible所描述的值
setTokenVisibilityLocked(wtoken, null, visible, AppTransition.TRANSIT_UNSET,
true, wtoken.voiceInteraction);
//向WMS服务上报参数token所描述的Activity组件的可见性
wtoken.updateReportedVisibilityLocked();
Binder.restoreCallingIdentity(origId);
}
}
@Override
public void addWindowToken(IBinder token, int type) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"addWindowToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
synchronized(mWindowMap) {
WindowToken wtoken = mTokenMap.get(token);
if (wtoken != null) {
Slog.w(TAG_WM, "Attempted to add existing input method token: " + token);
return;
}
wtoken = new WindowToken(this, token, type, true);最后一个参数为true 为显式声明。
mTokenMap.put(token, wtoken);
if (type == TYPE_WALLPAPER) {
mWallpaperControllerLocked.addWallpaperToken(wtoken);
}
}
}
void SurfaceComposerClient::onFirstRef() {//构造出来后与ComposerService 连接绑定
sp<ISurfaceComposer> sm(ComposerService::getComposerService());
if (sm != 0) {
//通过createConnection方法,调用端得到的业务接口的实现BpSurfaceComposerClient。SF进程服务端创建Client对象,继承BnSurfaceComposerClient
sp<ISurfaceComposerClient> conn = sm->createConnection();//ISurfaceComposerClient是Wms服务进程与SF进程通信业务接口 ,返回client 类接口
if (conn != 0) {
mClient = conn;
mStatus = NO_ERROR;
}
}
}